【通信协议及编码】实验2:三种I/O模型回送服务器程序设计


一、实验目的

  1. 掌握WinSock API I/O模型框架与原理
  2. 掌握WinSock API Select I/O模型;
  3. 掌握WinSock API WSAEventSelect I/O模型;
  4. 掌握WinSock API I/O Completion Port模型。

二、实验环境

  1. 操作系统:WINDOWS 7及以上
  2. 开发工具:Microsoft VisualBasic6.0
  3. 实验设备:PC

三、实验内容

  1. 基于Select I/O模型创建一个回送服务器,服务器在5150端口上侦听TCP连接,并将收到的客户机数据回送客户机。
  2. 设计一个WSAEventSelect I/O模型,服务器在端口5150侦听TCP连接,回送到客户机数据。
  3. 开发一个简单的WinSock回声服务器,应用程序监听端口5150的TCP连接,并将收到的客户机数据回送给客户机。

客户机:

//程序说明:
//    这是一个回送客户机程序。连接TCP server,发送数据,接收服务器回送的数据
//    命令行参数:
//    client [-p:x] [-s:IP] [-n:x] [-o]
//           -p:x      远程服务器端口
//           -s:IP     服务器地址或主机名
//           -n:x      消息的发送次数
//           -o        只发送,不接收 
#include "stdafx.h"
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#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] = '\0';
			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服务器

// 程序说明:
//
//    本程序演示如何基于Winsock 的 select() API I/O 模型创建一个回送服务器。
//    服务器在5150端口上侦听TCP连接
//    将收到的客户机数据再回送客户机,直到可会关闭连接

#include <winsock2.h>
#include <windows.h>
#include <stdio.h>

#pragma comment(lib,"ws2_32.lib")

#define PORT 5150
#define DATA_BUFSIZE 8192

typedef struct _SOCKET_INFORMATION {
	CHAR Buffer[DATA_BUFSIZE];
	WSABUF DataBuf;
	SOCKET Socket;
	OVERLAPPED Overlapped;
	DWORD BytesSEND;
	DWORD BytesRECV;
} SOCKET_INFORMATION, *LPSOCKET_INFORMATION;

// 原型
BOOL CreateSocketInformation(SOCKET s);
void FreeSocketInformation(DWORD Index);

// 全局变量
DWORD TotalSockets = 0;
LPSOCKET_INFORMATION SocketArray[FD_SETSIZE];

int main(int argc, char **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 RecvBytes;

	if ((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_STREAM, 0, NULL, 0,
		WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
	{
		printf("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)&InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)
	{
		printf("bind() failed with error %d\n", WSAGetLastError());
		return 1;
	}
	else
		printf("bind() is OK!\n");

	if (listen(ListenSocket, 5))
	{
		printf("listen() failed with error %d\n", WSAGetLastError());
		return 1;
	}
	else
		printf("listen() is OK!\n");

	// 将侦听套接字的阻塞模式转为非阻塞模式,这样服务器在等待连接到达期间不会发生阻塞
	NonBlock = 1;
	if (ioctlsocket(ListenSocket, FIONBIO, &NonBlock) == SOCKET_ERROR)
	{
		printf("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]->BytesSEND)
				FD_SET(SocketArray[i]->Socket, &WriteSet);
			else
				FD_SET(SocketArray[i]->Socket, &ReadSet);

		if ((Total = select(0, &ReadSet, &WriteSet, NULL, NULL)) == SOCKET_ERROR)
		{
			printf("select() returned with error %d\n", WSAGetLastError());
			return 1;
		}
		else
			printf("select() is OK!\n");

		// 检查到达的连接在侦听套接字
		if (FD_ISSET(ListenSocket, &ReadSet))
		{
			Total--;
			if ((AcceptSocket = accept(ListenSocket, NULL, NULL)) != INVALID_SOCKET)
			{
				// 设置套接字AcceptSocket为非阻塞模式
			   // 这样服务器在调用WSASends发送数据时就不会被阻塞
				NonBlock = 1;
				if (ioctlsocket(AcceptSocket, FIONBIO, &NonBlock) == SOCKET_ERROR)
				{
					printf("ioctlsocket(FIONBIO) failed with error %d\n", WSAGetLastError());
					return 1;
				}
				else
					printf("ioctlsocket(FIONBIO) is OK!\n");

				if (CreateSocketInformation(AcceptSocket) == FALSE)
				{
					printf("CreateSocketInformation(AcceptSocket) failed!\n");
					return 1;
				}
				else
					printf("CreateSocketInformation() is OK!\n");
			}
			else
			{
				if (WSAGetLastError() != WSAEWOULDBLOCK)
				{
					printf("accept() failed with error %d\n", WSAGetLastError());
					return 1;
				}
				else
					printf("accept() is fine!\n");
			}
		}

		// 依次处理所有的套接字,SocketInfo为当前要处理的套接字信息
		for (i = 0; Total > 0 && 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,
					&Flags, NULL, NULL) == SOCKET_ERROR)
				{
					if (WSAGetLastError() != WSAEWOULDBLOCK)
					{
						printf("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->BytesSEND;

				if (WSASend(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &SendBytes, 0,
					NULL, NULL) == SOCKET_ERROR)
				{
					if (WSAGetLastError() != WSAEWOULDBLOCK)
					{
						printf("WSASend() failed with error %d\n", WSAGetLastError());
						FreeSocketInformation(i);
					}
					else
						printf("WSASend() is OK!\n");

					continue;
				}
				else
				{
					SocketInfo->BytesSEND += SendBytes;

					if (SocketInfo->BytesSEND == SocketInfo->BytesRECV)
					{
						SocketInfo->BytesSEND = 0;
						SocketInfo->BytesRECV = 0;
					}
				}
			}
		}
	}
}

BOOL CreateSocketInformation(SOCKET s)
{
	LPSOCKET_INFORMATION SI;

	printf("Accepted socket number %d\n", s);

	if ((SI = (LPSOCKET_INFORMATION)GlobalAlloc(GPTR, sizeof(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 i;

	closesocket(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--;
}

运行结果:
服务器:
在这里插入图片描述
客户机:
在这里插入图片描述

2.WSAEventSelect服务器

// WSAEventSelect模型回送服务器
// WSAEventSelect.cpp
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
#define PORT 5150
#define DATA_BUFSIZE 8192

typedef struct _SOCKET_INFORMATION {
	CHAR Buffer[DATA_BUFSIZE];
	WSABUF DataBuf;
	SOCKET Socket;
	DWORD BytesSEND;
	DWORD BytesRECV;
} SOCKET_INFORMATION, *LPSOCKET_INFORMATION;

BOOL CreateSocketInformation(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 argc, char **argv)
{
	SOCKET Listen;
	SOCKET Accept;
	SOCKADDR_IN InternetAddr;
	LPSOCKET_INFORMATION SocketInfo;
	DWORD Event;
	WSANETWORKEVENTS NetworkEvents;
	WSADATA wsaData;
	DWORD Flags;
	DWORD RecvBytes;
	DWORD SendBytes;

	if (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_STREAM, 0)) == INVALID_SOCKET)
	{
		printf("socket() failed with error %d\n", WSAGetLastError());
		return 1;
	}
	else
		printf("socket() is OK!\n");

	if (CreateSocketInformation(Listen) == FALSE)
		printf("CreateSocketInformation() failed!\n");
	else
		printf("CreateSocketInformation() is OK lol!\n");

	if (WSAEventSelect(Listen, EventArray[EventTotal - 1], FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR)
	{
		printf("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)&InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)
	{
		printf("bind() failed with error %d\n", WSAGetLastError());
		return 1;
	}
	else
		printf("bind() is OK!\n");

	if (listen(Listen, 5))
	{
		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_FAILED)
		{
			printf("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_ERROR)
		{
			printf("WSAEnumNetworkEvents() failed with error %d\n", WSAGetLastError());
			return 1;
		}
		else
			printf("WSAEnumNetworkEvents() should be fine!\n");

		if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
		{
			if (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]->Socket, NULL, NULL)) == INVALID_SOCKET)
			{
				printf("accept() failed with error %d\n", WSAGetLastError());
				break;
			}
			else
				printf("accept() should be OK!\n");

			if (EventTotal > WSA_MAXIMUM_WAIT_EVENTS)
			{
				printf("Too many connections - closing socket...\n");
				closesocket(Accept);
				break;
			}

			CreateSocketInformation(Accept);

			if (WSAEventSelect(Accept, EventArray[EventTotal - 1], FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR)
			{
				printf("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_WRITE)
		{
			if (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,
					&Flags, NULL, NULL) == SOCKET_ERROR)
				{
					if (WSAGetLastError() != WSAEWOULDBLOCK)
					{
						printf("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->BytesSEND;

				if (WSASend(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &SendBytes, 0,
					NULL, NULL) == SOCKET_ERROR)
				{
					if (WSAGetLastError() != WSAEWOULDBLOCK)
					{
						printf("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 += SendBytes;

					if (SocketInfo->BytesSEND == SocketInfo->BytesRECV)
					{
						SocketInfo->BytesSEND = 0;
						SocketInfo->BytesRECV = 0;
					}
				}
			}
		}

		if (NetworkEvents.lNetworkEvents & FD_CLOSE)
		{
			if (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;
}

BOOL CreateSocketInformation(SOCKET s)
{
	LPSOCKET_INFORMATION SI;

	if ((EventArray[EventTotal] = WSACreateEvent()) == WSA_INVALID_EVENT)
	{
		printf("WSACreateEvent() failed with error %d\n", WSAGetLastError());
		return FALSE;
	}
	else
		printf("WSACreateEvent() is OK!\n");

	if ((SI = (LPSOCKET_INFORMATION)GlobalAlloc(GPTR, sizeof(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 i;

	closesocket(SI->Socket);
	GlobalFree(SI);

	if (WSACloseEvent(EventArray[Event]) == TRUE)
		printf("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--;
}

运行结果:
服务器:
在这里插入图片描述
客户机:
在这里插入图片描述

3.I/O Completion Port

// I/O完成端口服务器程序
//程序名:IOComplete.cpp

#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
#define PORT 5150
#define DATA_B UFSIZE 8192

// 类型定义
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;

// 原型声明
DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID);

int main(int argc, char **argv)
{
	SOCKADDR_IN InternetAddr;
	SOCKET Listen;
	HANDLE ThreadHandle;
	SOCKET Accept;
	HANDLE CompletionPort;
	SYSTEM_INFO SystemInfo;
	LPPER_HANDLE_DATA PerHandleData;
	LPPER_IO_OPERATION_DATA PerIoData;
	int i;
	DWORD RecvBytes;
	DWORD Flags;
	DWORD ThreadID;
	WSADATA wsaData;
	DWORD Ret;

	if ((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_VALUE, NULL, 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, CompletionPort, 0, &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_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
	{
		printf("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)&InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)
	{
		printf("bind() failed with error %d\n", WSAGetLastError());
		return 1;
	}
	else
		printf("bind() is fine!\n");

	// 开始监听
	if (listen(Listen, 5) == SOCKET_ERROR)
	{
		printf("listen() failed with error %d\n", WSAGetLastError());
		return 1;
	}
	else
		printf("listen() is working...\n");

	// 接受连接并且交给完成端口处理
	while (TRUE)
	{
		if ((Accept = WSAAccept(Listen, NULL, NULL, NULL, 0)) == SOCKET_ERROR)
		{
			printf("WSAAccept() failed with error %d\n", WSAGetLastError());
			return 1;
		}
		else
			printf("WSAAccept() looks fine!\n");

		// 为套接字分配内存
		if ((PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(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 = Accept;

		if (CreateIoCompletionPort((HANDLE)Accept, CompletionPort, (DWORD)PerHandleData, 0) == 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_DATA)GlobalAlloc(GPTR, sizeof(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_ERROR)
		{
			if (WSAGetLastError() != ERROR_IO_PENDING)
			{
				printf("WSARecv() failed with error %d\n", WSAGetLastError());
				return 1;
			}
		}
		else
			printf("WSARecv() is OK!\n");
	}//end main
}

DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)
{
	HANDLE CompletionPort = (HANDLE)CompletionPortID;
	DWORD BytesTransferred;
	LPPER_HANDLE_DATA PerHandleData;
	LPPER_IO_OPERATION_DATA PerIoData;
	DWORD SendBytes, RecvBytes;
	DWORD Flags;

	while (TRUE)
	{
		if (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_ERROR)
			{
				printf("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->BytesSEND;

			if (WSASend(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &SendBytes, 0,
				&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
			{
				if (WSAGetLastError() != ERROR_IO_PENDING)
				{
					printf("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->Buffer;

			if (WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
				&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
			{
				if (WSAGetLastError() != ERROR_IO_PENDING)
				{
					printf("WSARecv() failed with error %d\n", WSAGetLastError());
					return 0;
				}
			}
			else
				printf("WSARecv() is OK!\n");
		}
	}
}

运行结果:
服务器:
在这里插入图片描述
客户机:
在这里插入图片描述


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值