zoukankan      html  css  js  c++  java
  • 非阻塞Socket---基于TCP

    基本概念:

    5种编程模型:
    select模型

    主动获取socket状态

    WSAAsyncSelect模型
    以消息通知socket状态,被动接受消息(socket状态)


    WSAEventSelect模型
    以事件通知socket状态,被动接受事件(socket状态)

    重叠I/O模型
    使用重叠的数据结构一次投递多个I/O请求
    系统通过事件通知和完成例程通知应用程序

    完成端口模型
    基于线程池


    非阻塞的socket
    int ioctlsocket(
    _In_ SOCKET s,
    _In_ long cmd, //FIONBIO
    _Inout_ u_long *argp//0为阻塞,1为非阻塞
    );

    当socket被设置为FIONBIO =1 时,与它关联的accpt, recv,send, connect 函数都不会阻塞,将会执行这些函数很快就会返回继续执行

    所以需要解决这些问题使得成功执行这些函数

    测试代码:

    server.cpp

    #define WIN32_LEAN_AND_MEAN
    #include<iostream>
    #include<string>
    #include<winsock2.h>
    
    using namespace std;
    
    #pragma comment(lib,"ws2_32.lib")
    
    int main()
    {
        WSADATA wsaData;
        if (WSAStartup(MAKEWORD(2, 2), &wsaData))
        {
            cout << "版本错误, error code is " <<WSAGetLastError()<< endl;
            return 1;
        }
    
    
    
    
        ULONG noblock = 1;            //非阻塞参数
        SOCKET server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        SOCKET client_socket;
        SOCKADDR_IN bind_addr;
        SOCKADDR_IN client_addr;
        char recv_buffer[1024];
        char send_buffer[1024];
        int error_code;                //存储WSAGetLastError()的返回值
        int len = sizeof(SOCKADDR);
    
        //设置非阻塞socket
        if (!ioctlsocket(server_socket, FIONBIO, &noblock))
        {
            cout << "设置非阻塞模式成功" << endl;
        }
        bind_addr.sin_addr.S_un.S_addr = htonl(ADDR_ANY);
        bind_addr.sin_family = AF_INET;
        bind_addr.sin_port = htons(8889);
        
        //绑定ip和端口
        if (bind(server_socket, (sockaddr*)&bind_addr, len) == SOCKET_ERROR)
        {
            cout << "bind error code is " << WSAGetLastError() << endl;
            WSACleanup();
            return -1;
        }
        
        //开始监听
        if (listen(server_socket, 5) == SOCKET_ERROR)
        {
            cout << "listen error code is " << WSAGetLastError() << endl;
            WSACleanup();
            return -2;
        }
    
        //解决非阻塞的accpet
        while (1)
        {
            //非阻塞接受客户端连接,当没有客户端连接时当然返回INVALID_SOCKET,但是
            //错误码可以辨识是没有客户端连接还是有客户端连接却连接时失败.
            //也就是错误码可以让我们知道错误原因是什么. 如果错误码是WSAEWOULDBLOCK
            //则没有客户端连接,sleep一下后继续接受一下.如果是有客户端连接却失败了
            //需要打印出错误原因
            //如果接受成功则可以开始于客户端通信
            client_socket = accept(server_socket, (sockaddr*)&client_addr, &len);
            if (client_socket == INVALID_SOCKET)
            {
                error_code = WSAGetLastError();
                if (error_code == WSAEWOULDBLOCK)
                {
                    Sleep(100);
                    continue;
                }
                else
                {
                    cout << "accept失败,错误码" << error_code << endl;
                    closesocket(server_socket);
                    WSACleanup();
                    return -3;
                }
            }
            break;
        }
    
        //解决非阻塞的recv和send
        while (1)
        {
            memset(recv_buffer, '', 1024);
            //对于该函数,由于是非阻塞模式,没有数据不会阻塞,错误处理和上面类似
            if (recv(client_socket, recv_buffer, 1024, 0) == SOCKET_ERROR)
            {
                error_code = WSAGetLastError();
                if (error_code == WSAEWOULDBLOCK)
                {
                    Sleep(100);
                    continue;
                }
                else
                {
                    cout << "recv error code is " << error_code << endl;
                    closesocket(client_socket);
                    closesocket(server_socket);
                    WSACleanup();
                    return -4;
                }
            }
    
            if (strcmp(recv_buffer, "quit") == 0)
            {
                cout << "客户端退出" << endl;
                closesocket(client_socket);
                closesocket(server_socket);
                WSACleanup();
                return 1;
            }
            else 
            {
    
                while (1)
                {
                    cout << "recvice :" << recv_buffer << endl;
                    cin >> send_buffer;
                    if (send(client_socket, send_buffer, strlen(send_buffer) + 1, 0)==SOCKET_ERROR)
                    {
                        error_code = WSAGetLastError();
                        if (error_code == WSAEWOULDBLOCK)
                        {
                            Sleep(200);
                            continue;
                        }
                        else
                        {
                            cout << "send error code is " << error_code << endl;
                            closesocket(client_socket);
                            closesocket(server_socket);
                            WSACleanup();
                            return -5;
                        }
                    }
                    break;
                }
            }
        }
    
        return 0;
    }

    client.cpp

    #define _WINSOCK_DEPRECATED_NO_WARNINGS 
    #include<winsock2.h>
    #include<windows.h>
    #include<iostream>
    #include<string>
    using namespace std;
    
    #pragma comment(lib,"ws2_32.lib")
    
    int main()
    {
        WSADATA wsaData;
        if (WSAStartup(MAKEWORD(2, 2), &wsaData))
        {
            cout << "版本错误, error code is " << WSAGetLastError() << endl;
            return 1;
        }
    
    
    
    
        ULONG noblock = 1;
        SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        SOCKADDR_IN server_addr;
        char recv_buffer[1024];
        char send_buffer[1024];
        int error_code;
        int len = sizeof(SOCKADDR);
        int isQuit=0;
        if (!ioctlsocket(client_socket, FIONBIO, &noblock))
        {
            cout << "设置非阻塞模式成功" << endl;
        }
        server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(8889);
    
        while (1)
        {
            if (connect(client_socket, (const sockaddr*)&server_addr, len)==SOCKET_ERROR)
            {
                error_code = WSAGetLastError();
                if (error_code == WSAEWOULDBLOCK || error_code ==WSAEINVAL)
                {
                    continue;
                } 
                //当连接一次后需要判断是否已经连接.不然会报10056错误
                else if (error_code == WSAEISCONN)
                {
                    break;
                }
                else 
                {
                    cout << "connect error code is " << error_code << endl;
                    closesocket(client_socket);
                    WSACleanup();
                    return -1;
                }
            }
            break;
        }
        //发送和接受循环
        while (1)
        {
            memset(send_buffer, 0, 1024);
            cout << "输入内容:" << endl;
            cin >> send_buffer;
            if (strcmp(send_buffer, "quit")==0)
            {
                cout << "退出!" << endl;
                isQuit = 1;
            }
            
            //这个循环是发送一次的
            while (1)
            {
    
                if (send(client_socket, send_buffer,1024, 0) == SOCKET_ERROR)
                {
                    error_code = WSAGetLastError();
                    if (error_code == WSAEWOULDBLOCK)
                    {
                        Sleep(50);
                        continue;
                    }
                    else
                    {
                        cout << "send error code is " << error_code << endl;
                        closesocket(client_socket);
                        WSACleanup();
                        return -2;
                    }
                }
                else
                {
                    //发送成功,如果发送的是quit则退出否则继续运行
                    if (isQuit == 1)
                    {
                        closesocket(client_socket);
                        WSACleanup();
                        return 1;
                    }
                    else
                    {
                        break;
                    }
                    
                }
            }
    
            //这个循环是接受一次的
            while (1)
            {
                memset(recv_buffer, 0, 1024);
                if (recv(client_socket, recv_buffer, 1024, 0) == SOCKET_ERROR)
                {
                    error_code = WSAGetLastError();
                    if (error_code == WSAEWOULDBLOCK)
                    {
                        Sleep(100);
                        continue;
                    }
                    else
                    {
                        cout << "recv error code is " << error_code << endl;
                        closesocket(client_socket);
                        WSACleanup();
                        return -3;
                    }
                }
                else
                {
                    cout << "recvice :" << recv_buffer << endl;
                    break;
                }
            }
    
    
        }
    
    
        return 1;
    }
  • 相关阅读:
    python类库31[正则表达式匹配实例]
    Mysql百万级数据迁移实战笔记
    面试官:一千万数据,怎么快速查询?
    为什么MySQL不建议使用NULL作为列默认值?
    Redis各个数据类型最大存储量
    Rabbitmq延迟队列实现定时任务
    PHPstorm批量修改文件换行符CRLF为LF
    使用SeasLog打造高性能日志系统
    协程编程注意事项
    Rabbitmq 安装过程中常见问题(亲测可行)
  • 原文地址:https://www.cnblogs.com/freesec/p/6203242.html
Copyright © 2011-2022 走看看