/*
基于流式套接字C/S结构的服务器端。
通信规程:
1、客户端连接到服务器以后,向服务器端发送命令
2、服务器解析命令的正确性,对于错误的命令,向客户端发送"Bad Command"的响应信息。
3、客户端主动断开连接,通过发送Quit命令实现。
4、服务器端在服务过程中,检测到错误后断开连接。
说明:
1、服务器工作在单线程select模型,所有处理都在main()中进行。
2、在该模式下,服务器并发为多个客户端提供服务
3、和基于非阻塞模式的单线程并发服务程序的区别是:
不要求套接字工作在非阻塞模式。
*/
//添加必要的头文件
//#define FD_SETSIZE 20
#include <winsock2.h>
#include <stdio.h>
#define MAX_CLIENT_COUNT FD_SETSIZE-1
//用这种方式添加链接库信息
#pragma comment(lib,"ws2_32")
//服务器默认工作在7777端口,所有IP地址接收的连接请求都处理
#define DEFAULTPORT 7777
//接收、发送缓冲区大小
#define SIZE_INBUF 1024
#define SIZE_OUTBUF 1024
#define HTTP_SERVER_OUTPUT_HEAD "HTTP/1.1 200 OK\r\n" "Date: Wed,22 nov 2000 02:44:34 GMT\r\n" "Server: Windows\r\n" "Last-Modified: Tue, 18 Apr 2000 13:24:10 GM T\r\n" "Accept-Ranges: bytes\r\n" "Content-Length: 121\r\n" "Connection: close\r\n" "Content-Type: text/html; charset=US-ASCII\r\n\r\n"
#define METHOD_NOT_IMPLEMENTED "<html><head><title>test</title></head><body><h1><div align=center><p>Hello Word!</p></h1></body></html>\r\n\r\n"
int main(void)
{
//1 准备WinSock执行环境
WORD ver=MAKEWORD(2,2);
WSADATA wsdata;
int retCode;
retCode=WSAStartup(ver,&wsdata);
if(retCode)
{
printf("==========WinSock初始化失败!程序退出==========\n");
return -1;
}
printf("==========WinSock初始化成功!==========\n");
//2 创建套接字
SOCKET sockListen=INVALID_SOCKET;
sockListen=socket(AF_INET,SOCK_STREAM,0);
if(INVALID_SOCKET==sockListen)
{
printf("==========监听套接字创建失败!==========\n");
WSACleanup();
return -2;
}
printf("==========监听套接字[%d]创建成功!==========\n",sockListen);
//3 准备监听信息
sockaddr_in addrServer;
//将地址信息结构体的所有字节均用0填充。填充时需要指定起始地址、填充内容和填充字节数
memset((void*)&addrServer,0,sizeof(sockaddr_in));
addrServer.sin_family=AF_INET;
addrServer.sin_port=htons(DEFAULTPORT);
addrServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
//4 将监听套接字绑定到特定地址(服务器端独有)
printf("监听套接字绑定到[%s:%d]......\n",inet_ntoa(addrServer.sin_addr),DEFAULTPORT);
retCode=bind(sockListen,(sockaddr*)&addrServer,sizeof(addrServer));
if(SOCKET_ERROR==retCode)
{
printf("绑定失败,错误代码:%d\n",WSAGetLastError());
closesocket(sockListen);
WSACleanup();
return -3;
}
printf("绑定成功\n");
bool keepListening=true;
bool keepWorking=true;
int i;
//定义保存套接字的数组
SOCKET Socks[MAX_CLIENT_COUNT];
for(i=0;i<MAX_CLIENT_COUNT;i++)
{
Socks[i]=INVALID_SOCKET;
}
//定义关注可读性网络事件的集合
fd_set fdread;
//5 进入监听状态
retCode=listen(sockListen,5);
if(SOCKET_ERROR==retCode)
{
printf("监听套接字不能正确进入监听状态,错误代码:%d\n",WSAGetLastError());
closesocket(sockListen);
WSACleanup();
return -4;
}
printf("成功进入监听状态......\n");
//7 应用层通信规程
//对于服务器端,一般是经常运行
SOCKET sockResponse=INVALID_SOCKET;
sockaddr_in addrClient;
int addrSize=sizeof(addrClient);
int recvBytes=0;
char InBuf[SIZE_INBUF];
char OutBuf[SIZE_OUTBUF];
//保存客户端地址信息的字符数组,格式为IP地址:端口号
char ClientInfo[20];
// char m_requestheader[1024];
//主循环
//超时间隔
timeval tv;
tv.tv_sec=1;
tv.tv_usec=0;
while(keepListening)
{
//1.清空关心的套接字集合fd_set
FD_ZERO(&fdread);
//2.将感兴趣的套接字加入到相应集合
FD_SET(sockListen,&fdread);
for(i=0;i<MAX_CLIENT_COUNT;i++)
{
if(INVALID_SOCKET!=Socks[i])
FD_SET(Socks[i],&fdread);
}
//3.调用select
retCode=select(0,&fdread,NULL,NULL,NULL);//&tv);
//4.根据返回值进行处理
if(retCode==0)
{
printf("select函数结束:超时\n");
continue;
}
if(retCode==SOCKET_ERROR)
{
printf("select函数结束:错误,代码%d\n",WSAGetLastError());
continue;
}
//4.1监听套接字
if(FD_ISSET(sockListen,&fdread))
{
memset(&addrClient,0,sizeof(addrClient));
//调用accept函数接收客户端连接请求。
sockResponse=accept(sockListen,(sockaddr*)&addrClient,&addrSize);
if(INVALID_SOCKET==sockResponse)
{
printf("接收连接请求的过程中产生错误,代码:%d\n",WSAGetLastError());
}
else
{
sprintf(ClientInfo,"%s:%d",inet_ntoa(addrClient.sin_addr),ntohs(addrClient.sin_port));
printf("接受来自[%s]的客户端连接请求,建立连接,响应套接字ID是[%d]\n",ClientInfo,sockResponse);
//查一个可用的套接字数组元素
for(i=0;i<MAX_CLIENT_COUNT;i++)
{
if(INVALID_SOCKET==Socks[i])
{
Socks[i]=sockResponse;
break;
}
}
if(i==MAX_CLIENT_COUNT)
{
printf("已达到最大客户端数量%d,关闭刚建立的连接\n",MAX_CLIENT_COUNT);
closesocket(sockResponse);
}
}
}
//4.2所有响应套接字(轮询)
for(i=0;i<MAX_CLIENT_COUNT;i++)
{
if(Socks[i]!=INVALID_SOCKET)
{
if(FD_ISSET(Socks[i],&fdread))
{
//真正的通信,即与数据端进行数据收、发交互,调用recv/send函数
keepWorking=true;
//调用recv函数检测有没有数据到来
recvBytes=recv(Socks[i],InBuf,sizeof(InBuf),0);
switch(recvBytes)
{
//对方关闭了连接
case 0:
recv函数
printf("客户端[%s]已经关闭了连接,响应套接字[%d]上的连接即将断开。\n",ClientInfo,Socks[i]);
keepWorking=false;
break;
case SOCKET_ERROR:
//这种错误在非阻塞模式中很常见,它不意味着错误,而是没有数据到来、没有连接请求到来
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论