📄 serversocket.cpp
字号:
#include "StdAfx.h"
#include ".\serversocket.h"
#define DATA_BUFSIZE 4096 // 接收缓冲区
#define MAX_SOCKET 100 // 最大可以连入的SOCKET数量,这里可以自行更改
// 重叠I/O模型在P4级别的主机上可以处理上万个工作量不大的SOCKET连接
#define NULLSOCKET -1
CWinThread* pServerListenThread = NULL;
CWinThread* pWaitForCompletionThread = NULL;
SOCKET sockListen; // 监听SOCKET,用以接收客户端连接
SOCKET sockArray[MAX_SOCKET]; // 与客户端通信的SOCKET
WSAOVERLAPPED AcceptOverlapped[MAX_SOCKET]; // 重叠结构数组,每个事件对应一个
WSABUF DataBuf[MAX_SOCKET]; // 缓冲区,WSARecv的参数
WSAEVENT EventArray[1]; // 因为WSAWaitForMultipleEvents() API要求在一个或多个事件对象上等待,
// 因此不得不创建一个伪事件对象.详见配套文章
// 但是这个事件数组已经不是和SOCKET相关联的了
int nSockIndex = 0, // SOCKET序号
nSockTotal = 0, // SOCKET总数
nCurSockIndex = 0; // 当前完成重叠操作的SOCKET
BOOL bNewSocket = FALSE; // 是否是第一次投递SOCKET上的WSARecv操作
HWND CServerSocket::m_hNotifyWnd = NULL; // 主对话框句柄,用以发送消息
int GetCurrentSocketIndex(LPWSAOVERLAPPED Overlapped);
void ReleaseSocket(const int);
///////////////////////////////////////////////////////////////////////////////////
//
// Purposes: 系统自动调用的回调函数
//
// 在我们投递的WSARecv操作完成的时候,系统会自动调用这个函数
//
////////////////////////////////////////////////////////////////////////////////////
void CALLBACK CompletionRoutine(DWORD Error,
DWORD BytesTransfered,
LPWSAOVERLAPPED Overlapped,
DWORD inFlags)
{
TRACE("回调CompletionRoutine......");
nCurSockIndex = GetCurrentSocketIndex(Overlapped); // 根据传入的重叠结构,
// 来寻找究竟是哪个SOCKET上触发了事件
//////////////////////////////////////////////////////////////////////
// 错误处理:可能是对方关闭套接字,或者发生一个严重错误
if(Error != 0 || BytesTransfered == 0)
{
ReleaseSocket(nCurSockIndex);
return;
}
TRACE("数据:");
TRACE(DataBuf[nCurSockIndex].buf);
//////////////////////////////////////////////////////////////////////////////////
// 程序执行到这里,说明我们先前投递的WSARecv操作已经胜利完成了!!!^_^
// DataBuf结构里面就有客户端传来的数据了!!^_^
CServerSocket::ShowMessage(nCurSockIndex,CString(DataBuf[nCurSockIndex].buf));
return;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Purposes: 监听端口,接收连入的连接
//
////////////////////////////////////////////////////////////////////////////////////
UINT _ServerListenThread(LPVOID lParam)
{
TRACE("服务器端监听中.....\n");
CServerSocket* pServer = (CServerSocket*)lParam;
SOCKADDR_IN ClientAddr; // 定义一个客户端得地址结构作为参数
int addr_length=sizeof(ClientAddr);
while(TRUE)
{
SOCKET sockTemp = accept( // 接收连入的客户端
sockListen,
(SOCKADDR*)&ClientAddr,
&addr_length
);
if(sockTemp == INVALID_SOCKET)
{
AfxMessageBox("Accept Connection failed!");
continue;
}
nSockIndex = pServer->GetEmptySocket(); // 获得一个空闲的SOCKET索引号
sockArray[nSockIndex] = sockTemp;
// 这里可以取得客户端的IP和端口,但是我们只取其中的SOCKET编号
//LPCTSTR lpIP = inet_ntoa(ClientAddr.sin_addr); // IP
//UINT nPort = ntohs(ClientAddr.sin_port); // PORT
CServerSocket::ShowMessage(nSockIndex,"客户端建立连接!");
nSockTotal++; // SOCKET总数加一
bNewSocket = TRUE; // 标志投递一个新的WSARecv请求
if(nSockTotal == 1) // SOCKET数量不为空时激活处理线程
pWaitForCompletionThread->ResumeThread();
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////
//
// Purposes: 用于投递第一个WSARecv请求,并等待系统完成的通知,然后继续投递后续的请求
//
////////////////////////////////////////////////////////////////////////////////////////////
UINT _WaitForCompletionThread(LPVOID lParam)
{
TRACE("开始等待线程....\n");
EventArray[0] = WSACreateEvent(); // 建立一个事件
DWORD dwRecvBytes = 0, // WSARecv的参数
Flags = 0;
while(TRUE)
{
if(bNewSocket) // 如果标志为True,则表示投递第一个WSARecv请求!
{
bNewSocket = FALSE;
//************************************************************************
//
// 现在开始投递第一个WSARecv请求!
//
//************************************************************************
Flags = 0;
ZeroMemory(&AcceptOverlapped[nSockIndex],sizeof(WSAOVERLAPPED));
char buffer[DATA_BUFSIZE];
ZeroMemory(buffer,DATA_BUFSIZE);
DataBuf[nSockIndex].len = DATA_BUFSIZE;
DataBuf[nSockIndex].buf = buffer;
// 将WSAOVERLAPPED结构指定为一个参数,在套接字上投递一个异步WSARecv()请求
// 并提供下面的作为完成例程的CompletionRoutine回调函数
if(WSARecv(
sockArray[nSockIndex],
&DataBuf[nSockIndex],
1,
&dwRecvBytes,
&Flags,
&AcceptOverlapped[nSockIndex],
CompletionRoutine) == SOCKET_ERROR)
{
if(WSAGetLastError() != WSA_IO_PENDING)
{
ReleaseSocket(nSockIndex);
continue;
}
}
}
////////////////////////////////////////////////////////////////////////////////
// 等待重叠请求完成,自动回调完成例程函数
DWORD dwIndex = WSAWaitForMultipleEvents(1,EventArray,FALSE,10,TRUE);
///////////////////////////////////////////////////////////////////////////////////
// 返回WAIT_IO_COMPLETION表示一个重叠请求完成例程例结束。继续为更多的完成例程服务
if(dwIndex == WAIT_IO_COMPLETION)
{
TRACE("重叠操作完成...\n");
//************************************************************************
//
// 现在开始投递后续的WSARecv请求!
//
//**********************************************************************
// 前一个完成例程结束以后,开始在此套接字上投递下一个WSARecv,代码和前面的一模一样^_^
if(nCurSockIndex != NULLSOCKET) // 这个nCurSockIndex来自于前面完成例程得到的那个
{
Flags = 0;
ZeroMemory(&AcceptOverlapped[nCurSockIndex],sizeof(WSAOVERLAPPED));
char buffer[DATA_BUFSIZE];
ZeroMemory(buffer,DATA_BUFSIZE);
DataBuf[nCurSockIndex].len = DATA_BUFSIZE;
DataBuf[nCurSockIndex].buf = buffer;
/////////////////////////////////////////////////////////////////////////////////////
// 将WSAOVERLAPPED结构制定为一个参数,在套接字上投递一个异步WSARecv()请求
// 并提供下面作为完成例程的CompletionRoutine回调函数
if(WSARecv(
sockArray[nCurSockIndex],
&DataBuf[nCurSockIndex],
1,
&dwRecvBytes,
&Flags,
&AcceptOverlapped[nCurSockIndex],
CompletionRoutine // 注意这个回调函数
) == SOCKET_ERROR)
{
if(WSAGetLastError() != WSA_IO_PENDING) // 出现错误,关闭SOCKET
{
ReleaseSocket(nCurSockIndex);
continue;
}
}
// 这里最好添加一个步骤,去掉数组中无效的SOCKET
// 因为非正常关闭的客户端,比如拔掉网线等,这里是不会接到通知的
}
continue;
}
else
{
if(dwIndex == WAIT_TIMEOUT) // 继续等待
continue;
else // 如果出现其他错误就坏事了
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -