IOCP Server: Select

客户端代码:

/*
 * 文件:Select(客户端)
 * 说明:项目中需包含 Ws2_32.lib,或使用 #pragma comment(lib, "*.lib")
 */

#include <stdio.h>
#include <WinSock2.h>

#define LENGTH 128
#define PORT_NUM 8086
#define MAX_DELAY 3000					// 比简单示例多出的部分
#define EXIT_WITH_MESSAGE(content) \
do \
{ \
	WSACleanup(); \
	printf_s(content); \
	system("pause"); \
	return EXIT_FAILURE; \
} \
while (FALSE);

int main()
{
	int ret, len;
	WORD wVersion;							// 网络传输版本
	WSADATA wsaData;						// WSADATA
	char msg[LENGTH];
	struct sockaddr_in server;				// 服务器信息
	SOCKET sockClient = INVALID_SOCKET;		// 待创建的套接字

	/* 比简单示例多出的部分 */
	struct timeval tv;
	struct fd_set fdset;

	wVersion = MAKEWORD(2, 2);
	ret = WSAStartup(wVersion, &wsaData);	// 初始化 WinSock DLL
	if (ret != 0)
	{
		// 告诉用户不能获取可用的 WinSock DLL
		EXIT_WITH_MESSAGE("WSAStartup Failed, cannot find a usable winsock dll!\n");
	}

	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		// 告诉用户版本不正确
		EXIT_WITH_MESSAGE("Error version!\n");
	}

	sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == sockClient)
	{
		// 套接字创建失败
		EXIT_WITH_MESSAGE("Invalid socket!\n");
	}
	server.sin_family = AF_INET;
	server.sin_port = htons(PORT_NUM);
	server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	ret = connect(sockClient, (struct sockaddr *)&server, sizeof(struct sockaddr));
	if (SOCKET_ERROR == ret)
	{
		// 连接失败
		closesocket(sockClient);
		EXIT_WITH_MESSAGE("Connect failed!\n");
	}

	do
	{
		/* 比简单示例多出的部分 */
		FD_ZERO(&fdset);
		FD_SET(sockClient, &fdset);
		tv.tv_sec = 0;
		tv.tv_usec = MAX_DELAY;
		ret = select(0, NULL, &fdset, NULL, &tv);
		if (ret <= 0)
		{
			// 选择失败
			closesocket(sockClient);
			EXIT_WITH_MESSAGE("Select failed!\n");
		}
		else if (!FD_ISSET(sockClient, &fdset))
		{
			// 设置失败
			closesocket(sockClient);
			EXIT_WITH_MESSAGE("FD isn't set!\n");
		}

		printf_s("Your reply: ");
/* 此为测试代码,因为有严格的超时时间限制,如果在最大延时内收不到则认为设备掉线
/		strcpy_s(msg, LENGTH, "Hello, SweetLover!");	*/
		gets_s(msg, LENGTH);
		// send 函数将会把消息发送到已连接的套接字上,而 sendto 则是发送到指定套接字上
		len = strlen(msg) + 1;
		ret = send(sockClient, msg, len, 0);
		if (ret < 0 || ret > len)
		{
			// 发送有误
			closesocket(sockClient);
			EXIT_WITH_MESSAGE("Send message error!\n");
		}

		if (strcmp(msg, "Quit") == 0)
			break;

		/* 比简单示例多出的部分 */
		FD_ZERO(&fdset);
		FD_SET(sockClient, &fdset);
		tv.tv_sec = 0;
		tv.tv_usec = MAX_DELAY;
		ret = select(0, &fdset, NULL, NULL, &tv);
		if (ret <= 0)
		{
			// 选择失败
			closesocket(sockClient);
			EXIT_WITH_MESSAGE("Select failed!\n");
		}
		else if (!FD_ISSET(sockClient, &fdset))
		{
			// 设置失败
			closesocket(sockClient);
			EXIT_WITH_MESSAGE("FD isn't set!\n");
		}

		memset(msg, 0, LENGTH);
		ret = recv(sockClient, msg, LENGTH, 0);
		if (SOCKET_ERROR == ret)
		{
			// 接受有误
			closesocket(sockClient);
			EXIT_WITH_MESSAGE("Receive message error!\n");
		}
		else if (0 == ret)
		{
			// 连接断开
			closesocket(sockClient);
			EXIT_WITH_MESSAGE("The connection closed!\n");
		}

		printf_s("Server: %s\n", msg);
	}
	while (strcmp(msg, "Quit"));

	printf_s("Exit connection!\n");
	closesocket(sockClient);
	WSACleanup();
	system("pause");

	return EXIT_SUCCESS;
}


 

服务器端代码:

/*
 * 文件:Select(服务器端)
 * 说明:项目中需包含 Ws2_32.lib,或使用 #pragma comment(lib, "*.lib")
 */

#include <stdio.h>
#include <WinSock2.h>

#define LENGTH 128
#define PORT_NUM 8086
#define MAX_DELAY 3000					// 比简单示例多出的部分
#define EXIT_WITH_MESSAGE(content) \
do \
{ \
	WSACleanup(); \
	printf_s(content); \
	system("pause"); \
	return EXIT_FAILURE; \
} \
while (FALSE);

int main()
{
	int ret, len;
	WORD wVersion;														// 网络传输版本
	WSADATA wsaData;													// WSADATA
	char msg[LENGTH];
	struct sockaddr_in server, client;									// 服务器信息,客户端信息
	SOCKET sockServer = INVALID_SOCKET, sockListener = INVALID_SOCKET;	// 服务器套接字,监听套接字

	/* 比简单示例多出的部分 */
	struct timeval tv;
	struct fd_set fdset;

	wVersion = MAKEWORD(2, 2);
	ret = WSAStartup(wVersion, &wsaData);	// 初始化 WinSock DLL
	if (ret != 0)
	{
		// 告诉用户不能获取可用的 WinSock DLL
		EXIT_WITH_MESSAGE("WSAStartup Failed, cannot find a usable winsock dll!\n");
	}

	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		// 告诉用户版本不正确
		EXIT_WITH_MESSAGE("Error version!\n");
	}

	sockListener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == sockListener)
	{
		// 套接字创建失败
		EXIT_WITH_MESSAGE("Invalid listener socket!\n");
	}

	server.sin_family = AF_INET;
	server.sin_addr.s_addr = INADDR_ANY;
	server.sin_port = htons(PORT_NUM);

	ret = bind(sockListener, (struct sockaddr *)&server, sizeof(struct sockaddr_in));
	if (SOCKET_ERROR == ret)
	{
		// 绑定端口失败
		closesocket(sockListener);
		EXIT_WITH_MESSAGE("Bind error!\n");
	}

	ret = listen(sockListener, SOMAXCONN);
	if (SOCKET_ERROR == ret)
	{
		// 监听失败
		closesocket(sockListener);
		EXIT_WITH_MESSAGE("Listen error!\n");
	}

	len = sizeof(struct sockaddr_in);
	sockServer = accept(sockListener, (struct sockaddr*)&client, &len);
	if (INVALID_SOCKET == sockServer)
	{
		// 接收失败
		closesocket(sockListener);
		EXIT_WITH_MESSAGE("Accept error!\n");
	}

	do
	{
		/* 比简单示例多出的部分 */
		FD_ZERO(&fdset);
		FD_SET(sockServer, &fdset);
		tv.tv_sec = 0;
		tv.tv_usec = MAX_DELAY;
		ret = select(0, &fdset, NULL, NULL, &tv);
		if (ret <= 0)
		{
			// 选择失败
			closesocket(sockServer);
			EXIT_WITH_MESSAGE("Select failed!\n");
		}
		else if (!FD_ISSET(sockServer, &fdset))
		{
			// 设置失败
			closesocket(sockServer);
			EXIT_WITH_MESSAGE("FD isn't set!\n");
		}

		memset(msg, 0, LENGTH);
		ret = recv(sockServer, msg, LENGTH, 0);
		if (SOCKET_ERROR == ret)
		{
			// 接受有误
			closesocket(sockServer);
			EXIT_WITH_MESSAGE("Receive message error!\n");
		}
		else if (0 == ret)
		{
			// 连接断开
			closesocket(sockServer);
			EXIT_WITH_MESSAGE("The connection closed!\n");
		}
		printf_s("Client: %s\n", msg);

		/* 比简单示例多出的部分 */
		FD_ZERO(&fdset);
		FD_SET(sockServer, &fdset);
		tv.tv_sec = 0;
		tv.tv_usec = MAX_DELAY;
		ret = select(0, NULL, &fdset, NULL, &tv);
		if (ret <= 0)
		{
			// 选择失败
			closesocket(sockServer);
			EXIT_WITH_MESSAGE("Select failed!\n");
		}
		else if (!FD_ISSET(sockServer, &fdset))
		{
			// 设置失败
			closesocket(sockServer);
			EXIT_WITH_MESSAGE("FD isn't set!\n");
		}

		printf_s("Your reply: ");
		gets_s(msg, LENGTH);
		len = strlen(msg) + 1;
		ret = send(sockServer, msg, len, 0);
		if (ret <= 0 || ret > len)
		{
			// 发送有误
			closesocket(sockServer);
			EXIT_WITH_MESSAGE("Send message error!\n");
		}
	}
	while (TRUE);

	printf_s("Exit connection!\n");
	closesocket(sockServer);
	closesocket(sockListener);
	WSACleanup();
	system("pause");

	return EXIT_SUCCESS;
}


作用:http://baike.baidu.com/subview/621719/11844440.htm?fr=aladdin

1.原型:int select (int maxfd + 1,fd_set *readset,fd_set *writeset, fd_set *exceptset,const struct timeval * timeout);

2.参数:

参数一:最大的文件描述符加1。
参数二:用于检查可读性,
参数三:用于检查可写性,
参数四:用于检查 带外数据,
参数五:一个指向timeval结构的 指针,用于决定select等待I/o的最长时间。如果为空将一直等待。 timeval结构的定义:struct timeval{
long tv_sec; // seconds
long tv_usec; // microseconds
}

总结:

1.编程角度上,Select 的关键就是能否掌握对该函数的使用。

2.Select 中比较重要的结构体是 fd_set 和 timeval,在调用 select 前,要初始化:

FD_ZERO(&fdset);

FD_SET(sockServer, &fdset);

具体阐释:

1.struct fd_set fdset; // 文件(这里的文件指的是套接字 socket)描述集合;

一个文件集合中可以包含多个元素,即:element∈fd_set;

2.FD_ZERO(&fdset); // 初始化文件描述集合为空,没有任何元素

3.FD_SET(sockServer, &fdset); // 将元素放入集合中,可以后置多个

4.详解

函数作用:选择函数确定一个或多个套接字的状态,必要时等待执行同步I / O。

函数参数:

(1)nfds[in]:Windows 下可忽略,只是用于兼容 Berkeley 套接字(Linux、Android)时才会用到。

(2)readfds[in & out]:用于检测可读性的文件描述集合,其他两个是用于检测可写性和异常的文件描述集合。

(3)timeout[in]:最大等待时间。表现为:最大等待时间内,套接字个数检测结果不为空时立刻返回个数,为空则在超时后返回0,错误返回 SOCKET_ERROR。

返回值:三个检测集合中的套接字个数,超时返回0,错误返回 SOCKET_ERROR。

5.小示例:

FD_ZERO(&fd_read); // fd_read.count = 0

FD_SET(s1, &fd_read); // fd_read.count = 1

FD_SET(s2, &fd_read); // fd_read.count = 2

// 在 tv 时间内,服务器的两个套接字 s1 和 s2 只有,s1 来了数据(可读缓冲区不为空)

ret = select(0, &fd_read, NULL, NULL, &tv); // 因此执行后,ret = 1,fd_read.count = 1

if (FD_ISSET(s1, &fd_read)){// recv/recvfrom 接收数据,并且做出处理} // 这里的 if 会被执行

if (FD_ISSET(s2, &fd_read)){// ... } // 这里的 if 不会被执行

同理:如果可写套接字的缓冲区为空,那么检测后就是可写状态

你可能感兴趣的:(select)