Last active
August 9, 2018 17:30
-
-
Save AtsushiSakai/9424923 to your computer and use it in GitHub Desktop.
LinuxにおけるTCPソケット通信を利用したプロセス間通信用
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
@file: TcpLib.hpp | |
@brief:Linux用TCP通信サーバ用ライブラリ | |
ユーザが使用するクラス | |
1.TcpServer サーバ用 | |
2.TcpClient クライアント用 | |
@note TcpMasterクラスは、サーバとクライアントの | |
共通変数・関数用クラスなので | |
単独では使用しないでください。 | |
使用方法は、各クラスのヘッダコメントに書いてあります。 | |
@author: Atsushi Sakai | |
*/ | |
#ifndef TCP_LIB_HPP | |
#define TCP_LIB_HPP | |
#include <sys/socket.h> //ソケット通信用 | |
#include <errno.h> //エラー解析用 | |
#include <netinet/in.h> //sockaddr_in用 | |
#include <arpa/inet.h> //inet_addr用 | |
/** | |
* @brief TCP通信用マスタクラス | |
* サーバとクライアントの共通の関数や変数用 | |
* ReadとWrite関数はこちらのクラスに書いてあるので注意 | |
*/ | |
class TcpMaster{ | |
public: | |
TcpMaster(){ | |
InitMember();//メンバ変数の初期化 | |
} | |
virtual ~TcpMaster(){ | |
close(selfSock_);//ソケットを閉じる | |
} | |
//=====メンバ変数===== | |
bool isInit_; //初期化されたかどうか? | |
int selfSock_; //自身のソケット | |
int clientSock_;//クライアントのソケット | |
//(クライアントクラスの場合 selfsock_=clientsock_) | |
int port_; //ポート番号 | |
//=====メンバ関数===== | |
/** | |
* @brief データ受信関数 | |
* @param data 受信データ | |
* @return 受信データのバイト数 | |
*/ | |
int Read(vector<char> &data){ | |
if(!isInit_){//初期化チェック | |
cout<<"[Read] This object doesn't init"<<endl; | |
return 0; | |
} | |
//データの受信 | |
const int MAX_READ_BUF=22800; | |
char RecBuf[MAX_READ_BUF];//受信バッファ | |
//データの受信 | |
int nRead=read(selfSock_, RecBuf,sizeof(RecBuf)); | |
//受信データのダイアグノシス | |
if(nRead<=0){ | |
cout<<"[Read] cannot receive packet"<<endl; | |
perror("Read"); | |
data.clear();//受信変数を空にする | |
return 0; | |
} | |
else if(nRead>=MAX_READ_BUF){ | |
cout<<"[Read] Buffer Overflow:"<<nRead<<endl; | |
data.clear();//受信変数を空にする | |
return 0; | |
} | |
//vectorに格納 | |
data.resize(nRead);//事前確保 reserveではない | |
for(int ir=0;ir<nRead;ir++){ | |
data[ir]=RecBuf[ir]; | |
} | |
return nRead;//受信数を返す | |
} | |
/** | |
* @brief データ送信関数 | |
* @param data 送信データ | |
* @return 送信データのバイト数 | |
*/ | |
int Write(const vector<char> &data) const { | |
if(!isInit_){//初期化チェック | |
cout<<"[Write] This object doesn't init"<<endl; | |
return 0;//送信しない | |
} | |
//送信データのサイズ | |
int ndata=data.size(); | |
char sendBuf[ndata];//送信バッファ | |
//送信バッファにコピー ポインタを使用 | |
memcpy(sendBuf,&data[0],ndata); | |
//送信 | |
int nWrite=write(clientSock_,sendBuf, sizeof(sendBuf)); | |
if(nWrite<=0){//Write失敗 | |
cout<<"[Write] Write Failed:"<<nWrite<<endl; | |
perror("Write"); | |
} | |
return nWrite;//送信数を返す | |
} | |
/** | |
* @brief ソケットを作成する関数 | |
*/ | |
bool CreateSocket(void){ | |
//ソケットの設定はサーバもクライアントも一緒 | |
//引数1:アドレスファミリ AF_INET:IPv4 | |
//引数2:ソケットタイプ SOCK_STREAM:TCP | |
//引数3:プロトコル | |
selfSock_=socket(AF_INET, SOCK_STREAM, 0); | |
if(selfSock_<0){//失敗 | |
cout<<"[CreateSocket] socket failed"<<endl; | |
perror("CreateSocket"); | |
return false; | |
} | |
else{//成功 | |
cout<<"[CreateSocket] Create Socket"<<endl; | |
return true; | |
} | |
} | |
/** | |
* @brief サーバのデータ構造体を取得する関数 | |
* (クライアント,サーバ共通) | |
*/ | |
sockaddr_in GetServerInfo(void){ | |
sockaddr_in server; | |
server.sin_family =AF_INET;//TCP | |
//ポート番号の指定 htonsはエンディアンの変換 | |
server.sin_port =htons(port_); | |
return server; | |
} | |
/** | |
* @brief パケットの中身を16進数で表示する関数 | |
*/ | |
void ShowPacketContents(const vector<char> &pkt) const { | |
int nPkt=pkt.size(); | |
for(int ip=0;ip<nPkt;ip++){ | |
if(ip%4==0&&ip!=0){printf(",");}//4byte境界で区切る | |
printf("%02x",pkt[ip] & 0xff); | |
} | |
printf("\n"); | |
} | |
private: | |
/** | |
* @brief メンバ変数の初期化関数 | |
*/ | |
void InitMember(void){ | |
isInit_=false;//初期化されていない | |
} | |
}; | |
/** | |
* @brief TCP通信クライアント用クラス | |
* @note このクライアントを使用する時は、 | |
* オブジェクトを生成したあとInit関数を必ず呼んで下さい | |
* クライアントクラスと重複コードを減らすために | |
* TcpMasterクラスを継承しています。 | |
* 使用例: | |
* TcpClient client;//TCPクライアント用オブジェクトの作成 | |
* string LOCALHOST="127.0.0.1";//ローカルホストのIPアドレス | |
* int PORT=50001;//ポート番号 | |
* client.Init(LOCALHOST,PORT); | |
* | |
* vector<char> data; | |
* int nSend=tcp.Read(data);//データ受信 | |
* int nWrite=tcp.Write(data);//データ送信 | |
*/ | |
class TcpClient:public TcpMaster{ | |
public: | |
TcpClient():TcpMaster(){} | |
virtual ~TcpClient(){} | |
/** | |
* @brief 初期化関数 | |
* @param ip 接続先のサーバのIPアドレス | |
* @param ip 接続先のサーバのポート番号 | |
* @return Initが成功したか? | |
*/ | |
bool Init(string ip, int port){return InitClient(ip,port);} | |
private: | |
//=====メンバ変数==== | |
string ip_; //IPアドレス | |
//=====メンバ関数==== | |
/** | |
* @brief クライアントの初期化関数 | |
* @param ip 接続先のサーバのIPアドレス | |
* @param ip 接続先のサーバのポート番号 | |
* @return Initが成功したか? | |
*/ | |
bool InitClient(string ip, int port){ | |
//メンバ変数へのコピー | |
ip_ =ip; | |
port_ =port; | |
//ソケットの作成 | |
bool isCreated=CreateSocket(); | |
if(isCreated){//ソケットの作成に成功 | |
//クライアントクラスの場合 | |
//selfSockとclientsock_は同じになる | |
clientSock_=selfSock_; | |
} | |
//接続先指定用構造体の作成 | |
sockaddr_in server=GetServerInfo(); | |
//IPアドレスの指定 | |
//inet_addrは文字列のポインタから、バイナリ値に変換している。 | |
server.sin_addr.s_addr =inet_addr(ip_.c_str()); | |
//サーバに接続 | |
isInit_=Connect(server); | |
return isInit_; | |
} | |
/** | |
* @brief サーバに接続する関数 | |
* @return サーバに接続できたかどうか | |
*/ | |
bool Connect(const sockaddr_in &server){ | |
int conResult=connect(selfSock_,(struct sockaddr *)&server,sizeof(server)); | |
if(conResult<0){//失敗 | |
cout<<"Failed to open TCP Connection to "<<ip_<<endl; | |
perror("Connect"); | |
return false; | |
} | |
else{//成功 | |
cout<<"Sucsess to open TCP Connection to "<<ip_<<endl; | |
return true; | |
} | |
} | |
}; | |
/** | |
* @brief TCP通信サーバ用クラス | |
* @note このクライアントを使用する時は、 | |
* オブジェクトを生成したあとInit関数を必ず呼んで下さい | |
* クライアントクラスと重複コードを減らすために | |
* TcpMasterクラスを継承しています。 | |
* 使用方法: | |
* TcpServer server;//TCPサーバ用オブジェクト | |
* int PORT=50001;//サーバのポート番号 | |
* server.Init(PORT);//初期化 | |
* | |
* vector<char> data; | |
* int nSend=server.Send(data); //受信 | |
* int nWrite=server.Write(data);//送信 | |
*/ | |
class TcpServer:public TcpMaster{ | |
public: | |
TcpServer():TcpMaster(){} | |
virtual ~TcpServer(){ | |
close(clientSock_);//ソケットを閉じる サーバだけ | |
} | |
/** | |
* @brief 初期化関数 | |
* @param サーバのポート番号 | |
* @return 初期化が成功したか? | |
*/ | |
bool Init(int port){return InitServer(port);} | |
private: | |
/** | |
* @brief サーバ用初期化関数 | |
* @param サーバのポート番号 | |
* @return 初期化が成功したか? | |
*/ | |
bool InitServer(int port){ | |
//メンバ変数へのコピー | |
port_ =port; | |
//ソケットの作成 | |
CreateSocket(); | |
//サーバの設定用構造体の作成 | |
sockaddr_in server=GetServerInfo(); | |
//IPアドレスの指定(サーバ用設定) | |
server.sin_addr.s_addr =INADDR_ANY;//すべてのIPを接続可能に | |
//サーバを切った後にTIME WAIT状態にしなくするおまじない | |
int yes=1; | |
setsockopt(selfSock_,SOL_SOCKET,SO_REUSEADDR,(const char *)&yes,sizeof(yes)); | |
//サーバの設定(bind) | |
int statBind=bind(selfSock_, (struct sockaddr *)&server,sizeof(server)); | |
if(statBind!=0){//Bind失敗 | |
cou<<"[Init] Bind Failed:"<<statBind<<endl; | |
perror("Bind"); | |
return false; | |
} | |
//サーバを接続可能状態に | |
//2つ目の引数は接続待ちのクライアントの最大数 通常5-10らしい | |
int statListen=listen(selfSock_, 5); | |
if(statListen!=0){//Listen失敗 | |
cout<<"[Init] Listen Failed:"<<statListen<<endl; | |
perror("Listen"); | |
return false; | |
} | |
//サーバを接続待ちにする | |
isInit_=WaitClientConnection(); | |
return isInit_;//ステータスを返す | |
} | |
/** | |
* @brief クライアントのコネクションを待つ関数 | |
*/ | |
bool WaitClientConnection(void){ | |
cout<<"Waiting Client...."<<endl; | |
sockaddr_in client; | |
unsigned int len=sizeof(client); | |
clientSock_=accept(selfSock_, (struct sockaddr *)&client,&len); | |
if(clientSock_<0){//Accept失敗 | |
cout<<"[Init] Accept Failed:"<<clientSock_<<endl; | |
perror("Accept"); | |
return false; | |
} | |
//成功 Connect先のクライアントの情報を表示する | |
printf("[Init] Accepted connection from %s, port=%d\n", | |
inet_ntoa(client.sin_addr), ntohs(client.sin_port)); | |
return true; | |
} | |
}; | |
#endif //TCP_LIB_HPP |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
//データの受信
int nRead=read(selfSock_, RecBuf,sizeof(RecBuf));
64行目のreadの引数はselfSock_ではなくてclientSock_ではないでしょうか?
あと、細かいことですけど69行目のperrorの前が全角の空白になっていました...