zoukankan      html  css  js  c++  java
  • 非阻塞socket学习,select基本用法

    server

    #include <stdio.h>
    #include <winsock2.h>
    #include <iostream>
    
    #pragma comment(lib, "WS2_32.lib")
    
    #define PORT 9999
    #define DATA_BUFSIZE 8192
    
    typedef struct _SOCKET_INFORMATION{
        CHAR Buffer[DATA_BUFSIZE];        //发送和接收数据的缓冲区
        WSABUF DataBuf;                    //定义发送和接收数据缓冲区的结构体,包括缓冲区长度和内容
        SOCKET Socket;                    //与客户端进行通信的套接字
        DWORD BytesSEND;                //保存套接字发送的字节数
        DWORD BytesRECV;                //保存套接字接收的字节数
    } SOCKET_INFOMATION, * LPSOCKET_INFORMATION;
    
    DWORD TotalSockets = 0;
    LPSOCKET_INFORMATION SocketArray[FD_SETSIZE];
    
    bool CreateSocketInformation(SOCKET s)
    {
        LPSOCKET_INFORMATION SI;    //用于保存套接字的信息
        //为SI分配内存空间
        if ((SI = (LPSOCKET_INFORMATION)GlobalAlloc(GPTR, sizeof SOCKET_INFOMATION)) == NULL)
        {
            printf("GlobalAlloc()   failed   with   error   %d
    ", GetLastError());
            return   FALSE;
        }
    
        //初始化SI的值
        SI->Socket = s;
        SI->BytesSEND = 0;
        SI->BytesRECV = 0;
    
        SocketArray[TotalSockets] = SI;
        TotalSockets++;
    
        return true;
    }
    
    void FreeSocketInformation(DWORD Index)
    {
        LPSOCKET_INFORMATION SI = SocketArray[Index];
        DWORD i;
    
        closesocket(SI->Socket);
        GlobalFree(SI);
        if (Index != (TotalSockets - 1))
        {
            for (i = Index; i < TotalSockets; i++)
            {
                SocketArray[i] = SocketArray[i + 1];
            }
        }
    
        TotalSockets--;
    }
    
    int main()
    {
        SOCKET ListenSocket;        //监听套接字
        SOCKET AcceptSocket;        //与客户端通信的套接字
        SOCKADDR_IN InternetAddr;    //服务器地址
        WSADATA wsaData;
        int Ret;
    
        FD_SET WriteSet;            //获取可写套接字集合
        FD_SET ReadSet;                //获取可读性套接字集合
        DWORD Total = 0;            //处于就绪状态的套接字
        DWORD SendBytes;            //发送套接字
        DWORD RecvBytes;            //接收的套接字
    
        //初始化WinSock环境
        if ((Ret = WSAStartup(MAKEWORD(2, 2), &wsaData) != 0))
        {
            printf("WSAStartup()   failed   with   error   %d
    ", Ret);
            WSACleanup();
            return -1;
        }
    
        //创建用于监听的套接字
        if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
        {
            printf("WSASocket()   failed   with   error   %d
    ", WSAGetLastError());
            return -1;
        }
    
        //设置监听地址和端口
        InternetAddr.sin_family = AF_INET;
        InternetAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
        InternetAddr.sin_port = htons(PORT);
    
        //绑定监听套接字到本地地址和端口
        if (bind(ListenSocket, (PSOCKADDR)&InternetAddr, sizeof InternetAddr) == INVALID_SOCKET)
        {
            printf("bind()   failed   with   error   %d
    ", WSAGetLastError());
            return -1;
        }
    
        //开始监听
        if (listen(ListenSocket, 5))
        {
            printf("listen()   failed   with   error   %d
    ", WSAGetLastError());
            return -1;
        }
    
        //设置成非阻塞方式
        ULONG NonBlock = 1;
        if (ioctlsocket(ListenSocket, FIONBIO, &NonBlock))
        {
            printf("ioctlsocket() failed with error %d
    ", WSAGetLastError());
            return -1;
        }
    
        CreateSocketInformation(ListenSocket);//ListenSocket套接字创建对应的SOCKET_INFORMATION,把ListenSocket添加到SocketArray数组中
    
        while (true)
        {
            FD_ZERO(&ReadSet);//读套接字清零
            FD_ZERO(&WriteSet);//写套接字清零
    
            FD_SET(ListenSocket, &ReadSet);//向ReadSet中添加监听套接字ListenSocket
    
            for (DWORD i = 0; i < TotalSockets; ++i)
            {
                LPSOCKET_INFORMATION SocketInfo = SocketArray[i];
                FD_SET(SocketInfo->Socket, &ReadSet);
                FD_SET(SocketInfo->Socket, &WriteSet);
            }
    
            if((Total = select(0, &ReadSet, &WriteSet, NULL, NULL)) == SOCKET_ERROR){//刚开始为什么监听套接字没有可写返回,而与客户端连接的套接字总是可写???
                printf("select()   returned   with   error   %d
    ",   WSAGetLastError());   
                return -1;  
            }
    
            for (DWORD i = 0; i < TotalSockets; i++)
            {
                LPSOCKET_INFORMATION SocketInfo = SocketArray[i];//SocketArray保存了所有监听的可读和可写的套接字
                if (FD_ISSET(SocketInfo->Socket, &ReadSet))//判断套接字是否可读
                {
                    if (SocketInfo->Socket == ListenSocket)//对于监听套接字表示有新的客户端连接
                    {
                        Total--;
                        if ((AcceptSocket = accept(ListenSocket, NULL, NULL)) != INVALID_SOCKET)
                        {
                            NonBlock = 1;
                            if (ioctlsocket(AcceptSocket, FIONBIO, &NonBlock) == SOCKET_ERROR)//设置AcceptSocket套接字为非阻塞套接字,这样与客户端通信就不会阻塞了
                            {
                                printf("ioctlsocket()   failed   with   error   %d
    ",   WSAGetLastError());   
                                return -1;
                            }
    
                            if (CreateSocketInformation(AcceptSocket) == false)//将AcceptSocket添加到SocketArray数组中
                            {
                                return -1;
                            }
                        }
                        else
                        {
                            if (WSAGetLastError() != WSAEWOULDBLOCK)
                            {
                                printf("accept()   failed   with   error   %d
    ",   WSAGetLastError());   
                                return -1;
                            }
                        }
                    }
                    else//接收数据
                    {
                        Total--;
                        memset(SocketInfo->Buffer, 0, sizeof SocketInfo->Buffer);
                        SocketInfo->DataBuf.buf = SocketInfo->Buffer;
                        SocketInfo->DataBuf.len = DATA_BUFSIZE;
    
                        DWORD Flags = 0;
                        if (WSARecv(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &RecvBytes, &Flags, NULL, NULL) == SOCKET_ERROR)
                        {
                            if (WSAGetLastError() != WSAEWOULDBLOCK)
                            {
                                printf("WSARecv()   failed   with   error   %d
    ",   WSAGetLastError()); 
                                FreeSocketInformation(i);
                            }
                            continue;
                        }
                        else
                        {
                            SocketInfo->BytesRECV = RecvBytes;
                            SocketInfo->DataBuf.buf[RecvBytes] = '';
                            if (RecvBytes == 0)
                            {
                                FreeSocketInformation(i);
                                continue;
                            }
                            else
                            {
                                std::cout << SocketInfo->DataBuf.buf << std::endl;// 如果成功接收数据,则打印收到的数据
                            }
                        }
                    }
                }
                else
                {
                    if (FD_ISSET(SocketInfo->Socket, &WriteSet))//可写会一直被调用,判断大于其写缓冲的最低水位
                    {
                        Total--;
                        SocketInfo->DataBuf.buf = SocketInfo->Buffer + SocketInfo->BytesSEND;// 初始化缓冲区位置
                        SocketInfo->DataBuf.len = SocketInfo->BytesRECV - SocketInfo->BytesSEND;// 初始化缓冲区长度
    
                        if (SocketInfo->DataBuf.len > 0)// 如果有需要发送的数据,则发送数据
                        {
                            if (WSASend(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &SendBytes, 0, NULL, NULL) == SOCKET_ERROR)
                            {
                                if (WSAEWOULDBLOCK != WSAGetLastError())
                                {
                                    printf("WSASend()   failed   with   error   %d
    ", WSAGetLastError()); 
                                    FreeSocketInformation(i);
                                }
                                continue;
                            }
                            else
                            {
                                SocketInfo->BytesSEND += SendBytes;        //记录发送数据的字节数目
                                if (SocketInfo->BytesSEND == SocketInfo->BytesRECV)
                                {
                                    SocketInfo->BytesSEND = 0;
                                    SocketInfo->BytesRECV = 0;
                                }
                            }
                        }
                    }
                }
            }
        }
        system("pause");
        return 0;
        
    }

    client

    #include <winsock.h>
    #include <iostream>
    #define BUFSIZE 64
    #define PORT 9999
    
    #pragma comment(lib, "WS2_32.lib")
    int main()
    {
        WSADATA wsaData;
        SOCKET sClient;
    
        sockaddr_in addrServ;
        char buf[BUFSIZE];
        int retVal;
    
        if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
        {
            std::cout << "WSAStartup失败!" << std::endl;
            return -1;
        }
    
        sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (INVALID_SOCKET == sClient)
        {
            std::cout << "socket() 错误!" << std::endl;
            WSACleanup();
            return -1;
        }
    
        addrServ.sin_family = AF_INET;
        addrServ.sin_port = htons(PORT);
        addrServ.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    
        retVal = connect(sClient, (LPSOCKADDR)&addrServ, sizeof(addrServ));
        if (SOCKET_ERROR == retVal)
        {
            std::cout << "connect 错误!" << std::endl;
            closesocket(sClient);
            WSACleanup();
            return -1;
        }
    
        while(true)
        {
            std::cout << "输入要发给服务器的内容:" << std::endl;
            char msg[BUFSIZE];
            std::cin.getline(msg, BUFSIZE);
            ZeroMemory(buf, BUFSIZE);
            strcpy(buf, msg);
            retVal = send(sClient, buf, strlen(buf), 0);
            if (SOCKET_ERROR == retVal)
            {
                std::cout << "发送失败" << std::endl;
                closesocket(sClient);
                WSACleanup();
                return -1;
            }
    
            retVal = recv(sClient, buf, sizeof buf, 0);
            std::cout << "从服务器端接收:" << buf << std::endl;
            if (strcmp(buf, "quit") == 0)
            {
                std::cout << "quit" << std::endl;
                break;
            }
        }
        closesocket(sClient);
        WSACleanup();
        return 0;
    }
  • 相关阅读:
    统计nginx日志里访问次数最多的前十个IP
    while 格式化输出 运算符 字符编码
    Python 软件安装
    Python 基础
    Typora 基础的使用方法
    Django ORM (四) annotate,F,Q 查询
    Django 惰性机制
    Django ORM (三) 查询,删除,更新操作
    Django ORM (二) 增加操作
    Django ORM (一) 创建数据库和模型常用的字段类型参数及Field 重要参数介绍
  • 原文地址:https://www.cnblogs.com/zzyoucan/p/5153898.html
Copyright © 2011-2022 走看看