张会勇老师《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
------------------------