socket I/O多路复用--select函数

张会勇老师《WinSocket网络编程经络》,中外WindowsSocket的良心之作。

见代码吧。

头文件

#pragma once

#define		MSG_SEND_VERSION	'B'
#define		MSG_SEND_PORT		18
#define		MSG_BUF_SIZE				512

//函数返回值
#define		MSG_SUCCESS					0
#define		MSG_VERSION_ERROR	-1
#define		MSG_FIELD_ERROR			-2

//消息字段的枚举值
enum msp_field
{
	MSP_VERSION,
	MSP_RECIP,
	MSP_RECIP_TERM,
	MSP_MESSAGE,
	MSP_SENDER,
	MSP_SEND_TERM,
	MSP_COOKIE,
	MSP_SIGNAT,
	MSP_TOTAL
};

struct field_name
{
	enum msp_field	field;
	char		*name;
};


实现

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


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

//Message Send Protocol字段原枚举值对应的名字
static struct  field_name msp_info[MSP_TOTAL] = {
	{MSP_VERSION,				"VERSION"},
	{MSP_RECIP,						"RECIPIENT"},
	{MSP_RECIP_TERM,			"RECIP-TERM"},
	{MSP_MESSAGE,				"MESSAGE"},
	{MSP_SENDER,					"SENDER"},
	{MSP_SEND_TERM,			"SENDER-TERM"},
	{MSP_COOKIE,					"COOKIE"},
	{MSP_SIGNAT,					"SIGNATURE"}
};

static	SOCKET					msp_client_socket [FD_SETSIZE];		//用来接收客户端的连接
static	unsigned int			client_max;
int handle_error(SOCKET s1,SOCKET s2,int exit)
{
	closesocket(s1);
	closesocket(s2);
	WSACleanup();
	return exit;
}
//接收客户端的连接请求
//	listen_socket	服务端监听的socket
//	socket_set	新接收的socket需要加入此集合
//返回成功返回新接收的client_socket,失败返回INVALID_SOCKET

SOCKET	msp_new_client(SOCKET listen_socket, PFD_SET socket_set)
{
	SOCKADDR_IN		client_address;
	int							client_address_length = sizeof(SOCKADDR);
	SOCKET					client_socket = accept(listen_socket, (PSOCKADDR)&client_address, &client_address_length);
	if (INVALID_SOCKET == client_socket)
	{
		printf ("msp_new_client(), accept(),error:%d\n",WSAGetLastError());
		return INVALID_SOCKET;
	}
	printf ("[MSP] Accept connection: %s\n",inet_ntoa(client_address.sin_addr));
	for (unsigned int i = 0; i < FD_SETSIZE; i++)
	{
		if (msp_client_socket[i] == INVALID_SOCKET)	//找到第一个可用的位置
		{
			msp_client_socket[i] = client_socket;
			FD_SET(client_socket,socket_set);
			client_max= max(i,client_max);
			return client_socket;
		}
	}
	closesocket(client_socket);	//遍历FD_SETSIZE,无可用位置,只能关闭client_socket,暂不处理,返回
	return INVALID_SOCKET;
}

//关闭客户端的套接口描述符,套接口从集合socket_set中移除
//
void msp_close_client(int index, fd_set* socket_set)
{
	SOCKET	client_socket = msp_client_socket[index];
	closesocket(client_socket);
	FD_CLR(client_socket, socket_set);
	msp_client_socket[index] = msp_client_socket[client_max];
	msp_client_socket[client_max--] = INVALID_SOCKET;
}

//将接收的数据填写到buf中
int msp_recv_data(int i, char* buf, int len, PSOCKADDR_IN from)
{
	int result;
	int addr_len = sizeof(SOCKADDR);
	SOCKET	s = msp_client_socket[i];
	if (0 == i)	//udp
	{
		result = recvfrom(s,buf, len, 0, (PSOCKADDR)from, &addr_len);
	}
	else
	{
		result = recv(s, buf, len, 0);
	}
	return result;
}

int msp_process_request(char* buf, int len)
{
	char *buf_end = buf + len;
	char		*msp_field[MSP_TOTAL] = {NULL};
	int		i = MSP_RECIP;

	if (MSG_SEND_VERSION != buf[0])	//版本检查
	{
		return MSG_VERSION_ERROR;
	}

	//解析其它字段
	msp_field[MSP_RECIP] = ++buf;
	char		*sep = (char*)memchr(buf, 0, buf_end - buf);
	while (sep && sep < buf_end)
	{
		msp_field[++i] = sep + 1;
		if (i == MSP_SIGNAT)
		{
			break;
		}
		buf  = sep + 1;
		sep = (char*)memchr(buf, 0, buf_end - buf);
	}
	if (i != MSP_SIGNAT)
	{
		return MSG_FIELD_ERROR;
	}
	//显示
	printf ("------------------------\n");
	for (int i = MSP_RECIP; i < MSP_TOTAL; i++)
	{
		printf ("%s:%s\n",msp_info[i].name, msp_field[i][0] ? msp_field[i] : "Empty");	//所在字段为空时,显示"Empty"
	}
	printf ("------------------------\n");
	return MSG_SUCCESS;
}

int msp_send_reply(int i, int error, PSOCKADDR_IN from)
{
	int result;
	int msg_len;
	int from_len = sizeof(*from);
	SOCKET	client_socket = msp_client_socket[i];
	char		*msg;
	char		*reply_description[] = {"+ Message is accepted.",
															"- Version is not match.",
															"- Message format is wrong.",
															"- Unknow error."};
	msg = reply_description[error];
	msg_len = strlen(msg);
	if (0 == i)
	{
		result = sendto(client_socket, msg, msg_len, 0, (PSOCKADDR)from, from_len);
	}
	else
	{
		result = send(client_socket, msg, msg_len, 0);
	}
	return result;
}
int main (int __argc, char** __argv)
{
	for (unsigned int i = 0; i < FD_SETSIZE; i++)
	{
		msp_client_socket[i]  = INVALID_SOCKET;
	}

	WSADATA		wsa;
	if (WSAStartup(MAKEWORD(2,2), &wsa))
	{
		printf ("[Server] WSAStartup() error.\n");
		return EXIT_FAILURE;
	}
	SOCKET msp_server_socket_tcp = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == msp_server_socket_tcp)
	{
		printf ("socket(,SOCK_DGRAM,),error:%d\n",WSAGetLastError());
		return handle_error(INVALID_SOCKET,INVALID_SOCKET,EXIT_FAILURE);
	}
	SOCKADDR_IN		server_address;
	server_address.sin_family = AF_INET;
	server_address.sin_port = htons(MSG_SEND_PORT);
	server_address.sin_addr.s_addr = INADDR_ANY;


	int result = bind(msp_server_socket_tcp, (PSOCKADDR)&server_address, sizeof(SOCKADDR));
	if (SOCKET_ERROR == result)
	{
		printf ("bind() msp_server_socket_tcp, error:%d\n",WSAGetLastError());
		return handle_error(msp_server_socket_tcp,INVALID_SOCKET,EXIT_FAILURE);
	}
	result = listen(msp_server_socket_tcp,SOMAXCONN);
	if (SOCKET_ERROR == result)
	{
		printf ("listen(),error:%d\n",WSAGetLastError());
		return handle_error(msp_server_socket_tcp,INVALID_SOCKET, EXIT_FAILURE);
	}

	SOCKET msp_server_socket_udp = socket(AF_INET, SOCK_DGRAM, 0);
	if (INVALID_SOCKET == msp_server_socket_udp)
	{
		printf ("socket(,SOCK_DGRAM,),error:%d\n",WSAGetLastError());
		return handle_error(msp_server_socket_tcp,INVALID_SOCKET,EXIT_FAILURE);
	}
	result = bind(msp_server_socket_udp,(PSOCKADDR)&server_address, sizeof(server_address));
	if (SOCKET_ERROR == result)
	{
		printf ("bind(socket_udp),error:%d\n",WSAGetLastError());
		return handle_error(msp_server_socket_tcp, msp_server_socket_udp,EXIT_FAILURE);
	}
	FD_SET	read_set, read_all;
	FD_ZERO(&read_all);
	FD_SET(msp_server_socket_tcp, &read_all);
	FD_SET(msp_server_socket_udp, &read_all);
	msp_client_socket[0] = msp_server_socket_udp;	//UPD socket 固定在第一个位置

	printf ("[MSP] Server is running...\n");


	int							ready_count ;
	SOCKET					new_socket,client_socket;
	SOCKADDR_IN		client_address;
	char							recv_buf[MSG_BUF_SIZE+1];
	while (1)
	{
		read_set = read_all;
		ready_count = select(0, &read_set, NULL, NULL, NULL);
		if (SOCKET_ERROR == ready_count)
		{
			printf ("select(),error:%d\n",WSAGetLastError());
			break;
		}
		if (FD_ISSET(msp_server_socket_tcp,&read_set))	//检查描述符是否在集合中
		{
			
			new_socket = msp_new_client(msp_server_socket_tcp,&read_all);
			if (--ready_count <= 0)
			{
				continue;
			}
		}
		//有客户端发送数据,进入下面的for循环
		for (unsigned int i = 0; i <= client_max && ready_count > 0; i++)
		{
			client_socket = msp_client_socket[i];//遍历socket,并调用FD_ISSET,来检查是否在read_set中
			if (INVALID_SOCKET == client_socket)
			{
				return handle_error(msp_server_socket_tcp,msp_server_socket_udp,EXIT_FAILURE);
			}
			if (!FD_ISSET(client_socket,&read_set))
			{
				continue;
			}
			result = msp_recv_data(i, recv_buf, MSG_BUF_SIZE, &client_address);
			if (0 == i)	//msp_client_socket[0],UDP, recvfrom(,,,&client_address,);
			{
				printf ("recv data from %s\n",inet_ntoa(client_address.sin_addr));
			}
			
			if (result <= 0)
			{
				msp_close_client(i,&read_all);
				--ready_count;
				continue;
			}
			result = msp_process_request(recv_buf, result);
			result = msp_send_reply(i, result, &client_address);
			if (SOCKET_ERROR == result)
			{
				msp_close_client(i,&read_all);
			}
			--ready_count;
		}
	}
	for (unsigned int i	= 0; i < client_max; i++)
	{
		closesocket(msp_client_socket[i]);
	}
	closesocket(msp_server_socket_tcp);
	WSACleanup();
	return EXIT_SUCCESS;
}

执行结果:

>MsgServer.exe
[MSP] Server is running...
[MSP] Accept connection: 192.168.2.102
------------------------
RECIPIENT:zeek
RECIP-TERM:Console
MESSAGE:Come on
SENDER:me
SENDER-TERM:Console
COOKIE:20140513230733
SIGNATURE:Empty
------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值