一、实验目的
二、实验环境
三、实验内容
- 1.Select I/O服务器
- 2.WSAEventSelect服务器
- 3.I/O Completion Port
一、实验目的
- 掌握WinSock API I/O模型框架与原理
- 掌握WinSock API Select I/O模型;
- 掌握WinSock API WSAEventSelect I/O模型;
- 掌握WinSock API I/O Completion Port模型。
二、实验环境
- *** 作系统:WINDOWS 7及以上
- 开发工具:Microsoft VisualBasic6.0
- 实验设备:PC
三、实验内容
- 基于Select I/O模型创建一个回送服务器,服务器在5150端口上侦听TCP连接,并将收到的客户机数据回送客户机。
- 设计一个WSAEventSelect I/O模型,服务器在端口5150侦听TCP连接,回送到客户机数据。
- 开发一个简单的WinSock回声服务器,应用程序监听端口5150的TCP连接,并将收到的客户机数据回送给客户机。
客户机:
//程序说明:
// 这是一个回送客户机程序。
连接TCP server,发送数据,接收服务器回送的数据
// 命令行参数:
// client [-p:x] [-s:IP] [-n:x] [-o]
// -p:x 远程服务器端口
// -s:IP 服务器地址或主机名
// -n:x 消息的发送次数
// -o 只发送,不接收
#include "stdafx.h"
#include
#include
#include
#pragma comment(lib,"ws2_32.lib")
#define DEFAULT_COUNT 20
#define DEFAULT_PORT 5050
#define DEFAULT_BUFFER 2048
#define DEFAULT_MESSAGE "\'A test message from client\'"
char szServer[128], // 服务器主机名或地址
szMessage[1024]; // 发送到服务器的消息缓冲区
int iPort = DEFAULT_PORT; // 服务器端口
DWORD dwCount = DEFAULT_COUNT; // 发送消息的次数
BOOL bSendOnly = FALSE; // 只发送,不接收
// 函数用法说明
void usage()
{
printf("TcpClient: client [-p:x] [-s:IP] [-n:x] [-o]\n\n");
printf(" -p:x Remote port to send to\n");
printf(" -s:IP Server's IP address or hostname\n");
printf(" -n:x Number of times to send message\n");
printf(" -o Send messages only; don't receive\n");
printf("\n");
}
// 命令行参数解析
void ValidateArgs(int argc, char **argv)
{
int i;
for (i = 1; i < argc; i++)
{
if ((argv[i][0] == '-') || (argv[i][0] == '/'))
{
switch (tolower(argv[i][1]))
{
case 'p':
if (strlen(argv[i]) > 3)
iPort = atoi(&argv[i][3]);
break;
case 's':
if (strlen(argv[i]) > 3)
strcpy_s(szServer, sizeof(szServer), &argv[i][3]);
break;
case 'n':
if (strlen(argv[i]) > 3)
dwCount = atol(&argv[i][3]);
break;
case 'o':
bSendOnly = TRUE;
break;
default:
usage();
break;
}
}
}
}
// 主线程初始化 Winsock, 分析命令行参数, 创建套接字, 连接服务器,发送和接受数据
int main(int argc, char **argv)
{
WSADATA wsd;
SOCKET sClient;
char szBuffer[DEFAULT_BUFFER];
int ret,
i;
struct sockaddr_in server;
struct hostent *host = NULL;
if (argc < 2)
{
usage();
exit(1);
}
// 分析命令行参数,加载Winsock
ValidateArgs(argc, argv);
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
printf("Failed to load Winsock library! Error %d\n", WSAGetLastError());
return 1;
}
else
printf("Winsock library loaded successfully!\n");
strcpy_s(szMessage, sizeof(szMessage), DEFAULT_MESSAGE);
// 创建套接字,连接服务器
sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sClient == INVALID_SOCKET)
{
printf("socket() failed with error code %d\n", WSAGetLastError());
return 1;
}
else
printf("socket() looks fine!\n");
server.sin_family = AF_INET;
server.sin_port = htons(iPort);
server.sin_addr.s_addr = inet_addr(szServer);
if (server.sin_addr.s_addr == INADDR_NONE)
{
host = gethostbyname(szServer);
if (host == NULL)
{
printf("Unable to resolve server %s\n", szServer);
return 1;
}
else
printf("The hostname resolved successfully!\n");
CopyMemory(&server.sin_addr, host->h_addr_list[0], host->h_length);
}
if (connect(sClient, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
{
printf("connect() failed with error code %d\n", WSAGetLastError());
return 1;
}
else
printf("connect() is pretty damn fine!\n");
// 发送和接收数据
printf("Sending and receiving data if any...\n");
for (i = 0; i < (int)dwCount; i++)
{
ret = send(sClient, szMessage, strlen(szMessage), 0);
if (ret == 0)
break;
else if (ret == SOCKET_ERROR)
{
printf("send() failed with error code %d\n", WSAGetLastError());
break;
}
printf("send() should be fine. Send %d bytes\n", ret);
if (!bSendOnly)
{
ret = recv(sClient, szBuffer, DEFAULT_BUFFER, 0);
if (ret == 0) // 正常关闭
{
printf("It is a graceful close!\n");
break;
}
else if (ret == SOCKET_ERROR)
{
printf("recv() failed with error code %d\n", WSAGetLastError());
break;
}
szBuffer[ret] = ';'printf
("recv() is OK. Received %d bytes: %s\n",, ret) szBuffer;}
}
if
( closesocket()sClient== 0 )printf
("closesocket() is OK!\n");else
printf
("closesocket() failed with error code %d\n",WSAGetLastError ());if
( WSACleanup()== 0 )printf
("WSACleanup() is fine!\n");else
printf
("WSACleanup() failed with error code %d\n",WSAGetLastError ());return
0 ;}
// 程序说明:
1.Select I/O服务器
//
// 服务器在5150端口上侦听TCP连接
// 本程序演示如何基于Winsock 的 select() API I/O 模型创建一个回送服务器。
// 将收到的客户机数据再回送客户机,直到可会关闭连接
#
include#
include#
include#
pragmacomment (,lib"ws2_32.lib")#
definePORT 5150 #
defineDATA_BUFSIZE 8192 typedef
struct _SOCKET_INFORMATION [ {
CHAR Buffer]DATA_BUFSIZE;;
WSABUF DataBuf;
SOCKET Socket;
OVERLAPPED Overlapped;
DWORD BytesSEND;
DWORD BytesRECV}
, SOCKET_INFORMATION* ;LPSOCKET_INFORMATION// 原型
CreateSocketInformation
BOOL ()SOCKET s;void
FreeSocketInformation ()DWORD Index;// 全局变量
=
DWORD TotalSockets 0 ;[
LPSOCKET_INFORMATION SocketArray]FD_SETSIZE;int
main (int, argcchar * *)argv;
{
SOCKET ListenSocket;
SOCKET AcceptSocket;
SOCKADDR_IN InternetAddr;
WSADATA wsaData;
INT Ret;
FD_SET WriteSet;
FD_SET ReadSet;
DWORD i;
DWORD Total;
ULONG NonBlock;
DWORD Flags;
DWORD SendBytes;
DWORD RecvBytesif
( (=Ret WSAStartup (0x0202,& )wsaData)!= 0 )printf
{
("WSAStartup() failed with error %d\n",) Ret;WSACleanup
();return
1 ;}
else
printf
("WSAStartup() is fine!\n");// 创建用于侦听的套接字
if
( (=ListenSocket WSASocket (,AF_INET, SOCK_STREAM0 ,NULL ,0 ,)
WSA_FLAG_OVERLAPPED)== ) INVALID_SOCKETprintf
{
("WSASocket() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("WSASocket() is OK!\n");.
InternetAddr=sin_family ; AF_INET.
InternetAddr.sin_addr=s_addr htonl ()INADDR_ANY;.
InternetAddr=sin_port htons ()PORT;if
( bind(,ListenSocket( )PSOCKADDR&,InternetAddrsizeof ()InternetAddr)== ) SOCKET_ERRORprintf
{
("bind() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("bind() is OK!\n");if
( listen(,ListenSocket5 ))printf
{
("listen() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("listen() is OK!\n");// 将侦听套接字的阻塞模式转为非阻塞模式,这样服务器在等待连接到达期间不会发生阻塞
=
NonBlock 1 ;if
( ioctlsocket(,ListenSocket, FIONBIO& )NonBlock== ) SOCKET_ERRORprintf
{
("ioctlsocket() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("ioctlsocket() is OK!\n");while
( )TRUE// 初始化等待网络I/O事件通知的读写套接字集合
{
FD_ZERO
(&)ReadSet;FD_ZERO
(&)WriteSet;// 将侦听套接字加入套接字读集合
FD_SET
(,ListenSocket& )ReadSet;// 基于当前状态缓冲区为每个套接字设置读/写
// 如果缓冲区中有数据将其写入集合,否则读集
for
( =i 0 ;< i ; TotalSockets++ i)if
( [SocketArray]i[->BytesRECV > SocketArray]i)->BytesSENDFD_SET
([SocketArray]i,->Socket& )WriteSet;else
FD_SET
([SocketArray]i,->Socket& )ReadSet;if
( (=Total select (0,& ,ReadSet& ,WriteSetNULL ,NULL ))== ) SOCKET_ERRORprintf
{
("select() returned with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("select() is OK!\n");// 检查到达的连接在侦听套接字
if
( FD_ISSET(,ListenSocket& )ReadSet)--
{
Total;if
( (=AcceptSocket accept (,ListenSocketNULL ,NULL ))!= ) INVALID_SOCKET// 设置套接字AcceptSocket为非阻塞模式
{
// 这样服务器在调用WSASends发送数据时就不会被阻塞
=
NonBlock 1 ;if
( ioctlsocket(,AcceptSocket, FIONBIO& )NonBlock== ) SOCKET_ERRORprintf
{
("ioctlsocket(FIONBIO) failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("ioctlsocket(FIONBIO) is OK!\n");if
( CreateSocketInformation()AcceptSocket== ) FALSEprintf
{
("CreateSocketInformation(AcceptSocket) failed!\n");return
1 ;}
else
printf
("CreateSocketInformation() is OK!\n");}
else
if
{
( WSAGetLastError()!= ) WSAEWOULDBLOCKprintf
{
("accept() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("accept() is fine!\n");}
}
// 依次处理所有的套接字,SocketInfo为当前要处理的套接字信息
for
( =i 0 ;0 Total > && < i ; TotalSockets++ i)=
{
LPSOCKET_INFORMATION SocketInfo [ SocketArray]i;// 判断当前套接字的可读性,即是否有接入的连接请求或者可以接受数据
if
( FD_ISSET(,SocketInfo->Socket& )ReadSet)--
{
Total;.
SocketInfo->DataBuf=buf ; SocketInfo->Buffer.
SocketInfo->DataBuf=len ; DATA_BUFSIZE=
Flags 0 ;if
( WSARecv(,SocketInfo->Socket& ()SocketInfo->DataBuf,1 ,& ,RecvBytes&
,FlagsNULL ,NULL )== ) SOCKET_ERRORif
{
( WSAGetLastError()!= ) WSAEWOULDBLOCKprintf
{
("WSARecv() failed with error %d\n",WSAGetLastError ());FreeSocketInformation
()i;}
else
printf
("WSARecv() is OK!\n");continue
;}
else
=
{
SocketInfo->BytesRECV ; RecvBytes// 如果接收到0个字节,则表示对方关闭连接
if
( ==RecvBytes 0 )FreeSocketInformation
{
()i;continue
;}
}
}
// 如果当前套接字在WriteSet集合中
// 则表明
if
( FD_ISSET(,SocketInfo->Socket& )WriteSet)--
{
Total;.
SocketInfo->DataBuf=buf + SocketInfo->Buffer ; SocketInfo->BytesSEND.
SocketInfo->DataBuf=len - SocketInfo->BytesRECV ; SocketInfo->BytesSENDif
( WSASend(,SocketInfo->Socket& ()SocketInfo->DataBuf,1 ,& ,SendBytes0 ,NULL
,NULL )== ) SOCKET_ERRORif
{
( WSAGetLastError()!= ) WSAEWOULDBLOCKprintf
{
("WSASend() failed with error %d\n",WSAGetLastError ());FreeSocketInformation
()i;}
else
printf
("WSASend() is OK!\n");continue
;}
else
+=
{
SocketInfo->BytesSEND ; SendBytesif
( ==SocketInfo->BytesSEND ) SocketInfo->BytesRECV=
{
SocketInfo->BytesSEND 0 ;=
SocketInfo->BytesRECV 0 ;}
}
}
}
}
}
CreateSocketInformation
BOOL ()SOCKET s;
{
LPSOCKET_INFORMATION SIprintf
("Accepted socket number %d\n",) s;if
( (=SI ( )LPSOCKET_INFORMATIONGlobalAlloc(,GPTRsizeof ()SOCKET_INFORMATION))== NULL )printf
{
("GlobalAlloc() failed with error %d\n",GetLastError ());return
; FALSE}
else
printf
("GlobalAlloc() for SOCKET_INFORMATION is OK!\n");// Prepare SocketInfo structure for use
=
SI->Socket ; s=
SI->BytesSEND 0 ;=
SI->BytesRECV 0 ;[
SocketArray]TotalSockets= ; SI++
TotalSockets;return
()TRUE;}
void
FreeSocketInformation ()DWORD Index=
{
LPSOCKET_INFORMATION SI [ SocketArray]Index;;
DWORD iclosesocket
()SI->Socket;printf
("Closing socket number %d\n",) SI->Socket;GlobalFree
()SI;// Squash the socket array
for
( =i ; Index< i ; TotalSockets++ i)[
{
SocketArray]i= [ SocketArray+i 1 ];}
--
TotalSockets;}
// WSAEventSelect模型回送服务器
运行结果:
服务器:
客户机:
// WSAEventSelect.cpp
#
include#
include#
include#
pragmacomment (,lib"ws2_32.lib")#
definePORT 5150 #
defineDATA_BUFSIZE 8192 typedef
struct _SOCKET_INFORMATION [ {
CHAR Buffer]DATA_BUFSIZE;;
WSABUF DataBuf;
SOCKET Socket;
DWORD BytesSEND;
DWORD BytesRECV}
, SOCKET_INFORMATION* ;LPSOCKET_INFORMATIONCreateSocketInformation
BOOL ()SOCKET s;void
FreeSocketInformation ()DWORD Event;=
DWORD EventTotal 0 ;[
WSAEVENT EventArray]WSA_MAXIMUM_WAIT_EVENTS;[
LPSOCKET_INFORMATION SocketArray]WSA_MAXIMUM_WAIT_EVENTS;int
main (int, argcchar * *)argv;
{
SOCKET Listen;
SOCKET Accept;
SOCKADDR_IN InternetAddr;
LPSOCKET_INFORMATION SocketInfo;
DWORD Event;
WSANETWORKEVENTS NetworkEvents;
WSADATA wsaData;
DWORD Flags;
DWORD RecvBytes;
DWORD SendBytesif
( WSAStartup(0x0202,& )wsaData!= 0 )printf
{
("WSAStartup() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("WSAStartup() is OK!\n");if
( (=Listen socket (,AF_INET, SOCK_STREAM0 ))== ) INVALID_SOCKETprintf
{
("socket() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("socket() is OK!\n");if
( CreateSocketInformation()Listen== ) FALSEprintf
("CreateSocketInformation() failed!\n");else
printf
("CreateSocketInformation() is OK lol!\n");if
( WSAEventSelect(,Listen[ EventArray-EventTotal 1 ],| FD_ACCEPT ) FD_CLOSE== ) SOCKET_ERRORprintf
{
("WSAEventSelect() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("WSAEventSelect() is pretty fine!\n");.
InternetAddr=sin_family ; AF_INET.
InternetAddr.sin_addr=s_addr htonl ()INADDR_ANY;.
InternetAddr=sin_port htons ()PORT;if
( bind(,Listen( )PSOCKADDR&,InternetAddrsizeof ()InternetAddr)== ) SOCKET_ERRORprintf
{
("bind() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("bind() is OK!\n");if
( listen(,Listen5 ))printf
{
("listen() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("listen() is OK!\n");while
( )TRUE// 等待套接字接受I/O通知
{
if
( (=Event WSAWaitForMultipleEvents (,EventTotal, EventArray, FALSE, WSA_INFINITE) FALSE)== ) WSA_WAIT_FAILEDprintf
{
("WSAWaitForMultipleEvents() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("WSAWaitForMultipleEvents() is pretty damn OK!\n");if
( WSAEnumNetworkEvents([SocketArray-Event ] WSA_WAIT_EVENT_0,->Socket[
EventArray-Event ] WSA_WAIT_EVENT_0,& )NetworkEvents== ) SOCKET_ERRORprintf
{
("WSAEnumNetworkEvents() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("WSAEnumNetworkEvents() should be fine!\n");if
( .NetworkEvents&lNetworkEvents ) FD_ACCEPTif
{
( .NetworkEvents[iErrorCode]FD_ACCEPT_BIT!= 0 )printf
{
("FD_ACCEPT failed with error %d\n",. NetworkEvents[iErrorCode]FD_ACCEPT_BIT);break
;}
if
( (=Accept accept ([SocketArray-Event ] WSA_WAIT_EVENT_0,->SocketNULL ,NULL ))== ) INVALID_SOCKETprintf
{
("accept() failed with error %d\n",WSAGetLastError ());break
;}
else
printf
("accept() should be OK!\n");if
( )EventTotal > WSA_MAXIMUM_WAIT_EVENTSprintf
{
("Too many connections - closing socket...\n");closesocket
()Accept;break
;}
CreateSocketInformation
()Accept;if
( WSAEventSelect(,Accept[ EventArray-EventTotal 1 ],| FD_READ | FD_WRITE ) FD_CLOSE== ) SOCKET_ERRORprintf
{
("WSAEventSelect() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("WSAEventSelect() is OK!\n");printf
("Socket %d got connected...\n",) Accept;}
// 如果读取和写入事件发生,试着对数据缓冲区读取和写入数据
if
( .NetworkEvents&lNetworkEvents || FD_READ . NetworkEvents&lNetworkEvents ) FD_WRITEif
{
( .NetworkEvents&lNetworkEvents && FD_READ . NetworkEvents[iErrorCode]FD_READ_BIT!= 0 )printf
{
("FD_READ failed with error %d\n",. NetworkEvents[iErrorCode]FD_READ_BIT);break
;}
else
printf
("FD_READ is OK!\n");if
( .NetworkEvents&lNetworkEvents && FD_WRITE . NetworkEvents[iErrorCode]FD_WRITE_BIT!= 0 )printf
{
("FD_WRITE failed with error %d\n",. NetworkEvents[iErrorCode]FD_WRITE_BIT);break
;}
else
printf
("FD_WRITE is OK!\n");=
SocketInfo [ SocketArray-Event ] WSA_WAIT_EVENT_0;// 当收到的字节数为0时读取数据
if
( ==SocketInfo->BytesRECV 0 ).
{
SocketInfo->DataBuf=buf ; SocketInfo->Buffer.
SocketInfo->DataBuf=len ; DATA_BUFSIZE=
Flags 0 ;if
( WSARecv(,SocketInfo->Socket& ()SocketInfo->DataBuf,1 ,& ,RecvBytes&
,FlagsNULL ,NULL )== ) SOCKET_ERRORif
{
( WSAGetLastError()!= ) WSAEWOULDBLOCKprintf
{
("WSARecv() failed with error %d\n",WSAGetLastError ());FreeSocketInformation
(-Event ) WSA_WAIT_EVENT_0;return
1 ;}
}
else
printf
{
("WSARecv() is working!\n");=
SocketInfo->BytesRECV ; RecvBytes}
}
// 当收到的字节数大于发送的字节数时发送数据
if
( )SocketInfo->BytesRECV > SocketInfo->BytesSEND.
{
SocketInfo->DataBuf=buf + SocketInfo->Buffer ; SocketInfo->BytesSEND.
SocketInfo->DataBuf=len - SocketInfo->BytesRECV ; SocketInfo->BytesSENDif
( WSASend(,SocketInfo->Socket& ()SocketInfo->DataBuf,1 ,& ,SendBytes0 ,NULL
,NULL )== ) SOCKET_ERRORif
{
( WSAGetLastError()!= ) WSAEWOULDBLOCKprintf
{
("WSASend() failed with error %d\n",WSAGetLastError ());FreeSocketInformation
(-Event ) WSA_WAIT_EVENT_0;return
1 ;}
}
else
printf
{
("WSASend() is fine! Thank you...\n");+=
SocketInfo->BytesSEND ; SendBytesif
( ==SocketInfo->BytesSEND ) SocketInfo->BytesRECV=
{
SocketInfo->BytesSEND 0 ;=
SocketInfo->BytesRECV 0 ;}
}
}
}
if
( .NetworkEvents&lNetworkEvents ) FD_CLOSEif
{
( .NetworkEvents[iErrorCode]FD_CLOSE_BIT!= 0 )printf
{
("FD_CLOSE failed with error %d\n",. NetworkEvents[iErrorCode]FD_CLOSE_BIT);break
;}
else
printf
("FD_CLOSE is OK!\n");printf
("Closing socket information %d\n",[ SocketArray-Event ] WSA_WAIT_EVENT_0)->Socket;FreeSocketInformation
(-Event ) WSA_WAIT_EVENT_0;}
}
return
0 ;}
CreateSocketInformation
BOOL ()SOCKET s;
{
LPSOCKET_INFORMATION SIif
( ([EventArray]EventTotal= WSACreateEvent ())== ) WSA_INVALID_EVENTprintf
{
("WSACreateEvent() failed with error %d\n",WSAGetLastError ());return
; FALSE}
else
printf
("WSACreateEvent() is OK!\n");if
( (=SI ( )LPSOCKET_INFORMATIONGlobalAlloc(,GPTRsizeof ()SOCKET_INFORMATION))== NULL )printf
{
("GlobalAlloc() failed with error %d\n",GetLastError ());return
; FALSE}
else
printf
("GlobalAlloc() for LPSOCKET_INFORMATION is OK!\n");// 准备使用SocketInfo结构
=
SI->Socket ; s=
SI->BytesSEND 0 ;=
SI->BytesRECV 0 ;[
SocketArray]EventTotal= ; SI++
EventTotal;return
()TRUE;}
void
FreeSocketInformation ()DWORD Event=
{
LPSOCKET_INFORMATION SI [ SocketArray]Event;;
DWORD iclosesocket
()SI->Socket;GlobalFree
()SI;if
( WSACloseEvent([EventArray]Event)== ) TRUEprintf
("WSACloseEvent() is OK!\n\n");else
printf
("WSACloseEvent() failed miserabily!\n\n");// 将套接字和事件从数组删除
for
( =i ; Event< i ; EventTotal++ i)[
{
EventArray]i= [ EventArray+i 1 ];[
SocketArray]i= [ SocketArray+i 1 ];}
--
EventTotal;}
// I/O完成端口服务器程序
运行结果:
服务器:
客户机:
//程序名:IOComplete.cpp
#
include#
include#
include#
pragmacomment (,lib"ws2_32.lib")#
definePORT 5150 #
defineDATA_B 8192 UFSIZE // 类型定义
typedef
struct ;
{
OVERLAPPED Overlapped;
WSABUF DataBuf[
CHAR Buffer]DATA_BUFSIZE;;
DWORD BytesSEND;
DWORD BytesRECV}
, PER_IO_OPERATION_DATA* ;LPPER_IO_OPERATION_DATA// 结构定义
typedef
struct ;
{
SOCKET Socket}
, PER_HANDLE_DATA* ;LPPER_HANDLE_DATA// 原型声明
ServerWorkerThread
DWORD WINAPI ()LPVOID CompletionPortID;int
main (int, argcchar * *)argv;
{
SOCKADDR_IN InternetAddr;
SOCKET Listen;
HANDLE ThreadHandle;
SOCKET Accept;
HANDLE CompletionPort;
SYSTEM_INFO SystemInfo;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoDataint
; i;
DWORD RecvBytes;
DWORD Flags;
DWORD ThreadID;
WSADATA wsaData;
DWORD Retif
( (=Ret WSAStartup ((2,2 ),& )wsaData)!= 0 )printf
{
("WSAStartup() failed with error %d\n",) Ret;return
1 ;}
else
printf
("WSAStartup() is OK!\n");// 设置一个I/O完成端口
if
( (=CompletionPort CreateIoCompletionPort (,INVALID_HANDLE_VALUENULL ,0 ,0 ))== NULL )printf
{
("CreateIoCompletionPort() failed with error %d\n",GetLastError ());return
1 ;}
else
printf
("CreateIoCompletionPort() is damn OK!\n");// 测试系统中有多少CPU处理器
GetSystemInfo
(&)SystemInfo;// 基于系统可用的处理器创建工作线程
// 为每个处理器创建两个线程
for
( =i 0 ;< i ( int).SystemInfo*dwNumberOfProcessors 2 ;++ i)// 创建一个服务器工作线程,并且传递一个完成端口给这个线程
{
if
( (=ThreadHandle CreateThread (NULL,0 ,, ServerWorkerThread, CompletionPort0 ,& )ThreadID)== NULL )printf
{
("CreateThread() failed with error %d\n",GetLastError ());return
1 ;}
else
printf
("CreateThread() is OK!\n");// 关闭线程句柄
CloseHandle
()ThreadHandle;}
// 创建服务器监听套接字
if
( (=Listen WSASocket (,AF_INET, SOCK_STREAM0 ,NULL ,0 ,) WSA_FLAG_OVERLAPPED)== ) INVALID_SOCKETprintf
{
("WSASocket() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("WSASocket() is OK!\n");.
InternetAddr=sin_family ; AF_INET.
InternetAddr.sin_addr=s_addr htonl ()INADDR_ANY;.
InternetAddr=sin_port htons ()PORT;if
( bind(,Listen( )PSOCKADDR&,InternetAddrsizeof ()InternetAddr)== ) SOCKET_ERRORprintf
{
("bind() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("bind() is fine!\n");// 开始监听
if
( listen(,Listen5 )== ) SOCKET_ERRORprintf
{
("listen() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("listen() is working...\n");// 接受连接并且交给完成端口处理
while
( )TRUEif
{
( (=Accept WSAAccept (,ListenNULL ,NULL ,NULL ,0 ))== ) SOCKET_ERRORprintf
{
("WSAAccept() failed with error %d\n",WSAGetLastError ());return
1 ;}
else
printf
("WSAAccept() looks fine!\n");// 为套接字分配内存
if
( (=PerHandleData ( )LPPER_HANDLE_DATAGlobalAlloc(,GPTRsizeof ()PER_HANDLE_DATA))== NULL )printf
{
("GlobalAlloc() failed with error %d\n",GetLastError ());return
1 ;}
else
printf
("GlobalAlloc() for LPPER_HANDLE_DATA is OK!\n");// 将套接字与完成端口关联起来
printf
("Socket number %d got connected...\n",) Accept;=
PerHandleData->Socket ; Acceptif
( CreateIoCompletionPort(()HANDLE,Accept, CompletionPort( )DWORD,PerHandleData0 )== NULL )printf
{
("CreateIoCompletionPort() failed with error %d\n",GetLastError ());return
1 ;}
else
printf
("CreateIoCompletionPort() is OK!\n");// 创建一个I/O套接字信息结构体,为下面调用的WSARecv函数服务
if
( (=PerIoData ( )LPPER_IO_OPERATION_DATAGlobalAlloc(,GPTRsizeof ()PER_IO_OPERATION_DATA))== NULL )printf
{
("GlobalAlloc() failed with error %d\n",GetLastError ());return
1 ;}
else
printf
("GlobalAlloc() for LPPER_IO_OPERATION_DATA is OK!\n");ZeroMemory
(&()PerIoData->Overlapped,sizeof ()OVERLAPPED);=
PerIoData->BytesSEND 0 ;=
PerIoData->BytesRECV 0 ;.
PerIoData->DataBuf=len ; DATA_BUFSIZE.
PerIoData->DataBuf=buf ; PerIoData->Buffer=
Flags 0 ;if
( WSARecv(,Accept& ()PerIoData->DataBuf,1 ,& ,RecvBytes& ,Flags& ()PerIoData->Overlapped,NULL )== ) SOCKET_ERRORif
{
( WSAGetLastError()!= ) ERROR_IO_PENDINGprintf
{
("WSARecv() failed with error %d\n",WSAGetLastError ());return
1 ;}
}
else
printf
("WSARecv() is OK!\n");}
//end main}
ServerWorkerThread
DWORD WINAPI ()LPVOID CompletionPortID=
{
HANDLE CompletionPort ( )HANDLE;CompletionPortID;
DWORD BytesTransferred;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData,
DWORD SendBytes; RecvBytes;
DWORD Flagswhile
( )TRUEif
{
( GetQueuedCompletionStatus(,CompletionPort& ,BytesTransferred(
)LPDWORD&,PerHandleData( *LPOVERLAPPED )&,PerIoData) INFINITE== 0 )printf
{
("GetQueuedCompletionStatus() failed with error %d\n",GetLastError ());return
0 ;}
else
printf
("GetQueuedCompletionStatus() is OK!\n");// 检查套接字是否发生了错误,如果发生了错误,关闭套接字并释放相关内存
if
( ==BytesTransferred 0 )printf
{
("Closing socket %d\n",) PerHandleData->Socket;if
( closesocket()PerHandleData->Socket== ) SOCKET_ERRORprintf
{
("closesocket() failed with error %d\n",WSAGetLastError ());return
0 ;}
else
printf
("closesocket() is fine!\n");GlobalFree
()PerHandleData;GlobalFree
()PerIoData;continue
;}
// 如果ByteRECV字段等于0,表示一个WSARecv调用刚刚完成
if
( ==PerIoData->BytesRECV 0 )=
{
PerIoData->BytesRECV ; BytesTransferred=
PerIoData->BytesSEND 0 ;}
//printf("客户消息:%s\n",PerIoData->DataBuf.buf);
else
+=
{
PerIoData->BytesSEND ; BytesTransferred}
if
( )PerIoData->BytesRECV > PerIoData->BytesSEND// 调用WSASend()发送,直到所有收到的字节被发送
{
ZeroMemory
(&()PerIoData->Overlapped,sizeof ()OVERLAPPED);.
PerIoData->DataBuf=buf + PerIoData->Buffer ; PerIoData->BytesSEND.
PerIoData->DataBuf=len - PerIoData->BytesRECV ; PerIoData->BytesSENDif
( WSASend(,PerHandleData->Socket& ()PerIoData->DataBuf,1 ,& ,SendBytes0 ,&
()PerIoData->Overlapped,NULL )== ) SOCKET_ERRORif
{
( WSAGetLastError()!= ) ERROR_IO_PENDINGprintf
{
("WSASend() failed with error %d\n",WSAGetLastError ());return
0 ;}
}
else
printf
("WSASend() is OK!\n");}
else
=
{
PerIoData->BytesRECV 0 ;// 现在没有多余的数据可以发送,发出另外一个WSARecv()请求
=
Flags 0 ;ZeroMemory
(&()PerIoData->Overlapped,sizeof ()OVERLAPPED);.
PerIoData->DataBuf=len ; DATA_BUFSIZE.
PerIoData->DataBuf=buf ; PerIoData->Bufferif
( WSARecv(,PerHandleData->Socket& ()PerIoData->DataBuf,1 ,& ,RecvBytes& ,Flags&
()PerIoData->Overlapped,NULL )== ) SOCKET_ERRORif
{
( WSAGetLastError()!= ) ERROR_IO_PENDINGprintf
{
("WSARecv() failed with error %d\n",WSAGetLastError ());return
0 ;}
}
else
printf
("WSARecv() is OK!\n");}
}
}
运行结果:
服务器:
客户机:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)