客户端向服务器发送数据,并显示服务器反馈的内容。
服务器采用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 <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;
}