C++ネットワークプログラミング学習:バッファオーバーフローと粘着パケットパケット
ネットワークプログラミング学習レコードで使用される言語はC/C++ です.ソースコードでサポートされているプラットフォームは、Windows/Linux です.
笔记一:基础TCP服务端/クライアント 点我跳转笔记二:ネットワークデータメッセージの送受信 点我跳转笔记三:selectネットワークモデル 点我跳转笔记四:プラットフォームをまたいでWindowsをサポート、Linuxシステム 点我跳转笔记5:ソースコードのパッケージ 点我跳转笔记6:バッファオーバーフローと粘着包分包 点我跳转笔记7:サービス端マルチスレッド分离业务処理高负荷 点我跳转笔记
ノート6ネットワークプログラミング学習記録 一、バッファオーバーフロー について 1.バッファオーバーフローの原因 2.バッファオーバーフローの処理方法 二、粘着包と分包 1.接着と分割の原因 2.接着パックと分割パックの処理方法 2.1クライアントアップグレード構想 2.2サービス側アップグレード構想
三、アップグレード後のソースコード及びその詳細な注釈 1.クライアントソースTcpClient.hpp 2.サービス側ソースTcpServer.hpp
一、バッファオーバーフローについて
1.バッファオーバーフローの原因
以前に作成したサービス側とクライアントのデータ量は小さく、操作も頻繁ではなく、命令を入力してメッセージを送信する必要があります.
以前のクライアントコードのサイクルで、パケットを絶えず送信し、パケットのサイズを1000バイトに大きくすると、すぐにサービス側とクライアントに問題が発生することがあります.データ受信に問題が発生するか、サービス側またはクライアントプログラムが直接カードを切るかのいずれかです.ここで問題が発生した原因はsocketカーネルバッファオーバーフローです.
まずsendとrecv関数は直接NICで操作されるわけではありません.send関数を使用する場合、send関数はまずデータを送信バッファに書き込み、その後、NICを通じて発行する.recv関数を使用する場合、NICはまず受信したメッセージを受信バッファに書き込み、recv関数はそこからcopyデータを書き込む.上記の2つのバッファはカーネルに存在し、プログラムでカスタマイズされたバッファではないことに注意してください.
以前のソースコードでは、recvの論理は、パケットヘッダを先に受信し、パケットヘッダに基づいてパケットボディを受信することである.一方、NICがデータを受信しすぎると、パケットヘッダを受信する時間が長くなり、NICは2つの完全なパケットを新たに受信する可能性があります.これにより、カーネル受信バッファ内のデータ量が増加し、最終的に受信バッファがオーバーフローし、正常な送信ができなくなり、プログラムがブロックされる問題が発生します.
例を挙げると、バッファはバスタブのようなもので、私たちは鉢で水をすくう人です.私たちは前に1つのバッグを受け取ったのは、1つのバッグほどの水をすくい取ることに相当し、その後、バッグほどの水をすくい取ることに相当します.2回すくって1つの新聞文ほどの水をすくっただけだ.バスタブの放水速度が大きいと、私たちは簡単に処理できません.最終的にバスタブがオーバーフロー(バッファオーバーフロー)します.
2.バッファオーバーフローの処理方法
次に、上記の例を見て、バスタブ(バッファ)のオーバーフローを阻止するにはどうすればいいのでしょうか.まず、バスタブの大きさを変えることはできません.面倒すぎて、治標が治らないので、バスタブが水を入れる時間が長くなると、いつも溢れてしまいます.それから、水をすくうスピードも私たちには変えられません.一時半は変えられないからです.では、水をすくう回数と数を変えるしかありません.
どのようにすくい水の数と回数を変えますか?一度に十分な水をすくい出し、すくい取った水から欲しい量の水を分けることで、バスタブがあふれる可能性が大幅に減少します.
从代码层面来看上面的思路,只要我们程序内新建一个足够大的缓冲区,一次从内核缓冲区上recv足够的数据,就可以避免内核缓冲区溢出了.大まかな考え方は以下の通りである:
ただし、このようにして新しい問題、すなわち粘着とパケットの問題が発生します.以下を参照してください.
二、粘着包と分包
1.粘着と分割の原因
上記ではバッファオーバーフローを処理する考え方は問題ないが,上記のソースコードの書き方に問題がある.
私たちは一度にそんなに多くのデータを受信します.ここで、データの限界は限定されません.例えば、上記では4096バイトを一度に受信したいと思っています.バッファ内に1000バイトサイズのパケットが5つある場合、私たちは今回4096バイトを受信しました.受信したデータの中に4.096個のパケットがあり、その中に新しい問題が含まれています.
ジルコニアはまず粘着包の問題である.すなわち,1回の受信に複数のパケットが含まれているため,パケットの境界がはっきりせず,くっついてしまう.上記の4.096パケットのように、受信側は不明であり、受信側は4096バイトのデータしか知らないが、1パケットがどれだけ大きいか分からない.したがって、パケットヘッダを介してパケットのサイズを取得することができ、それによって、パケットの問題を解決するために、対応するサイズのデータを処理することができる.
次にパケット化の問題です.すなわち、一度の受信に不完全なパケットが含まれている.例えば、4つの完全なパケットと、1つのパケットの最初の96バイトを含む4096バイト.これに対して、最初の4つの完全なパケットしか処理できません.問題は、上記のバッファについて、recv関数がこのバッファを毎回上書きするため、バッファ内に未処理のメッセージを格納できないことである.この問題に対して、未処理のメッセージを格納し、デュアルバッファを実現し、パケット化の問題を処理できるバッファを新規作成できます. TCPはデータストリーム向けのプロトコルなので、粘着パケットの問題が発生します.UDPはメッセージ向けのプロトコルであり、各データセグメントはメッセージであるため、粘着パケットの問題は発生しない.
2.粘着パックと分割パックの処理方法
2.1クライアントのアップグレードの考え方
まず、recvからのデータを格納するための2つのバッファを新規に作成し、処理対象のすべてのデータを格納するために使用します.まず、第1のバッファrecvをデータに、その後、第1のバッファ内のデータcopyを第2のバッファ内に、データの格納を実現することができる.その後、データなどの処理は、まずパケットヘッダを取得し、その後、パケットヘッダに基づいてパケットボリュームデータを処理する.大まかな考え方は以下の通りである:
2.2サービス側のアップグレード構想
はクライアント全体と似ていますが、サービス側には複数の接続があり、複数の接続が1つのバッファを共有するとエラーが発生するため、クライアント接続ごとに独自のバッファが必要になります.これに対して、クライアント接続クラスを新規作成して、各クライアントのsocketとそのバッファを格納できます.大まかな考え方は以下の通りである:
三、アップグレード後のソースコードとその詳細な注釈
1.クライアントソースTcpClient.hpp
2.サービス側ソースTcpServer.hpp
笔记一:基础TCP服务端/クライアント 点我跳转笔记二:ネットワークデータメッセージの送受信 点我跳转笔记三:selectネットワークモデル 点我跳转笔记四:プラットフォームをまたいでWindowsをサポート、Linuxシステム 点我跳转笔记5:ソースコードのパッケージ 点我跳转笔记6:バッファオーバーフローと粘着包分包 点我跳转笔记7:サービス端マルチスレッド分离业务処理高负荷 点我跳转笔记
ノート6
一、バッファオーバーフローについて
1.バッファオーバーフローの原因
以前に作成したサービス側とクライアントのデータ量は小さく、操作も頻繁ではなく、命令を入力してメッセージを送信する必要があります.
以前のクライアントコードのサイクルで、パケットを絶えず送信し、パケットのサイズを1000バイトに大きくすると、すぐにサービス側とクライアントに問題が発生することがあります.データ受信に問題が発生するか、サービス側またはクライアントプログラムが直接カードを切るかのいずれかです.ここで問題が発生した原因はsocketカーネルバッファオーバーフローです.
まずsendとrecv関数は直接NICで操作されるわけではありません.send関数を使用する場合、send関数はまずデータを送信バッファに書き込み、その後、NICを通じて発行する.recv関数を使用する場合、NICはまず受信したメッセージを受信バッファに書き込み、recv関数はそこからcopyデータを書き込む.上記の2つのバッファはカーネルに存在し、プログラムでカスタマイズされたバッファではないことに注意してください.
以前のソースコードでは、recvの論理は、パケットヘッダを先に受信し、パケットヘッダに基づいてパケットボディを受信することである.一方、NICがデータを受信しすぎると、パケットヘッダを受信する時間が長くなり、NICは2つの完全なパケットを新たに受信する可能性があります.これにより、カーネル受信バッファ内のデータ量が増加し、最終的に受信バッファがオーバーフローし、正常な送信ができなくなり、プログラムがブロックされる問題が発生します.
例を挙げると、バッファはバスタブのようなもので、私たちは鉢で水をすくう人です.私たちは前に1つのバッグを受け取ったのは、1つのバッグほどの水をすくい取ることに相当し、その後、バッグほどの水をすくい取ることに相当します.2回すくって1つの新聞文ほどの水をすくっただけだ.バスタブの放水速度が大きいと、私たちは簡単に処理できません.最終的にバスタブがオーバーフロー(バッファオーバーフロー)します.
2.バッファオーバーフローの処理方法
次に、上記の例を見て、バスタブ(バッファ)のオーバーフローを阻止するにはどうすればいいのでしょうか.まず、バスタブの大きさを変えることはできません.面倒すぎて、治標が治らないので、バスタブが水を入れる時間が長くなると、いつも溢れてしまいます.それから、水をすくうスピードも私たちには変えられません.一時半は変えられないからです.では、水をすくう回数と数を変えるしかありません.
どのようにすくい水の数と回数を変えますか?一度に十分な水をすくい出し、すくい取った水から欲しい量の水を分けることで、バスタブがあふれる可能性が大幅に減少します.
从代码层面来看上面的思路,只要我们程序内新建一个足够大的缓冲区,一次从内核缓冲区上recv足够的数据,就可以避免内核缓冲区溢出了.
char _Recv_buf[4096];
int DataRecv
{
//
int recv_len = recv(socket, _Recv_buf, 4096, 0);
if(recv_len <= 0)
{
return -1;
}
while(_Recv_buf )
{
_Recv_buf
}
return 0;
}
ただし、このようにして新しい問題、すなわち粘着とパケットの問題が発生します.以下を参照してください.
二、粘着包と分包
1.粘着と分割の原因
上記ではバッファオーバーフローを処理する考え方は問題ないが,上記のソースコードの書き方に問題がある.
私たちは一度にそんなに多くのデータを受信します.ここで、データの限界は限定されません.例えば、上記では4096バイトを一度に受信したいと思っています.バッファ内に1000バイトサイズのパケットが5つある場合、私たちは今回4096バイトを受信しました.受信したデータの中に4.096個のパケットがあり、その中に新しい問題が含まれています.
ジルコニアはまず粘着包の問題である.すなわち,1回の受信に複数のパケットが含まれているため,パケットの境界がはっきりせず,くっついてしまう.上記の4.096パケットのように、受信側は不明であり、受信側は4096バイトのデータしか知らないが、1パケットがどれだけ大きいか分からない.したがって、パケットヘッダを介してパケットのサイズを取得することができ、それによって、パケットの問題を解決するために、対応するサイズのデータを処理することができる.
次にパケット化の問題です.すなわち、一度の受信に不完全なパケットが含まれている.例えば、4つの完全なパケットと、1つのパケットの最初の96バイトを含む4096バイト.これに対して、最初の4つの完全なパケットしか処理できません.問題は、上記のバッファについて、recv関数がこのバッファを毎回上書きするため、バッファ内に未処理のメッセージを格納できないことである.この問題に対して、未処理のメッセージを格納し、デュアルバッファを実現し、パケット化の問題を処理できるバッファを新規作成できます.
2.粘着パックと分割パックの処理方法
2.1クライアントのアップグレードの考え方
まず、recvからのデータを格納するための2つのバッファを新規に作成し、処理対象のすべてのデータを格納するために使用します.まず、第1のバッファrecvをデータに、その後、第1のバッファ内のデータcopyを第2のバッファ内に、データの格納を実現することができる.その後、データなどの処理は、まずパケットヘッダを取得し、その後、パケットヘッダに基づいてパケットボリュームデータを処理する.
//
char [4096]
char [40960];
int RecvData(SOCKET temp_socket)//
{
//
int recv_len = recv(temp_socket, , 4096, 0);
if(recv_len <= 0)
{
printf(" ,
");
return -1;
}
1.
while(2. )
{
3. //
if(4. ) //
{
5.
6.
}
}
return 0;
}
2.2サービス側のアップグレード構想
はクライアント全体と似ていますが、サービス側には複数の接続があり、複数の接続が1つのバッファを共有するとエラーが発生するため、クライアント接続ごとに独自のバッファが必要になります.これに対して、クライアント接続クラスを新規作成して、各クライアントのsocketとそのバッファを格納できます.
class
{
public:
1. socket()
2. ()
3. ()
4. ()
private:
1.socket
2.
};
std::vector< *> _clients;// socket
char [4096];
0. OnRun
// socket
for(int n=0; n<_clients.size(); ++n)
{
if(FD_ISSET(_clients[n]-> socket(),&fdRead))
{
if(-1 == RecvData(_clients[n]))//
{
std::vector< *>::iterator iter = _clients.begin()+n;//
if(iter != _clients.end())//
{
delete _clients[n];
_clients.erase(iter);//
}
}
}
}
int RecvData( * client)//
{
//
int recv_len = recv(client-> socket(), , 4096, 0);
if(recv_len <= 0)
{
printf(" ,
");
return -1;
}
1. client-> ();
while(2. ) client-> ();
{
3. //
if(4. ) //
{
5.
6. client-> ();
}
}
return 0;
}
三、アップグレード後のソースコードとその詳細な注釈
1.クライアントソースTcpClient.hpp
#ifndef _TcpClient_hpp_
#define _TcpClient_hpp_
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include
#include
#pragma comment(lib,"ws2_32.lib")
#else
#include
#include
#include
#define SOCKET int
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR (-1)
#endif
//
enum cmd
{
CMD_LOGIN,//
CMD_LOGINRESULT,//
CMD_LOGOUT,//
CMD_LOGOUTRESULT,//
CMD_NEW_USER_JOIN,//
CMD_ERROR//
};
//
struct DataHeader
{
short cmd;//
short date_length;//
};
// 1
struct Login : public DataHeader
{
Login()//
{
this->cmd = CMD_LOGIN;
this->date_length = sizeof(Login);
}
char UserName[32];//
char PassWord[32];//
char data[932];
};
// 2
struct LoginResult : public DataHeader
{
LoginResult()//
{
this->cmd = CMD_LOGINRESULT;
this->date_length = sizeof(LoginResult);
}
int Result;
char Data[992];//
};
// 3
struct Logout : public DataHeader
{
Logout()//
{
this->cmd = CMD_LOGOUT;
this->date_length = sizeof(Logout);
}
char UserName[32];//
char data[964];
};
// 4
struct LogoutResult : public DataHeader
{
LogoutResult()//
{
this->cmd = CMD_LOGOUTRESULT;
this->date_length = sizeof(LogoutResult);
}
int Result;
char data[992];
};
// 5
struct NewUserJoin : public DataHeader
{
NewUserJoin()//
{
this->cmd = CMD_NEW_USER_JOIN;
this->date_length = sizeof(NewUserJoin);
}
char UserName[32];//
};
#include
#define RECV_BUFFER_SIZE 4096
class TcpClient
{
public:
//
TcpClient()
{
_sock = INVALID_SOCKET;
//
_Recv_buf = new char[RECV_BUFFER_SIZE];
_Msg_buf = new char[RECV_BUFFER_SIZE*10];
_Len_buf = 0;
}
//
virtual ~TcpClient()
{
delete[] _Recv_buf;
delete[] _Msg_buf;
// socket
CloseSocket();
}
// socket 1
int InitSocket()
{
#ifdef _WIN32
// windows socket 2,x
WORD ver = MAKEWORD(2,2);
WSADATA dat;
if(0 != WSAStartup(ver,&dat))
{
return -1;//-1
}
#endif
// socket
if(INVALID_SOCKET != _sock)
{
printf("
" ,_sock);
CloseSocket();//
}
_sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(INVALID_SOCKET == _sock)
{
return 0;//0 socket
}
return 1;
}
// 1
int Connect(const char *ip,unsigned short port)
{
//
if(INVALID_SOCKET == _sock)
{
InitSocket();
}
//
sockaddr_in _sin = {
};
_sin.sin_family = AF_INET;//IPV4
_sin.sin_port = htons(port);//
#ifdef _WIN32
_sin.sin_addr.S_un.S_addr = inet_addr(ip);//IP
#else
_sin.sin_addr.s_addr = inet_addr(ip);//IP
#endif
if(SOCKET_ERROR == connect(_sock,(sockaddr*)&_sin,sizeof(sockaddr_in)))
{
return 0;//
}
else
{
return 1;//
}
}
// socket
void CloseSocket()
{
if(INVALID_SOCKET != _sock)
{
#ifdef _WIN32
// socket
closesocket(_sock);
// windows socket
WSACleanup();
#else
// socket/LINUX
close(_sock);
#endif
_sock = INVALID_SOCKET;
}
}
//
bool OnRun()
{
if(IsRun())//
{
fd_set _fdRead;//
FD_ZERO(&_fdRead);//
FD_SET(_sock,&_fdRead);//
timeval _t = {
1,0};//select
// seclect
int _ret = select(_sock+1,&_fdRead,NULL,NULL,&_t);
if(_ret<0)
{
printf("seclect
");
return false;
}
if(FD_ISSET(_sock,&_fdRead))// socket
{
FD_CLR(_sock,&_fdRead);//
if(-1 == RecvData(_sock))
{
CloseSocket();
return false;
}
}
return true;
}
return false;
}
//
bool IsRun()
{
return _sock != INVALID_SOCKET;
}
//
int SendData(DataHeader *_head)
{
if(IsRun() && _head)
{
send(_sock,(const char*)_head,_head->date_length,0);
return 1;
}
return 0;
}
//
int RecvData(SOCKET temp_socket)//
{
//
int recv_len = recv(temp_socket, _Recv_buf, RECV_BUFFER_SIZE, 0);
if(recv_len <= 0)
{
printf(" ,
");
return -1;
}
//
memcpy(_Msg_buf+_Len_buf, _Recv_buf, recv_len);
//
_Len_buf += recv_len;
//
while(_Len_buf >= sizeof(DataHeader))//
{
//
DataHeader* header = (DataHeader*)_Msg_buf;
//
if(_Len_buf >= header->date_length)
{
//
int size = _Len_buf - header->date_length;
//
NetMsg(header);
//
memcpy(_Msg_buf, _Msg_buf + header->date_length, size);
//
_Len_buf = size;
}
else
{
//
break;
}
}
return 0;
}
//
virtual void NetMsg(DataHeader *_head)
{
printf(" , :%d, :%d
",_head->cmd,_head->date_length);
switch(_head->cmd)
{
case CMD_LOGINRESULT://
{
LoginResult *_result = (LoginResult*)_head;
printf(" :%d
",_result->Result);
}
break;
case CMD_LOGOUTRESULT://
{
LogoutResult *_result = (LogoutResult*)_head;
printf(" :%d
",_result->Result);
}
break;
case CMD_NEW_USER_JOIN://
{
NewUserJoin *_result = (NewUserJoin*)_head;
printf(" :%s
",_result->UserName);
}
break;
case CMD_ERROR://
{
printf("
");
getchar();
}
break;
default:
{
printf("
");
getchar();
}
break;
}
}
private:
SOCKET _sock;
//
char *_Recv_buf;//
char *_Msg_buf;//
int _Len_buf;//
};
#endif
2.サービス側ソースTcpServer.hpp
#ifndef _TcpServer_hpp_
#define _TcpServer_hpp_
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include
#include
#pragma comment(lib,"ws2_32.lib")// windows
#else
#include //selcet
#include //uni std
#include
#define SOCKET int
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR (-1)
#endif
//
enum cmd
{
CMD_LOGIN,//
CMD_LOGINRESULT,//
CMD_LOGOUT,//
CMD_LOGOUTRESULT,//
CMD_NEW_USER_JOIN,//
CMD_ERROR//
};
//
struct DataHeader
{
short cmd;//
short date_length;//
};
// 1
struct Login : public DataHeader
{
Login()//
{
this->cmd = CMD_LOGIN;
this->date_length = sizeof(Login);
}
char UserName[32];//
char PassWord[32];//
char data[932];
};
// 2
struct LoginResult : public DataHeader
{
LoginResult()//
{
this->cmd = CMD_LOGINRESULT;
this->date_length = sizeof(LoginResult);
}
int Result;
char Data[992];//
};
// 3
struct Logout : public DataHeader
{
Logout()//
{
this->cmd = CMD_LOGOUT;
this->date_length = sizeof(Logout);
}
char UserName[32];//
char data[964];
};
// 4
struct LogoutResult : public DataHeader
{
LogoutResult()//
{
this->cmd = CMD_LOGOUTRESULT;
this->date_length = sizeof(LogoutResult);
}
int Result;
char data[992];
};
// 5
struct NewUserJoin : public DataHeader
{
NewUserJoin()//
{
this->cmd = CMD_NEW_USER_JOIN;
this->date_length = sizeof(NewUserJoin);
}
char UserName[32];//
};
#include
#define RECV_BUFFER_SIZE 4096
class ClientSocket
{
public:
//
ClientSocket(SOCKET sockfd = INVALID_SOCKET)
{
_sockfd = sockfd;
//
_Msg_buf = new char[RECV_BUFFER_SIZE*10];
_Len_buf = 0;
}
//
virtual ~ClientSocket()
{
delete[] _Msg_buf;
}
// socket
SOCKET GetSockfd()
{
return _sockfd;
}
//
char* MsgBuf()
{
return _Msg_buf;
}
//
int GetLen()
{
return _Len_buf;
}
//
void SetLen(int len)
{
_Len_buf = len;
}
private:
SOCKET _sockfd;
//
char *_Msg_buf;//
int _Len_buf;//
};
class TcpServer
{
public:
//
TcpServer()
{
_sock = INVALID_SOCKET;
//
_Recv_buf = new char[RECV_BUFFER_SIZE];
}
//
virtual ~TcpServer()
{
delete[] _Recv_buf;
// socket
CloseSocket();
}
// socket 1
int InitSocket()
{
#ifdef _WIN32
// windows socket 2,x
WORD ver = MAKEWORD(2,2);
WSADATA dat;
if(0 != WSAStartup(ver,&dat))
{
return -1;//-1
}
#endif
// socket
if(INVALID_SOCKET != _sock)
{
printf("
" ,_sock);
CloseSocket();//
}
_sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(INVALID_SOCKET == _sock)
{
return 0;//0 socket
}
return 1;
}
// IP/
int Bind(const char* ip,unsigned short port)
{
//
if(INVALID_SOCKET == _sock)
{
InitSocket();
}
// IP
sockaddr_in _myaddr = {
};
_myaddr.sin_family = AF_INET;//IPV4
_myaddr.sin_port = htons(port);//
#ifdef _WIN32
if(ip)//ip
{
_myaddr.sin_addr.S_un.S_addr = inet_addr(ip);//IP
}
else
{
_myaddr.sin_addr.S_un.S_addr = INADDR_ANY;//IP
}
#else
if(ip)//ip
{
_myaddr.sin_addr.s_addr = inet_addr(ip);//IP
}
else
{
_myaddr.sin_addr.s_addr = INADDR_ANY;//IP
}
#endif
if(SOCKET_ERROR == bind(_sock,(sockaddr*)&_myaddr,sizeof(sockaddr_in)))//socket ( )sockaddr
{
printf("
");
return 0;
}
else
{
printf("
%d
",port);
return 1;
}
}
//
int Listen(int n)
{
//
if(INVALID_SOCKET == _sock)
{
printf(" IP
");
return 0;
}
//
if(SOCKET_ERROR == listen(_sock,n))//
{
printf("
");
return 0;
}
else
{
printf("
");
return 1;
}
}
//
int Accept()
{
//
sockaddr_in clientAddr = {
};// sockadd
int addr_len = sizeof(sockaddr_in);// sockadd
SOCKET temp_socket = INVALID_SOCKET;//
#ifdef _WIN32
temp_socket = accept(_sock,(sockaddr*)&clientAddr,&addr_len);//
#else
temp_socket = accept(_sock,(sockaddr*)&clientAddr,(socklen_t*)&addr_len);//
#endif
if(INVALID_SOCKET == temp_socket)//
{
printf(" , SOCKET
" ,temp_socket);
return 0;
}
else
{
printf("
IP :%s
", inet_ntoa(clientAddr.sin_addr));
//
NewUserJoin *user_join = new NewUserJoin();
strcpy(user_join->UserName,inet_ntoa(clientAddr.sin_addr));
SendDataToAll(user_join);
//
_clients.push_back(new ClientSocket(temp_socket));
return 1;
}
}
// socket
void CloseSocket()
{
if(INVALID_SOCKET != _sock)
{
#ifdef _WIN32
// socket
for(int n=0; n<_clients.size(); ++n)
{
closesocket(_clients[n]->GetSockfd());
delete _clients[n];
}
// socket
closesocket(_sock);
// windows socket
WSACleanup();
#else
// socket
for(int n=0; n<_clients.size(); ++n)
{
close(_clients[n]->GetSockfd());
delete _clients[n];
}
// socket/LINUX
close(_sock);
#endif
_sock = INVALID_SOCKET;
_clients.clear();
}
}
//
bool OnRun()
{
if(IsRun())
{
fd_set fdRead;//
fd_set fdWrite;
fd_set fdExcept;
FD_ZERO(&fdRead);//
FD_ZERO(&fdWrite);
FD_ZERO(&fdExcept);
FD_SET(_sock,&fdRead);//
FD_SET(_sock,&fdWrite);
FD_SET(_sock,&fdExcept);
timeval s_t = {
2,0};//select
SOCKET maxSock = _sock;// socket
// read
for(int n=_clients.size()-1; n>=0; --n)
{
FD_SET(_clients[n]->GetSockfd(),&fdRead);
if(maxSock < _clients[n]->GetSockfd())
{
maxSock = _clients[n]->GetSockfd();
}
}
//select select
int ret = select(maxSock+1,&fdRead,&fdWrite,&fdExcept,&s_t);
if(ret<0)
{
printf("select
");
CloseSocket();
return false;
}
if(FD_ISSET(_sock,&fdRead))// socket
{
FD_CLR(_sock,&fdRead);//
Accept();//
}
// socket
for(int n=0; n<_clients.size(); ++n)
{
if(FD_ISSET(_clients[n]->GetSockfd(),&fdRead))
{
if(-1 == RecvData(_clients[n]))//
{
std::vector<ClientSocket*>::iterator iter = _clients.begin()+n;//
if(iter != _clients.end())//
{
delete _clients[n];
_clients.erase(iter);//
}
}
}
}
//printf("
");
return true;
}
return false;
}
//
bool IsRun()
{
return _sock != INVALID_SOCKET;
}
//
int SendData(DataHeader *head,SOCKET temp_socket)
{
if(IsRun() && head)
{
send(temp_socket,(const char*)head,head->date_length,0);
return 1;
}
return 0;
}
//
void SendDataToAll(DataHeader *head)
{
for(int n=0;n<_clients.size();++n)
{
SendData(head, _clients[n]->GetSockfd());
}
}
//
int RecvData(ClientSocket *t_client)//
{
//
int buf_len = recv(t_client->GetSockfd(), _Recv_buf, RECV_BUFFER_SIZE, 0);
if(buf_len<=0)
{
printf("
");
return -1;
}
//
memcpy(t_client->MsgBuf() + t_client->GetLen(), _Recv_buf, buf_len);
//
t_client->SetLen(t_client->GetLen() + buf_len);
//
while(t_client->GetLen() >= sizeof(DataHeader))//
{
//
DataHeader* header = (DataHeader*)t_client->MsgBuf();
//
if(t_client->GetLen() >= header->date_length)
{
//
int size = t_client->GetLen() - header->date_length;
//
NetMsg(header,t_client->GetSockfd());
//
memcpy(t_client->MsgBuf(), t_client->MsgBuf() + header->date_length, size);
//
t_client->SetLen(size);
}
else
{
//
break;
}
}
return 0;
}
//
void NetMsg(DataHeader *head,SOCKET temp_socket)
{
printf(" , :%d, :%d
",head->cmd,head->date_length);
switch(head->cmd)
{
case CMD_LOGIN://
{
Login *login = (Login*)head;
/*
*/
//printf("%s
:%s
",login->UserName,login->PassWord);
LoginResult *result = new LoginResult;
result->Result = 1;
SendData(result,temp_socket);
}
break;
case CMD_LOGOUT://
{
Logout *logout = (Logout*)head;
/*
*/
//printf("%s
",logout->UserName);
LogoutResult *result = new LogoutResult();
result->Result = 1;
SendData(result,temp_socket);
}
break;
default://
{
head->cmd = CMD_ERROR;
head->date_length = 0;
SendData(head,temp_socket);
}
break;
}
}
private:
SOCKET _sock;
std::vector<ClientSocket*> _clients;//
//
char *_Recv_buf;//
};
#endif