📄 serversocket.cpp
字号:
#include "StdAfx.h"
#include ".\serversocket.h"
#define DATA_BUFSIZE 4096
CWinThread* pServerListenThread = NULL;
CWinThread* pOverlappedThread = NULL;
SOCKET sockListen;
SOCKET sockAcceptArray[WSA_MAXIMUM_WAIT_EVENTS]; // 与客户端通信的SOCKET
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS]; // 与重叠结构配套的事件组
WSAOVERLAPPED AcceptOverlapped[WSA_MAXIMUM_WAIT_EVENTS]; // 重叠结构,每个SOCKET操作对应一个
WSABUF DataBuf[WSA_MAXIMUM_WAIT_EVENTS]; // 接收缓冲区,WSARecv的参数,每个SCOKET对应一个
DWORD dwEventTotal = 0,
dwRecvBytes = 0,
Flags = 0;
int nSockIndex = 0; // socket数组的编号
BOOL bOverlapped = FALSE; // 是否处理重叠请求
// 监听线程
UINT _ServerListenThread(LPVOID lParam)
{
TRACE("服务器端监听中.....\n");
CServerSocket* pServer = (CServerSocket*)lParam;
SOCKADDR_IN ClientAddr; // 定义一个客户端得地址结构作为参数
int addr_length=sizeof(ClientAddr);
while(TRUE)
{
if(dwEventTotal>=WSA_MAXIMUM_WAIT_EVENTS) // 因为超出了Windows的最大等待事件数量
{
AfxMessageBox("已达到最大连接数!");
continue;
}
SOCKET sockTemp = accept(sockListen,(SOCKADDR*)&ClientAddr,&addr_length);
if(sockTemp == INVALID_SOCKET)
{
AfxMessageBox("Accept Connection failed!");
continue;
}
nSockIndex = pServer->GetEmptySocket(); // 从Socket数组中获得一个空闲的socket
sockAcceptArray[nSockIndex] = sockTemp;
// 这里可以取得客户端的IP和端口,但是我们只取其中的SOCKET编号
LPCTSTR lpIP = inet_ntoa(ClientAddr.sin_addr);
UINT nPort = ClientAddr.sin_port;
CString strSock;
strSock.Format("SOCKET编号:%d",sockAcceptArray[nSockIndex] );
::SendMessage(pServer->m_hNotifyWnd,WM_MSG_NEW_SOCKET,
(LPARAM)(LPCTSTR)strSock,(LPARAM)(LPCTSTR)"客户端建立连接!");
// 接收客户端连接以后,为每一个连入的SOCKET都初始化建立一个重叠结构
Flags = 0;
EventArray[nSockIndex] = WSACreateEvent();
ZeroMemory(&AcceptOverlapped[nSockIndex],sizeof(WSAOVERLAPPED));
char buffer[DATA_BUFSIZE];
ZeroMemory(buffer,DATA_BUFSIZE);
AcceptOverlapped[nSockIndex].hEvent = EventArray[nSockIndex]; // 关联事件
DataBuf[nSockIndex].len = DATA_BUFSIZE;
DataBuf[nSockIndex].buf = buffer;
// 投递第一个WSARecv请求,以便开始在套接字上接受数据
if(WSARecv(sockAcceptArray[nSockIndex] ,&DataBuf[nSockIndex],1,&dwRecvBytes,&Flags,
& AcceptOverlapped[nSockIndex] ,NULL) == SOCKET_ERROR)
{
if(WSAGetLastError() != WSA_IO_PENDING)
{
// 返回WSA_IO_PENDING是正常情况,表示IO操作正在进行,不能立即完成
// 如果不是WSA_IO_PENDING错误,就表示操作失败了
AfxMessageBox("错误:第一次投递Recv操作失败!!此套接字将被关闭!");
closesocket(sockAcceptArray[nSockIndex]);
sockAcceptArray[nSockIndex] = INVALID_SOCKET;
WSACloseEvent(EventArray[nSockIndex]);
continue;
}
}
dwEventTotal ++;
if(dwEventTotal == 1) // 此时如果dwEventTotal加以后等于1
// 就说明_OverlappedThread休眠了,此时唤醒之
pOverlappedThread->ResumeThread();
}
return 0;
}
// 重叠I/O处理线程
UINT _OverlappedThread(LPVOID lParam)
{
CServerSocket* pServer = (CServerSocket*)lParam;
while(bOverlapped) // 循环检测事件数组中的事件,并对接收的数据进行处理:)
{
DWORD dwIndex;
// 等候重叠I/O调用结束
// 因为我们把事件和Overlapped绑定在一起,重叠操作完成后我们会接到事件通知
dwIndex = WSAWaitForMultipleEvents(dwEventTotal,EventArray,FALSE,10,FALSE);
if(dwIndex == WSA_WAIT_TIMEOUT)
continue;
if(dwIndex == WSA_WAIT_FAILED) // 出现监听错误
{
int nErrorCode = WSAGetLastError();
if(nErrorCode == WSA_INVALID_HANDLE)
{
AfxMessageBox("监听出现错误:无效的 lphEvents 参数!"); // 代码经常会出现这种错误,我实在没时间改了,55555555
}
else
if(nErrorCode == WSA_INVALID_PARAMETER)
{
AfxMessageBox("监听出现错误:无效的 CEvents 参数!");
}
continue;
}
// 取得索引值,得知事件的索引号
dwIndex = dwIndex - WSA_WAIT_EVENT_0;
// 获得索引号以后,这个事件已经没有利用价值,重置之
WSAResetEvent(EventArray[dwIndex]);
// 然后确定当前索引号的SOCKET的重叠请求状态
DWORD dwBytesTransferred;
WSAOVERLAPPED& CurrentOverlapped = AcceptOverlapped[dwIndex]; // 这里纯粹为了减少代码长度,付给了一个临时变量
SOCKET& sockCurrent = sockAcceptArray[dwIndex] ; // 同上
WSAGetOverlappedResult(sockCurrent,&CurrentOverlapped ,
&dwBytesTransferred,FALSE,&Flags);
// 先检查通信对方是否已经关闭连接
// 如果==0则表示连接已经,则关闭套接字
if(dwBytesTransferred == 0)
{
CString strSock;
strSock.Format("SOCKET编号:%d",sockAcceptArray[dwIndex] );
::SendMessage(pServer->m_hNotifyWnd,WM_MSG_NEW_SOCKET,
(LPARAM)(LPCTSTR)strSock,(LPARAM)(LPCTSTR)"客户端断开连接!");
closesocket(sockCurrent);
sockCurrent = INVALID_SOCKET;
//WSACloseEvent( EventArray[dwIndex] );
dwEventTotal--;
if(dwEventTotal <= 0) // 如果没有事件等待则暂停重叠IO处理线程
{
pOverlappedThread->SuspendThread(); // 没有需要处理的重叠结构了,则本线程休眠
}
continue;
}
// DataBuf中包含接收到的数据,我们发到对话框中给予显示
CString strSock;
strSock.Format("SOCKET编号:%d",sockCurrent);
::SendMessage(pServer->m_hNotifyWnd,WM_MSG_NEW_MSG,
(LPARAM)(LPCTSTR)strSock,(LPARAM)(LPCTSTR)DataBuf[dwIndex].buf);
// 然后在套接字上投递另一个WSARecv请求(不要晕,注意看,代码和前面第一次WSARecv一样^_^)
Flags = 0;
ZeroMemory(&CurrentOverlapped,sizeof(WSAOVERLAPPED));
char buffer[DATA_BUFSIZE];
ZeroMemory(buffer,DATA_BUFSIZE);
CurrentOverlapped.hEvent = EventArray[dwIndex];
DataBuf[dwIndex].len = DATA_BUFSIZE;
DataBuf[dwIndex].buf = buffer;
// 开始另外一个WSARecv
if(WSARecv(sockCurrent,&DataBuf[dwIndex],1,&dwRecvBytes,&Flags,
&CurrentOverlapped ,NULL) == SOCKET_ERROR)
{
if(WSAGetLastError() != WSA_IO_PENDING)
{
AfxMessageBox("错误:投递Recv操作失败!!此套接字将被关闭!");
closesocket(sockCurrent);
sockCurrent = INVALID_SOCKET;
WSACloseEvent(EventArray[dwIndex]);
dwEventTotal--;
continue;
}
}
}
return 0;
}
CServerSocket::CServerSocket(void)
{
for(int i=0;i<WSA_MAXIMUM_WAIT_EVENTS;i++) // 初始化SOCKET
{
sockAcceptArray[i] = INVALID_SOCKET;
}
}
CServerSocket::~CServerSocket(void)
{
TRACE("CServerSocket 析构!");
if(pServerListenThread != NULL)
{
DWORD dwStatus;
::GetExitCodeThread(pServerListenThread->m_hThread, &dwStatus);
if (dwStatus == STILL_ACTIVE)
{
delete pServerListenThread;
pServerListenThread = NULL;
}
}
if(pOverlappedThread != NULL)
{
DWORD dwStatus;
::GetExitCodeThread(pOverlappedThread->m_hThread, &dwStatus);
if (dwStatus == STILL_ACTIVE)
{
delete pOverlappedThread;
pOverlappedThread = NULL;
}
}
WSACleanup();
}
// 这些都是固定套路了,没有区别
int CServerSocket::StartListening(const UINT& port)
{
m_nPort = port;
WSADATA wsaData;
int nRet;
nRet=WSAStartup(MAKEWORD(2,2),&wsaData); //开启winsock.dll
if(nRet!=0)
{
AfxMessageBox("Load winsock2 failed");
WSACleanup();
return -1;
}
sockListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //创建服务套接字(流式)
SOCKADDR_IN ServerAddr; //分配端口及协议族并绑定
ServerAddr.sin_family=AF_INET;
ServerAddr.sin_addr.S_un.S_addr =htonl(INADDR_ANY);
ServerAddr.sin_port=htons(port);
nRet=bind(sockListen,(LPSOCKADDR)&ServerAddr,sizeof(ServerAddr)); // 绑定套接字
if(nRet==SOCKET_ERROR)
{
AfxMessageBox("Bind Socket Fail!");
closesocket(sockListen);
return -1;
}
nRet=listen(sockListen,5); //开始监听,并设置监听客户端数量
if(nRet==SOCKET_ERROR)
{
AfxMessageBox("Listening Fail!");
return -1;
}
pServerListenThread = AfxBeginThread(_ServerListenThread,this); // 开始监听线程
bOverlapped = TRUE;
// 开始重叠IO线程,但是先休眠,因为没有需要等待的事件。
pOverlappedThread = AfxBeginThread(_OverlappedThread,this,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED,NULL);
return 0;
}
int CServerSocket::StopListening()
{
bOverlapped = FALSE;
return 0;
}
// 返回一个空闲的SOCKET,算法可以改进的,这样穷举效率太低
// 一定要改,不要偷懒啊^_^
int CServerSocket::GetEmptySocket()
{
for(int i=0;i<WSA_MAXIMUM_WAIT_EVENTS;i++)
{
if(sockAcceptArray[i]==INVALID_SOCKET)
{
return i;
}
}
return -1;
}
// 获得本机IP
CString CServerSocket::GetLocalIP()
{
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)==SOCKET_ERROR)
{
AfxMessageBox("取得主机名字失败!");
return "未能获得";
}
int nLen=20;
char hostname[20];
gethostname(hostname,nLen); // 获得本机主机名
CString strHostName(hostname);
struct hostent FAR* lpHostEnt = gethostbyname(strHostName);
if(lpHostEnt == NULL)
{
return "0.0.0.0";
}
LPSTR lpAddr = lpHostEnt->h_addr_list[0]; // 取得IP地址列表中的第一个为返回的IP
struct in_addr inAddr;
memmove(&inAddr,lpAddr,4);
return inet_ntoa(inAddr); // 转化成标准的IP地址形式
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -