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;
    }
  • 相关阅读:
    垃圾回收相关概念
    垃圾回收相关算法
    垃圾回收概述
    StringTable
    执行引擎
    [前端]背景图,中间放大特效
    [Javascript]类数组对象为什么不能用for in进行遍历
    [前端] 画个圈圈显示百分比
    win10 Build 14905.rs_prerelease.160811-1739 填坑记录
    [翻译][10 By 10 外文博客] 01.uwp获得关注并安装
  • 原文地址:https://www.cnblogs.com/zzyoucan/p/5153898.html
Copyright © 2011-2022 走看看