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;
    }
  • 相关阅读:
    1026 Table Tennis (30)
    1029 Median
    1025 PAT Ranking (25)
    1017 Queueing at Bank (25)
    1014 Waiting in Line (30)
    1057 Stack (30)
    1010 Radix (25)
    1008 Elevator (20)
    字母大小写转换
    Nmap的基础知识
  • 原文地址:https://www.cnblogs.com/freesec/p/6203242.html
Copyright © 2011-2022 走看看