/*
    基于流式套接字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小时内删除。