继续之前的异步重叠的系列,本文实践的为TCPSrv的完成端口实现,之前文章中层提到过该概念,也是被奉为WINDOWS SOCKET编程的最优解。但是否真的是最优,本文没测试,很多东西,个人认为看情形用远比抱着一个什么最好的情结更实用。
依旧的风格,直接上代码,至于想捣鼓IOCP的人,看完整篇的代码也应该懂了。我还是相信代码以及运行结果远比一本书的文字来的有说服力。当然我的代码中必要的注释都还是存在的,以协助说明思路。
编写主体前准备工作:
View Code
1 #include <afxtempl.h>
2 #include "winsock2.h"
3 #include <Mswsock.h>
4 #include <list>
5 #include <vector>
6 #include <string>
7 #include <process.h>
8 #include <time.h>
9 #include <map>
10
11 #include "iostream"
12 using namespace std;
13
14 #define SOCKET_BUF_SIZE 10240 // 发送、接收缓冲区最大长度,10K
15 #define OVERLAPPED_DELIVER_SEND 0 // 发送数据
16 #define OVERLAPPED_DELIVER_RECV 2 // 接收数据
17
18 class MyTcpSrv;
19
20 // 客户端套接字元
21 typedef struct _CLIENT_ELEMENT
22 {
23 SOCKET s_socket; // 客户端SOCKET套接字
24 SOCKADDR_IN s_clientInfo; // 客户端终端信息
25 time_t s_tVisitTM; // 访问时间
26 bool s_bFirstFrame; // 第一帧标志位
27
28 _CLIENT_ELEMENT()
29 {
30 s_socket = INVALID_SOCKET;
31 }
32 } TS_CLIENT_ELEMENT, * LPTS_CLIENT_ELEMENT;
33
34 // 通信载体,此处使用尾随参数的解决思路,故而OVERLAPPED必须为第一个参数
35 typedef struct _SOCKET_INFORMATION
36 {
37 OVERLAPPED s_overlapped;
38 CHAR s_buffer[SOCKET_BUF_SIZE]; // 存储接收的字符
39 int s_nBufLen; // 接收的长度
40 WSABUF s_dataBuf; // 接收缓冲区,为OS自动填充区
41 LPTS_CLIENT_ELEMENT s_lpClientElement; // 客户端套接字元
42 UINT s_uDeliverType; // 异步请求类型
43 int s_uAntiSendSize; // 预期发送字节数
44 bool s_bIsDiscard; // 作废标志位
45 MyTcpSrv* s_pThis; // 所有者
46
47 _SOCKET_INFORMATION()
48 {
49 s_lpClientElement = NULL;
50 s_pThis = NULL;
51 }
52 } SOCKET_INFORMATION, * LPSOCKET_INFORMATION;
53
54 // TCPSrv、UDP网络报文数据结构
55 typedef struct _netPara
56 {
57 UCHAR s_szBuf[SOCKET_BUF_SIZE]; // 数据缓存
58 WORD s_wBufLen; // 数据长度
59
60 // 追加字符
61 void Append(_netPara* pNet)
62 {
63 memcpy(s_szBuf + s_wBufLen,pNet->s_szBuf,min(SOCKET_BUF_SIZE - s_wBufLen, pNet->s_wBufLen));
64 s_wBufLen += pNet->s_wBufLen;
65 }
66 // 释放
67 void Release(UINT uSize)
68 {
69 UCHAR szBuf[SOCKET_BUF_SIZE];
70 memcpy(szBuf, s_szBuf + uSize, s_wBufLen - uSize);
71 memset(s_szBuf, 0, SOCKET_BUF_SIZE);
72 memcpy(s_szBuf, szBuf, s_wBufLen - uSize);
73 s_wBufLen = s_wBufLen - uSize;
74 }
75 }SNetPara, * LPNetPara;
76
77
78 // 终端信息结构
79 typedef struct _terminal_info
80 {
81 string s_ip; // IP地址
82 USHORT s_uPort; // 端口号
83 // 重载<运算符,以支持作为map的key
84 friend inline bool operator < (const _terminal_info &ls, const _terminal_info &rs);
85 friend inline bool operator == (const _terminal_info &ls, const _terminal_info &rs);
86 }TERMINAL_INFO, * LPTERMINAL_INFO;
87
88 // LESS
89 bool operator < (const _terminal_info& ls, const _terminal_info& rs)
90 {
91 return (ls.s_ip < rs.s_ip || (ls.s_ip == rs.s_ip && ls.s_uPort < rs.s_uPort));
92 }
93
94 // EQUAL
95 bool operator == (const _terminal_info& ls, const _terminal_info& rs)
96 {
97 return (ls.s_ip == rs.s_ip && ls.s_uPort == rs.s_uPort);
98 }
99
100 typedef vector<TS_CLIENT_ELEMENT> VClnt; // 客户端信息列表
101 typedef list<SNetPara> LNetPara; // 接收信息列表
102
103 typedef map<TERMINAL_INFO, SNetPara> MNetDatagram; // 网络数据包
2 #include "winsock2.h"
3 #include <Mswsock.h>
4 #include <list>
5 #include <vector>
6 #include <string>
7 #include <process.h>
8 #include <time.h>
9 #include <map>
10
11 #include "iostream"
12 using namespace std;
13
14 #define SOCKET_BUF_SIZE 10240 // 发送、接收缓冲区最大长度,10K
15 #define OVERLAPPED_DELIVER_SEND 0 // 发送数据
16 #define OVERLAPPED_DELIVER_RECV 2 // 接收数据
17
18 class MyTcpSrv;
19
20 // 客户端套接字元
21 typedef struct _CLIENT_ELEMENT
22 {
23 SOCKET s_socket; // 客户端SOCKET套接字
24 SOCKADDR_IN s_clientInfo; // 客户端终端信息
25 time_t s_tVisitTM; // 访问时间
26 bool s_bFirstFrame; // 第一帧标志位
27
28 _CLIENT_ELEMENT()
29 {
30 s_socket = INVALID_SOCKET;
31 }
32 } TS_CLIENT_ELEMENT, * LPTS_CLIENT_ELEMENT;
33
34 // 通信载体,此处使用尾随参数的解决思路,故而OVERLAPPED必须为第一个参数
35 typedef struct _SOCKET_INFORMATION
36 {
37 OVERLAPPED s_overlapped;
38 CHAR s_buffer[SOCKET_BUF_SIZE]; // 存储接收的字符
39 int s_nBufLen; // 接收的长度
40 WSABUF s_dataBuf; // 接收缓冲区,为OS自动填充区
41 LPTS_CLIENT_ELEMENT s_lpClientElement; // 客户端套接字元
42 UINT s_uDeliverType; // 异步请求类型
43 int s_uAntiSendSize; // 预期发送字节数
44 bool s_bIsDiscard; // 作废标志位
45 MyTcpSrv* s_pThis; // 所有者
46
47 _SOCKET_INFORMATION()
48 {
49 s_lpClientElement = NULL;
50 s_pThis = NULL;
51 }
52 } SOCKET_INFORMATION, * LPSOCKET_INFORMATION;
53
54 // TCPSrv、UDP网络报文数据结构
55 typedef struct _netPara
56 {
57 UCHAR s_szBuf[SOCKET_BUF_SIZE]; // 数据缓存
58 WORD s_wBufLen; // 数据长度
59
60 // 追加字符
61 void Append(_netPara* pNet)
62 {
63 memcpy(s_szBuf + s_wBufLen,pNet->s_szBuf,min(SOCKET_BUF_SIZE - s_wBufLen, pNet->s_wBufLen));
64 s_wBufLen += pNet->s_wBufLen;
65 }
66 // 释放
67 void Release(UINT uSize)
68 {
69 UCHAR szBuf[SOCKET_BUF_SIZE];
70 memcpy(szBuf, s_szBuf + uSize, s_wBufLen - uSize);
71 memset(s_szBuf, 0, SOCKET_BUF_SIZE);
72 memcpy(s_szBuf, szBuf, s_wBufLen - uSize);
73 s_wBufLen = s_wBufLen - uSize;
74 }
75 }SNetPara, * LPNetPara;
76
77
78 // 终端信息结构
79 typedef struct _terminal_info
80 {
81 string s_ip; // IP地址
82 USHORT s_uPort; // 端口号
83 // 重载<运算符,以支持作为map的key
84 friend inline bool operator < (const _terminal_info &ls, const _terminal_info &rs);
85 friend inline bool operator == (const _terminal_info &ls, const _terminal_info &rs);
86 }TERMINAL_INFO, * LPTERMINAL_INFO;
87
88 // LESS
89 bool operator < (const _terminal_info& ls, const _terminal_info& rs)
90 {
91 return (ls.s_ip < rs.s_ip || (ls.s_ip == rs.s_ip && ls.s_uPort < rs.s_uPort));
92 }
93
94 // EQUAL
95 bool operator == (const _terminal_info& ls, const _terminal_info& rs)
96 {
97 return (ls.s_ip == rs.s_ip && ls.s_uPort == rs.s_uPort);
98 }
99
100 typedef vector<TS_CLIENT_ELEMENT> VClnt; // 客户端信息列表
101 typedef list<SNetPara> LNetPara; // 接收信息列表
102
103 typedef map<TERMINAL_INFO, SNetPara> MNetDatagram; // 网络数据包
主体结构设计:
View Code
1 // TCP服务器端
2 class MyTcpSrv
3 {
4 private:
5 CRITICAL_SECTION m_csLock; // 临界对象,冲突锁
6
7 HANDLE m_hIOCP; // 完成端口句柄
8
9 SOCKADDR_IN m_LocalAddr; // 本地终端地址
10 SOCKET m_sLinstenSocket; // 侦听套接字
11 HANDLE m_hListenThread; // 侦听线程
12 UINT m_nListenThreadId; // 侦听线程ID
13 HANDLE m_hListenEventExit; // 侦听线程退出事件对象,用于退出侦听线程时通知子线程关闭
14 SYSTEM_INFO m_sysInfo; // 系统信息,其中包含处理器等信息
15 VClnt m_clntList; // 客户端清单
16 MNetDatagram m_netParaMap; // 接收信息列表
17
18 public:
19 MyTcpSrv()
20 {
21 InitializeCriticalSection(&m_csLock);
22 m_hIOCP = INVALID_HANDLE_VALUE;
23 m_sLinstenSocket = INVALID_SOCKET;
24 m_hListenThread = INVALID_HANDLE_VALUE;
25 m_hListenEventExit = INVALID_HANDLE_VALUE;
26 }
27 ~MyTcpSrv()
28 {
29 DeleteCriticalSection(&m_csLock);
30 }
31
32 private:
33 // 加载SOCK DLL
34 bool LoadSock();
35
36 // 设置本地终端
37 bool SetLocalAddr(WORD nPort, const char *szAddr);
38
39 // 初始化完成端口
40 bool InitialIOCP();
41
42 // 设置侦听套接字
43 bool SetupListenSocket();
44
45 // 投递接收请求
46 void DeliverRecv(LPSOCKET_INFORMATION lpInfo);
47
48 // 投递发送请求
49 void DeliverSend(LPSOCKET_INFORMATION lpInfo);
50
51 // 启动侦听线程
52 bool RunListenThread();
53
54 // 侦听例程
55 UINT static WINAPI TCPListenThreadProc(LPVOID pParam);
56
57 // 工作例程
58 UINT static WINAPI ServerWorkerThread(LPVOID CompletionPortID);
59
60 // 擦除记录
61 void Erase(LPSOCKET_INFORMATION lpSocketInfo);
62
63 // 解析获取的数据
64 void OnRecvData(LPSOCKET_INFORMATION lpSocketInfo);
65
66 // 停止侦听
67 void StopListening();
68
69 // 断开与指定客户端连接
70 void DisconnectUser(SOCKADDR_IN clientAddr);
71
72 // 断开与所有用户连接
73 void DisconnectAllUsers();
74
75 // 卸载SOCKDLL
76 void UnloadSock();
77
78 public:
79 // 创建通道
80 bool Create(LPTERMINAL_INFO pInfo);
81
82 // 关闭通道
83 void Close();
84
85 // 读取数据
86 int Read(byte byRecv[], UINT uMaxSize, LPTERMINAL_INFO pInfo);
87
88 // 发送数据
89 int Write(byte bySend[], UINT uAntiSize, LPTERMINAL_INFO pInfo);
90
91 // 重置服务器
92 bool Reset();
93
94 // 获取客户端列表信息
95 VClnt GetClinetList()
96 {
97 return m_clntList;
98 }
99 };
2 class MyTcpSrv
3 {
4 private:
5 CRITICAL_SECTION m_csLock; // 临界对象,冲突锁
6
7 HANDLE m_hIOCP; // 完成端口句柄
8
9 SOCKADDR_IN m_LocalAddr; // 本地终端地址
10 SOCKET m_sLinstenSocket; // 侦听套接字
11 HANDLE m_hListenThread; // 侦听线程
12 UINT m_nListenThreadId; // 侦听线程ID
13 HANDLE m_hListenEventExit; // 侦听线程退出事件对象,用于退出侦听线程时通知子线程关闭
14 SYSTEM_INFO m_sysInfo; // 系统信息,其中包含处理器等信息
15 VClnt m_clntList; // 客户端清单
16 MNetDatagram m_netParaMap; // 接收信息列表
17
18 public:
19 MyTcpSrv()
20 {
21 InitializeCriticalSection(&m_csLock);
22 m_hIOCP = INVALID_HANDLE_VALUE;
23 m_sLinstenSocket = INVALID_SOCKET;
24 m_hListenThread = INVALID_HANDLE_VALUE;
25 m_hListenEventExit = INVALID_HANDLE_VALUE;
26 }
27 ~MyTcpSrv()
28 {
29 DeleteCriticalSection(&m_csLock);
30 }
31
32 private:
33 // 加载SOCK DLL
34 bool LoadSock();
35
36 // 设置本地终端
37 bool SetLocalAddr(WORD nPort, const char *szAddr);
38
39 // 初始化完成端口
40 bool InitialIOCP();
41
42 // 设置侦听套接字
43 bool SetupListenSocket();
44
45 // 投递接收请求
46 void DeliverRecv(LPSOCKET_INFORMATION lpInfo);
47
48 // 投递发送请求
49 void DeliverSend(LPSOCKET_INFORMATION lpInfo);
50
51 // 启动侦听线程
52 bool RunListenThread();
53
54 // 侦听例程
55 UINT static WINAPI TCPListenThreadProc(LPVOID pParam);
56
57 // 工作例程
58 UINT static WINAPI ServerWorkerThread(LPVOID CompletionPortID);
59
60 // 擦除记录
61 void Erase(LPSOCKET_INFORMATION lpSocketInfo);
62
63 // 解析获取的数据
64 void OnRecvData(LPSOCKET_INFORMATION lpSocketInfo);
65
66 // 停止侦听
67 void StopListening();
68
69 // 断开与指定客户端连接
70 void DisconnectUser(SOCKADDR_IN clientAddr);
71
72 // 断开与所有用户连接
73 void DisconnectAllUsers();
74
75 // 卸载SOCKDLL
76 void UnloadSock();
77
78 public:
79 // 创建通道
80 bool Create(LPTERMINAL_INFO pInfo);
81
82 // 关闭通道
83 void Close();
84
85 // 读取数据
86 int Read(byte byRecv[], UINT uMaxSize, LPTERMINAL_INFO pInfo);
87
88 // 发送数据
89 int Write(byte bySend[], UINT uAntiSize, LPTERMINAL_INFO pInfo);
90
91 // 重置服务器
92 bool Reset();
93
94 // 获取客户端列表信息
95 VClnt GetClinetList()
96 {
97 return m_clntList;
98 }
99 };
主体中成员方法的实现:
View Code
1 #include "TCPSrv.h"
2
3 // 加载SOCK DLL
4 bool MyTcpSrv::LoadSock()
5 {
6 WSADATA wsaData;
7 return WSAStartup(MAKEWORD(2, 2), &wsaData) == 0 ? true : false;
8 }
9
10 // 卸载SOCK DLL
11 void MyTcpSrv::UnloadSock()
12 {
13 WSACleanup();
14 }
15
16 // 设置本地终端
17 bool MyTcpSrv::SetLocalAddr(WORD nPort, const char *szAddr)
18 {
19 memset(&m_LocalAddr, 0, sizeof(SOCKADDR_IN));
20 m_LocalAddr.sin_family = AF_INET;
21 m_LocalAddr.sin_port = htons(nPort);
22 if(szAddr == NULL)
23 m_LocalAddr.sin_addr.s_addr = htonl(INADDR_ANY);
24 else
25 {
26 if((m_LocalAddr.sin_addr.s_addr = inet_addr(szAddr)) == INADDR_NONE)
27 {
28 HOSTENT *host = NULL;
29 host = gethostbyname(szAddr);
30 if(host == NULL)
31 return false;
32 CopyMemory(&m_LocalAddr.sin_addr, host->h_addr, host->h_length);
33 }
34 }
35 return true;
36 }
37
38 // 初始化完成端口
39 bool MyTcpSrv::InitialIOCP()
40 {
41 // 创建完成端口,其中第三个参数为零,表示完成端口最大同时支持CPU个线程
42 if ((m_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
43 return false;
44
45 // 获取系统信息,计算所需工作线程数,创建工作线程
46 SYSTEM_INFO sysInfo;
47 GetSystemInfo(&sysInfo);
48 UINT ThreadID;
49 for(USHORT i = 0; i < sysInfo.dwNumberOfProcessors * 2 + 2; i++)
50 {
51 HANDLE hThread;
52 if((hThread = (HANDLE)_beginthreadex(NULL, 0, ServerWorkerThread, this, 0, &ThreadID)) == NULL)
53 return false;//创建工作线程失败
54 CloseHandle(hThread);
55 }
56 return true;
57 }
58
59 // 设置侦听套接字
60 bool MyTcpSrv::SetupListenSocket()
61 {
62 // 创建侦听套接字
63 if ((m_sLinstenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
64 return false; // 创建侦听套接字失败
65
66 // 绑定端口
67 if (bind(m_sLinstenSocket, (PSOCKADDR) &m_LocalAddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
68 return false; // 绑定端口失败
69
70 // 进入侦听被动模式,准备接收
71 if (listen(m_sLinstenSocket, SOMAXCONN) == SOCKET_ERROR)
72 return false; // 侦听失败
73
74 return true;
75 }
76
77 // 启动侦听线程
78 bool MyTcpSrv::RunListenThread()
79 {
80 // 初始化信号
81 m_hListenEventExit = CreateEvent(NULL, TRUE, FALSE, NULL);
82 if((m_hListenThread = (HANDLE)_beginthreadex(NULL, 0, TCPListenThreadProc, this, 0, &m_nListenThreadId)) == NULL)
83 {
84 closesocket(m_sLinstenSocket);
85 return false; // 启动侦听线程失败
86 }
87 return true;
88 }
89
90 // 侦听执行体
91 UINT WINAPI MyTcpSrv::TCPListenThreadProc(LPVOID pParam)
92 {
93 // 接收套接字
94 TS_CLIENT_ELEMENT AcceptElement;
95 WSAEVENT wsaListenEvent;
96 OVERLAPPED ListenOverlapped;
97 CHAR szAcceptBuffer [2 * (sizeof(SOCKADDR_IN) + 16)];
98 DWORD dwBytesReceived;
99 LPSOCKADDR lpServerSockInfo;
100 LPSOCKADDR lpClientSockInfo;
101 int nServerAddrLen;
102 int nClientAddrLen;
103 LPSOCKET_INFORMATION lpSocketInfo;
104 MyTcpSrv* pthis;
105
106 pthis = (MyTcpSrv*)pParam;
107 wsaListenEvent = WSACreateEvent();
108
109 // 不断接收连接接入
110 while(true)
111 {
112 ZeroMemory(&AcceptElement, sizeof(TS_CLIENT_ELEMENT));
113 if ((AcceptElement.s_socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
114 return 0L;//创建套接字失败
115
116 ZeroMemory(&ListenOverlapped, sizeof(WSAOVERLAPPED));
117 WSAResetEvent(wsaListenEvent);
118 ListenOverlapped.hEvent = wsaListenEvent;
119
120 // 异步接收套接字连接
121 if (!AcceptEx(pthis->m_sLinstenSocket, AcceptElement.s_socket, (PVOID)szAcceptBuffer, 0, sizeof(SOCKADDR_IN)\
122 + 16, sizeof(SOCKADDR_IN) + 16, &dwBytesReceived, &ListenOverlapped))
123 {
124 if (WSAGetLastError() != ERROR_IO_PENDING)
125 return 0L;//接受套接字失败
126 }
127
128 // 等待AcceptEx返回
129 while(TRUE)
130 {
131 HANDLE hEvents[2];
132 hEvents[0] = ListenOverlapped.hEvent;
133 hEvents[1] = pthis->m_hListenEventExit;
134
135 DWORD dwEventIndex = WSAWaitForMultipleEvents(2, hEvents, FALSE, WSA_INFINITE, TRUE);
136 if (dwEventIndex == WSA_WAIT_FAILED)
137 return 0L; // 异常
138 if ((dwEventIndex-WAIT_OBJECT_0) == 1)
139 return 0L; // 接到退出线程通知
140
141 if (dwEventIndex != WAIT_IO_COMPLETION)
142 break;
143 }
144
145 // 获取客户端详细信息,并登记
146 GetAcceptExSockaddrs(szAcceptBuffer, 0, sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, \
147 &lpServerSockInfo, &nServerAddrLen, &lpClientSockInfo, &nClientAddrLen);
148 memcpy(&AcceptElement.s_clientInfo, lpClientSockInfo, sizeof(sockaddr_in));
149 time(&AcceptElement.s_tVisitTM);// 获取访问时间
150 AcceptElement.s_bFirstFrame = TRUE;
151 lpSocketInfo = new SOCKET_INFORMATION;// 创建通信载体
152 pthis->m_clntList.push_back(AcceptElement);
153 lpSocketInfo->s_lpClientElement = &(pthis->m_clntList.back());
154 lpSocketInfo->s_dataBuf.len = SOCKET_BUF_SIZE;
155 lpSocketInfo->s_dataBuf.buf = lpSocketInfo->s_buffer;
156 lpSocketInfo->s_nBufLen = 0;
157 lpSocketInfo->s_pThis = pthis;
158
159 // 将获取到的SOCKET绑定到完成端口
160 if (CreateIoCompletionPort((HANDLE)lpSocketInfo->s_lpClientElement->s_socket, lpSocketInfo->s_pThis->m_hIOCP, (DWORD)lpSocketInfo, 0) == NULL)
161 return 0L; // 绑定完成端口失败
162
163 // 递交异步接收请求
164 lpSocketInfo->s_pThis->DeliverRecv(lpSocketInfo);
165 }
166 return 1L;
167 }
168
169 // 递交异步接收请求
170 void MyTcpSrv::DeliverRecv(LPSOCKET_INFORMATION lpSocketInfo)
171 {
172 DWORD dwRecvBytes = 0;
173 DWORD dwFlags = 0;
174
175 // 整理通信载体
176 ZeroMemory(&(lpSocketInfo->s_overlapped), sizeof(WSAOVERLAPPED));
177 lpSocketInfo->s_dataBuf.len = SOCKET_BUF_SIZE;
178 lpSocketInfo->s_dataBuf.buf = lpSocketInfo->s_buffer;
179 lpSocketInfo->s_nBufLen = 0;
180 lpSocketInfo->s_pThis = this;
181 lpSocketInfo->s_uDeliverType = OVERLAPPED_DELIVER_RECV;
182
183 if (WSARecv(lpSocketInfo->s_lpClientElement->s_socket, &(lpSocketInfo->s_dataBuf), 1, &dwRecvBytes, \
184 &dwFlags, &(lpSocketInfo->s_overlapped), NULL) == SOCKET_ERROR)
185 {
186 // 异步投递失败
187 if (WSAGetLastError() != WSA_IO_PENDING)
188 Erase(lpSocketInfo);
189 }
190 }
191
192 // 递交异步发送请求
193 void MyTcpSrv::DeliverSend(LPSOCKET_INFORMATION lpSocketInfo)
194 {
195 DWORD dwSendBytes = 0;
196 DWORD dwFlags = 0;
197
198 // 已经全部发送完毕
199 if (lpSocketInfo->s_uAntiSendSize - lpSocketInfo->s_nBufLen <= 0)
200 {
201 delete lpSocketInfo;
202 return;
203 }
204
205 // 整理通信载体
206 ZeroMemory(&(lpSocketInfo->s_overlapped), sizeof(WSAOVERLAPPED));
207 lpSocketInfo->s_dataBuf.len = lpSocketInfo->s_uAntiSendSize - lpSocketInfo->s_nBufLen;
208 lpSocketInfo->s_dataBuf.buf = lpSocketInfo->s_buffer + lpSocketInfo->s_nBufLen;
209 lpSocketInfo->s_pThis = this;
210 lpSocketInfo->s_uDeliverType = OVERLAPPED_DELIVER_SEND; // 发送
211
212 // 异步发送请求
213 if(WSASend(lpSocketInfo->s_lpClientElement->s_socket, &lpSocketInfo->s_dataBuf, 1, &dwSendBytes, dwFlags, &lpSocketInfo->s_overlapped, NULL) == SOCKET_ERROR)
214 {
215 // 异步发送请求失败
216 if (WSAGetLastError() != WSA_IO_PENDING)
217 {
218 delete lpSocketInfo;// 释放通信载体
219 return;
220 }
221 }
222 }
223
224 // 擦除记录
225 void MyTcpSrv::Erase(LPSOCKET_INFORMATION lpSocketInfo)
226 {
227 // 关闭连接
228 closesocket(lpSocketInfo->s_lpClientElement->s_socket);
229
230 // 清除记录
231 EnterCriticalSection(&m_csLock);
232 VClnt::iterator ite;
233 for(ite=m_clntList.begin();ite != m_clntList.end();++ite)
234 {
235 if (ite->s_socket == lpSocketInfo->s_lpClientElement->s_socket)
236 {
237 m_clntList.erase(ite);
238 break;
239 }
240 }
241 LeaveCriticalSection(&m_csLock);
242
243
2
3 // 加载SOCK DLL
4 bool MyTcpSrv::LoadSock()
5 {
6 WSADATA wsaData;
7 return WSAStartup(MAKEWORD(2, 2), &wsaData) == 0 ? true : false;
8 }
9
10 // 卸载SOCK DLL
11 void MyTcpSrv::UnloadSock()
12 {
13 WSACleanup();
14 }
15
16 // 设置本地终端
17 bool MyTcpSrv::SetLocalAddr(WORD nPort, const char *szAddr)
18 {
19 memset(&m_LocalAddr, 0, sizeof(SOCKADDR_IN));
20 m_LocalAddr.sin_family = AF_INET;
21 m_LocalAddr.sin_port = htons(nPort);
22 if(szAddr == NULL)
23 m_LocalAddr.sin_addr.s_addr = htonl(INADDR_ANY);
24 else
25 {
26 if((m_LocalAddr.sin_addr.s_addr = inet_addr(szAddr)) == INADDR_NONE)
27 {
28 HOSTENT *host = NULL;
29 host = gethostbyname(szAddr);
30 if(host == NULL)
31 return false;
32 CopyMemory(&m_LocalAddr.sin_addr, host->h_addr, host->h_length);
33 }
34 }
35 return true;
36 }
37
38 // 初始化完成端口
39 bool MyTcpSrv::InitialIOCP()
40 {
41 // 创建完成端口,其中第三个参数为零,表示完成端口最大同时支持CPU个线程
42 if ((m_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
43 return false;
44
45 // 获取系统信息,计算所需工作线程数,创建工作线程
46 SYSTEM_INFO sysInfo;
47 GetSystemInfo(&sysInfo);
48 UINT ThreadID;
49 for(USHORT i = 0; i < sysInfo.dwNumberOfProcessors * 2 + 2; i++)
50 {
51 HANDLE hThread;
52 if((hThread = (HANDLE)_beginthreadex(NULL, 0, ServerWorkerThread, this, 0, &ThreadID)) == NULL)
53 return false;//创建工作线程失败
54 CloseHandle(hThread);
55 }
56 return true;
57 }
58
59 // 设置侦听套接字
60 bool MyTcpSrv::SetupListenSocket()
61 {
62 // 创建侦听套接字
63 if ((m_sLinstenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
64 return false; // 创建侦听套接字失败
65
66 // 绑定端口
67 if (bind(m_sLinstenSocket, (PSOCKADDR) &m_LocalAddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
68 return false; // 绑定端口失败
69
70 // 进入侦听被动模式,准备接收
71 if (listen(m_sLinstenSocket, SOMAXCONN) == SOCKET_ERROR)
72 return false; // 侦听失败
73
74 return true;
75 }
76
77 // 启动侦听线程
78 bool MyTcpSrv::RunListenThread()
79 {
80 // 初始化信号
81 m_hListenEventExit = CreateEvent(NULL, TRUE, FALSE, NULL);
82 if((m_hListenThread = (HANDLE)_beginthreadex(NULL, 0, TCPListenThreadProc, this, 0, &m_nListenThreadId)) == NULL)
83 {
84 closesocket(m_sLinstenSocket);
85 return false; // 启动侦听线程失败
86 }
87 return true;
88 }
89
90 // 侦听执行体
91 UINT WINAPI MyTcpSrv::TCPListenThreadProc(LPVOID pParam)
92 {
93 // 接收套接字
94 TS_CLIENT_ELEMENT AcceptElement;
95 WSAEVENT wsaListenEvent;
96 OVERLAPPED ListenOverlapped;
97 CHAR szAcceptBuffer [2 * (sizeof(SOCKADDR_IN) + 16)];
98 DWORD dwBytesReceived;
99 LPSOCKADDR lpServerSockInfo;
100 LPSOCKADDR lpClientSockInfo;
101 int nServerAddrLen;
102 int nClientAddrLen;
103 LPSOCKET_INFORMATION lpSocketInfo;
104 MyTcpSrv* pthis;
105
106 pthis = (MyTcpSrv*)pParam;
107 wsaListenEvent = WSACreateEvent();
108
109 // 不断接收连接接入
110 while(true)
111 {
112 ZeroMemory(&AcceptElement, sizeof(TS_CLIENT_ELEMENT));
113 if ((AcceptElement.s_socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
114 return 0L;//创建套接字失败
115
116 ZeroMemory(&ListenOverlapped, sizeof(WSAOVERLAPPED));
117 WSAResetEvent(wsaListenEvent);
118 ListenOverlapped.hEvent = wsaListenEvent;
119
120 // 异步接收套接字连接
121 if (!AcceptEx(pthis->m_sLinstenSocket, AcceptElement.s_socket, (PVOID)szAcceptBuffer, 0, sizeof(SOCKADDR_IN)\
122 + 16, sizeof(SOCKADDR_IN) + 16, &dwBytesReceived, &ListenOverlapped))
123 {
124 if (WSAGetLastError() != ERROR_IO_PENDING)
125 return 0L;//接受套接字失败
126 }
127
128 // 等待AcceptEx返回
129 while(TRUE)
130 {
131 HANDLE hEvents[2];
132 hEvents[0] = ListenOverlapped.hEvent;
133 hEvents[1] = pthis->m_hListenEventExit;
134
135 DWORD dwEventIndex = WSAWaitForMultipleEvents(2, hEvents, FALSE, WSA_INFINITE, TRUE);
136 if (dwEventIndex == WSA_WAIT_FAILED)
137 return 0L; // 异常
138 if ((dwEventIndex-WAIT_OBJECT_0) == 1)
139 return 0L; // 接到退出线程通知
140
141 if (dwEventIndex != WAIT_IO_COMPLETION)
142 break;
143 }
144
145 // 获取客户端详细信息,并登记
146 GetAcceptExSockaddrs(szAcceptBuffer, 0, sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, \
147 &lpServerSockInfo, &nServerAddrLen, &lpClientSockInfo, &nClientAddrLen);
148 memcpy(&AcceptElement.s_clientInfo, lpClientSockInfo, sizeof(sockaddr_in));
149 time(&AcceptElement.s_tVisitTM);// 获取访问时间
150 AcceptElement.s_bFirstFrame = TRUE;
151 lpSocketInfo = new SOCKET_INFORMATION;// 创建通信载体
152 pthis->m_clntList.push_back(AcceptElement);
153 lpSocketInfo->s_lpClientElement = &(pthis->m_clntList.back());
154 lpSocketInfo->s_dataBuf.len = SOCKET_BUF_SIZE;
155 lpSocketInfo->s_dataBuf.buf = lpSocketInfo->s_buffer;
156 lpSocketInfo->s_nBufLen = 0;
157 lpSocketInfo->s_pThis = pthis;
158
159 // 将获取到的SOCKET绑定到完成端口
160 if (CreateIoCompletionPort((HANDLE)lpSocketInfo->s_lpClientElement->s_socket, lpSocketInfo->s_pThis->m_hIOCP, (DWORD)lpSocketInfo, 0) == NULL)
161 return 0L; // 绑定完成端口失败
162
163 // 递交异步接收请求
164 lpSocketInfo->s_pThis->DeliverRecv(lpSocketInfo);
165 }
166 return 1L;
167 }
168
169 // 递交异步接收请求
170 void MyTcpSrv::DeliverRecv(LPSOCKET_INFORMATION lpSocketInfo)
171 {
172 DWORD dwRecvBytes = 0;
173 DWORD dwFlags = 0;
174
175 // 整理通信载体
176 ZeroMemory(&(lpSocketInfo->s_overlapped), sizeof(WSAOVERLAPPED));
177 lpSocketInfo->s_dataBuf.len = SOCKET_BUF_SIZE;
178 lpSocketInfo->s_dataBuf.buf = lpSocketInfo->s_buffer;
179 lpSocketInfo->s_nBufLen = 0;
180 lpSocketInfo->s_pThis = this;
181 lpSocketInfo->s_uDeliverType = OVERLAPPED_DELIVER_RECV;
182
183 if (WSARecv(lpSocketInfo->s_lpClientElement->s_socket, &(lpSocketInfo->s_dataBuf), 1, &dwRecvBytes, \
184 &dwFlags, &(lpSocketInfo->s_overlapped), NULL) == SOCKET_ERROR)
185 {
186 // 异步投递失败
187 if (WSAGetLastError() != WSA_IO_PENDING)
188 Erase(lpSocketInfo);
189 }
190 }
191
192 // 递交异步发送请求
193 void MyTcpSrv::DeliverSend(LPSOCKET_INFORMATION lpSocketInfo)
194 {
195 DWORD dwSendBytes = 0;
196 DWORD dwFlags = 0;
197
198 // 已经全部发送完毕
199 if (lpSocketInfo->s_uAntiSendSize - lpSocketInfo->s_nBufLen <= 0)
200 {
201 delete lpSocketInfo;
202 return;
203 }
204
205 // 整理通信载体
206 ZeroMemory(&(lpSocketInfo->s_overlapped), sizeof(WSAOVERLAPPED));
207 lpSocketInfo->s_dataBuf.len = lpSocketInfo->s_uAntiSendSize - lpSocketInfo->s_nBufLen;
208 lpSocketInfo->s_dataBuf.buf = lpSocketInfo->s_buffer + lpSocketInfo->s_nBufLen;
209 lpSocketInfo->s_pThis = this;
210 lpSocketInfo->s_uDeliverType = OVERLAPPED_DELIVER_SEND; // 发送
211
212 // 异步发送请求
213 if(WSASend(lpSocketInfo->s_lpClientElement->s_socket, &lpSocketInfo->s_dataBuf, 1, &dwSendBytes, dwFlags, &lpSocketInfo->s_overlapped, NULL) == SOCKET_ERROR)
214 {
215 // 异步发送请求失败
216 if (WSAGetLastError() != WSA_IO_PENDING)
217 {
218 delete lpSocketInfo;// 释放通信载体
219 return;
220 }
221 }
222 }
223
224 // 擦除记录
225 void MyTcpSrv::Erase(LPSOCKET_INFORMATION lpSocketInfo)
226 {
227 // 关闭连接
228 closesocket(lpSocketInfo->s_lpClientElement->s_socket);
229
230 // 清除记录
231 EnterCriticalSection(&m_csLock);
232 VClnt::iterator ite;
233 for(ite=m_clntList.begin();ite != m_clntList.end();++ite)
234 {
235 if (ite->s_socket == lpSocketInfo->s_lpClientElement->s_socket)
236 {
237 m_clntList.erase(ite);
238 break;
239 }
240 }
241 LeaveCriticalSection(&m_csLock);
242
243