程序实例(1) ---- chating room
客户端向服务器发送数据,并显示服务器反馈的内容。
服务器采用IOCP模型,把每个客户端发送的数据向所有连接的客户端发送一遍。
运行一个服务器,可以连接多个运行的客户端。
服务器:
客户端:
服务器采用IOCP模型,把每个客户端发送的数据向所有连接的客户端发送一遍。
运行一个服务器,可以连接多个运行的客户端。
服务器:
#include
<
iostream
>
#include < winsock2.h >
#pragma comment (lib, " Ws2_32.lib " )
using namespace std;
char SERVER_IP[ 32 ] = " 127.0.0.1 " ;
const DWORD SERVER_PORT = 1555 ;
const DWORD BUFFER_SIZE = 1024 ;
// OverLapped结构体扩展
struct OVERLAPPEDPLUS
{
OVERLAPPED ol;
char buf[BUFFER_SIZE]; // 在SOCKET上收数据的buffer
char clientIP[ 32 ]; // 客户端IP
u_short clientPort; // 客户端port
DWORD index; // SOCKET在数组中的index,从0开始
};
SOCKET sock_array[ 64 ] = {NULL}; // 保存客户端SOCKET
DWORD sock_num = 0 ; // 记录SOCKET数
// 线程函数声明
DWORD CALLBACK ServerWorkerThread( void * CompletionPortID) ;
// Start function
void StartServer()
{
WSADATA wd;
::WSAStartup(MAKEWORD( 2 , 2 ), & wd);
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(SERVER_PORT);
addr.sin_addr.s_addr = inet_addr(SERVER_IP);
SOCKET s = ::socket(AF_INET, SOCK_STREAM, 0 );
if ( 0 != ::bind(s, ( const sockaddr * ) & addr, sizeof (addr)))
{
cout << " bind error: " << WSAGetLastError() << endl;
::closesocket(s);
::WSACleanup();
return ;
}
::listen(s, 2 );
// 创建完成端口
HANDLE CompetionPort = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL, 0 , 0 );
// 获得CPU数,创建CPU数*2+2个线程
SYSTEM_INFO SystenInfo;
::GetSystemInfo( & SystenInfo);
for (DWORD i = 0 ; i < SystenInfo.dwNumberOfProcessors * 2 + 2 ; ++ i)
{
DWORD ThreadID = 0 ;
HANDLE ThreadHandle = ::CreateThread(NULL, 0 ,ServerWorkerThread,CompetionPort, 0 , & ThreadID);
::CloseHandle(ThreadHandle);
}
cout << " Server Started! " << endl << endl;
while (TRUE)
{
sockaddr_in client_addr;
int fromlen = sizeof (sockaddr_in);
SOCKET ns = ::accept(s, (sockaddr * ) & client_addr, NULL);
// SOCKET入数组
sock_array[sock_num] = ns;
++ sock_num;
// 新客户端连接进来
::CreateIoCompletionPort((HANDLE)ns,CompetionPort,(DWORD)ns, 0 );
// 在堆中创建OverLapped扩展结构体的对象
OVERLAPPEDPLUS * olp = (OVERLAPPEDPLUS * )::GlobalAlloc(GPTR, sizeof (OVERLAPPEDPLUS));
// 根据SOCKET取客户端的IP和PORT,保存在OVERLAPPEDPLUS里
ZeroMemory( & client_addr, sizeof (sockaddr_in));
::getpeername(ns, (sockaddr * ) & client_addr, & fromlen);
strcpy_s(olp -> clientIP, 32 , inet_ntoa(client_addr.sin_addr));
olp -> clientPort = ntohs(client_addr.sin_port);
cout << endl << olp -> clientIP << " : " << olp -> clientPort << " Connected! " << endl << endl;
// 保存SOCKET的index
olp -> index = sock_num - 1 ;
WSABUF wBuffer;
wBuffer.buf = olp -> buf;
wBuffer.len = BUFFER_SIZE;
DWORD dwFlag = 0 ;
DWORD dwRecvd = 0 ;
// 在指定SOCKET上投递一个异步的IO请求
::WSARecvFrom(ns, & wBuffer, 1 , & dwRecvd, & dwFlag, (sockaddr * ) & client_addr, & fromlen, & (olp -> ol), NULL);
}
::closesocket(s);
::WSACleanup();
}
// 工作者线程
DWORD CALLBACK ServerWorkerThread( void * CompletionPortID)
{
HANDLE CompletionPort = (HANDLE) CompletionPortID;
DWORD BytesTransferred = 0 ;
SOCKET mySock;
OVERLAPPEDPLUS * polp;
DWORD dwSend = 0 , dwRecv = 0 , dwFlag = 0 ;
while (TRUE)
{
// 从指定的CP队列中取出一个完成包,一个完成包表示一次IO操作的完成;若队列为空则阻塞
::GetQueuedCompletionStatus(CompletionPort, & BytesTransferred, (LPDWORD) & mySock, (OVERLAPPED ** ) & polp, INFINITE);
if (BytesTransferred == 0 )
{
// 如果客户端关闭连接,则服务器端关闭SOCKET,释放申请的堆内存空间
-- sock_num;
::closesocket(mySock);
DWORD dwIndex = polp -> index;
::GlobalFree(polp);
cout << endl << polp -> clientIP << " : " << polp -> clientPort << " Disconnected! " << endl << endl;
// 删掉的SOCKET后面往前挪
for (DWORD i = dwIndex; i < sock_num; ++ i)
{
sock_array[i] = sock_array[i + 1 ];
}
continue ; // 不要关闭工作者线程
}
WSABUF wBuffer;
wBuffer.buf = polp -> buf;
wBuffer.len = BUFFER_SIZE;
// 将收到的数据反馈给每个客户端
for (DWORD i = 0 ; i < sock_num; ++ i)
{
::send(sock_array[i], polp -> buf, ( int )strlen(polp -> buf) + 1 , 0 );
}
// 输出收到的数据,并投递一个WSARecv请求
cout << polp -> clientIP << " : " << polp -> buf << endl;
::WSARecv(mySock, & wBuffer, 1 , & dwSend, & dwFlag, & (polp -> ol), NULL);
}
return 0 ;
}
int main()
{
cout << " Input your IP: " << endl;
cin >> SERVER_IP;
StartServer();
return 0 ;
}
#include < winsock2.h >
#pragma comment (lib, " Ws2_32.lib " )
using namespace std;
char SERVER_IP[ 32 ] = " 127.0.0.1 " ;
const DWORD SERVER_PORT = 1555 ;
const DWORD BUFFER_SIZE = 1024 ;
// OverLapped结构体扩展
struct OVERLAPPEDPLUS
{
OVERLAPPED ol;
char buf[BUFFER_SIZE]; // 在SOCKET上收数据的buffer
char clientIP[ 32 ]; // 客户端IP
u_short clientPort; // 客户端port
DWORD index; // SOCKET在数组中的index,从0开始
};
SOCKET sock_array[ 64 ] = {NULL}; // 保存客户端SOCKET
DWORD sock_num = 0 ; // 记录SOCKET数
// 线程函数声明
DWORD CALLBACK ServerWorkerThread( void * CompletionPortID) ;
// Start function
void StartServer()
{
WSADATA wd;
::WSAStartup(MAKEWORD( 2 , 2 ), & wd);
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(SERVER_PORT);
addr.sin_addr.s_addr = inet_addr(SERVER_IP);
SOCKET s = ::socket(AF_INET, SOCK_STREAM, 0 );
if ( 0 != ::bind(s, ( const sockaddr * ) & addr, sizeof (addr)))
{
cout << " bind error: " << WSAGetLastError() << endl;
::closesocket(s);
::WSACleanup();
return ;
}
::listen(s, 2 );
// 创建完成端口
HANDLE CompetionPort = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL, 0 , 0 );
// 获得CPU数,创建CPU数*2+2个线程
SYSTEM_INFO SystenInfo;
::GetSystemInfo( & SystenInfo);
for (DWORD i = 0 ; i < SystenInfo.dwNumberOfProcessors * 2 + 2 ; ++ i)
{
DWORD ThreadID = 0 ;
HANDLE ThreadHandle = ::CreateThread(NULL, 0 ,ServerWorkerThread,CompetionPort, 0 , & ThreadID);
::CloseHandle(ThreadHandle);
}
cout << " Server Started! " << endl << endl;
while (TRUE)
{
sockaddr_in client_addr;
int fromlen = sizeof (sockaddr_in);
SOCKET ns = ::accept(s, (sockaddr * ) & client_addr, NULL);
// SOCKET入数组
sock_array[sock_num] = ns;
++ sock_num;
// 新客户端连接进来
::CreateIoCompletionPort((HANDLE)ns,CompetionPort,(DWORD)ns, 0 );
// 在堆中创建OverLapped扩展结构体的对象
OVERLAPPEDPLUS * olp = (OVERLAPPEDPLUS * )::GlobalAlloc(GPTR, sizeof (OVERLAPPEDPLUS));
// 根据SOCKET取客户端的IP和PORT,保存在OVERLAPPEDPLUS里
ZeroMemory( & client_addr, sizeof (sockaddr_in));
::getpeername(ns, (sockaddr * ) & client_addr, & fromlen);
strcpy_s(olp -> clientIP, 32 , inet_ntoa(client_addr.sin_addr));
olp -> clientPort = ntohs(client_addr.sin_port);
cout << endl << olp -> clientIP << " : " << olp -> clientPort << " Connected! " << endl << endl;
// 保存SOCKET的index
olp -> index = sock_num - 1 ;
WSABUF wBuffer;
wBuffer.buf = olp -> buf;
wBuffer.len = BUFFER_SIZE;
DWORD dwFlag = 0 ;
DWORD dwRecvd = 0 ;
// 在指定SOCKET上投递一个异步的IO请求
::WSARecvFrom(ns, & wBuffer, 1 , & dwRecvd, & dwFlag, (sockaddr * ) & client_addr, & fromlen, & (olp -> ol), NULL);
}
::closesocket(s);
::WSACleanup();
}
// 工作者线程
DWORD CALLBACK ServerWorkerThread( void * CompletionPortID)
{
HANDLE CompletionPort = (HANDLE) CompletionPortID;
DWORD BytesTransferred = 0 ;
SOCKET mySock;
OVERLAPPEDPLUS * polp;
DWORD dwSend = 0 , dwRecv = 0 , dwFlag = 0 ;
while (TRUE)
{
// 从指定的CP队列中取出一个完成包,一个完成包表示一次IO操作的完成;若队列为空则阻塞
::GetQueuedCompletionStatus(CompletionPort, & BytesTransferred, (LPDWORD) & mySock, (OVERLAPPED ** ) & polp, INFINITE);
if (BytesTransferred == 0 )
{
// 如果客户端关闭连接,则服务器端关闭SOCKET,释放申请的堆内存空间
-- sock_num;
::closesocket(mySock);
DWORD dwIndex = polp -> index;
::GlobalFree(polp);
cout << endl << polp -> clientIP << " : " << polp -> clientPort << " Disconnected! " << endl << endl;
// 删掉的SOCKET后面往前挪
for (DWORD i = dwIndex; i < sock_num; ++ i)
{
sock_array[i] = sock_array[i + 1 ];
}
continue ; // 不要关闭工作者线程
}
WSABUF wBuffer;
wBuffer.buf = polp -> buf;
wBuffer.len = BUFFER_SIZE;
// 将收到的数据反馈给每个客户端
for (DWORD i = 0 ; i < sock_num; ++ i)
{
::send(sock_array[i], polp -> buf, ( int )strlen(polp -> buf) + 1 , 0 );
}
// 输出收到的数据,并投递一个WSARecv请求
cout << polp -> clientIP << " : " << polp -> buf << endl;
::WSARecv(mySock, & wBuffer, 1 , & dwSend, & dwFlag, & (polp -> ol), NULL);
}
return 0 ;
}
int main()
{
cout << " Input your IP: " << endl;
cin >> SERVER_IP;
StartServer();
return 0 ;
}
客户端:
#include
<
iostream
>
#include < winsock2.h >
#pragma comment (lib, " Ws2_32.lib " )
using namespace std;
char SERVER_IP[ 32 ] = " 127.0.0.1 " ;
const DWORD SERVER_PORT = 1555 ;
// 线程函数声明
DWORD CALLBACK ReceiveThread( void * CompletionPortID) ;
int main()
{
cout << " Input server IP: " << endl;
cin >> SERVER_IP;
// 初始化使用socket函数要用到的dll
WSADATA wd;
WSAStartup(MAKEWORD( 2 , 2 ), & wd);
// 服务器地址信息
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(SERVER_PORT);
addr.sin_addr.s_addr = inet_addr(SERVER_IP);
// 创建套接字,并与服务器连接
SOCKET s = ::socket(AF_INET, SOCK_STREAM, 0 );
cout << " Connecting to: " << SERVER_IP << " : " << SERVER_PORT << endl;
while ( 0 != ::connect(s, ( const sockaddr * ) & addr, sizeof (addr)))
{
}
cout << " Connected! " << endl << endl;
DWORD ThreadID = 0 ;
// 创建线程,传入参数为SOCKET
HANDLE ThreadHandle = ::CreateThread(NULL, 0 ,ReceiveThread, & s, 0 , & ThreadID);
::CloseHandle(ThreadHandle);
// 发送数据
while ( 1 )
{
char cData[ 1024 ] = { 0 };
cin.getline(cData, 1024 );
::send(s, cData, ( int )strlen(cData) + 1 , 0 );
}
// 关闭套接字
::closesocket(s);
// ws2.dll的收尾工作
::WSACleanup();
return 0 ;
}
DWORD CALLBACK ReceiveThread( void * p)
{
while (TRUE)
{
char cBuffer[ 1024 ] = { 0 };
if (SOCKET_ERROR == ::recv( * (SOCKET * )p, cBuffer, 1024 , 0 ))
{
cout << " \nServer Closed! " << endl;
break ;
}
cout << " ****** " << cBuffer << endl;
}
return 0 ;
}
#include < winsock2.h >
#pragma comment (lib, " Ws2_32.lib " )
using namespace std;
char SERVER_IP[ 32 ] = " 127.0.0.1 " ;
const DWORD SERVER_PORT = 1555 ;
// 线程函数声明
DWORD CALLBACK ReceiveThread( void * CompletionPortID) ;
int main()
{
cout << " Input server IP: " << endl;
cin >> SERVER_IP;
// 初始化使用socket函数要用到的dll
WSADATA wd;
WSAStartup(MAKEWORD( 2 , 2 ), & wd);
// 服务器地址信息
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(SERVER_PORT);
addr.sin_addr.s_addr = inet_addr(SERVER_IP);
// 创建套接字,并与服务器连接
SOCKET s = ::socket(AF_INET, SOCK_STREAM, 0 );
cout << " Connecting to: " << SERVER_IP << " : " << SERVER_PORT << endl;
while ( 0 != ::connect(s, ( const sockaddr * ) & addr, sizeof (addr)))
{
}
cout << " Connected! " << endl << endl;
DWORD ThreadID = 0 ;
// 创建线程,传入参数为SOCKET
HANDLE ThreadHandle = ::CreateThread(NULL, 0 ,ReceiveThread, & s, 0 , & ThreadID);
::CloseHandle(ThreadHandle);
// 发送数据
while ( 1 )
{
char cData[ 1024 ] = { 0 };
cin.getline(cData, 1024 );
::send(s, cData, ( int )strlen(cData) + 1 , 0 );
}
// 关闭套接字
::closesocket(s);
// ws2.dll的收尾工作
::WSACleanup();
return 0 ;
}
DWORD CALLBACK ReceiveThread( void * p)
{
while (TRUE)
{
char cBuffer[ 1024 ] = { 0 };
if (SOCKET_ERROR == ::recv( * (SOCKET * )p, cBuffer, 1024 , 0 ))
{
cout << " \nServer Closed! " << endl;
break ;
}
cout << " ****** " << cBuffer << endl;
}
return 0 ;
}