BSD 套接字 - 如何使用非阻塞套接字?

Ste*_*ing 3 c++ sockets bsd tcp nonblocking

我正在尝试使用非阻塞 TCP 套接字。问题是他们仍然在阻塞。代码如下 -

服务器代码 -

struct sockaddr name;
char buf[80];

void set_nonblock(int socket) {
    int flags;
    flags = fcntl(socket,F_GETFL,0);
    assert(flags != -1);
    fcntl(socket, F_SETFL, flags | O_NONBLOCK);
}

int main(int agrc, char** argv) {

    int sock, new_sd, adrlen;   //sock is this socket, new_sd is connection socket

    name.sa_family = AF_UNIX;
    strcpy(name.sa_data, "127.0.0.1");
    adrlen = strlen(name.sa_data) + sizeof(name.sa_family);

    //make socket
    sock = socket(AF_UNIX, SOCK_STREAM, 0);

    if (sock < 0) {
        printf("\nBind error %m", errno);
        exit(1);
    }

    //unlink and bind
    unlink("127.0.0.1");
    if(bind (sock, &name, adrlen) < 0)
        printf("\nBind error %m", errno);

    //listen
    if(listen(sock, 5) < 0)
        printf("\nListen error %m", errno);

    //accept
    new_sd = accept(sock, &name, (socklen_t*)&adrlen);
    if( new_sd < 0) {
        cout<<"\nserver accept failure "<<errno;
        exit(1);
    }

    //set nonblock
    set_nonblock(new_sd);

    char* in = new char[80];
    std::string out = "Got it";
    int numSent;
    int numRead;

    while( !(in[0] == 'q' && in[1] == 'u' && in[2] == 'i' && in[3] == 't') ) {

        //clear in buffer
        for(int i=0;i<80;i++)
            in[i] = ' ';

        cin>>out;
        cin.get();

        //if we typed something, send it
        if(strlen(out.c_str()) > 0) {
            numSent = send(new_sd, out.c_str(), strlen(out.c_str()), 0);
            cout<<"\n"<<numSent<<" bytes sent";
        }

        numRead = recv(new_sd, in, 80, 0);
        if(numRead > 0)
            cout<<"\nData read from client - "<<in;

     }   //end while

     cout<<"\nExiting normally\n";
     return 0;
}
Run Code Online (Sandbox Code Playgroud)

客户代码——

struct sockaddr name;

void set_nonblock(int socket) {
    int flags;
    flags = fcntl(socket,F_GETFL,0);
    assert(flags != -1);
    fcntl(socket, F_SETFL, flags | O_NONBLOCK);
}

int main(int agrc, char** argv) {

    int sock, new_sd, adrlen;

    sock = socket(AF_UNIX, SOCK_STREAM, 0);

    if (sock < 0) {
        printf("\nserver socket failure %m", errno);
        exit(1);
    }

    //stuff for server socket
    name.sa_family = AF_UNIX;
    strcpy(name.sa_data, "127.0.0.1");
    adrlen = strlen(name.sa_data) + sizeof(name.sa_family);

    if(connect(sock, &name, adrlen) < 0) {
        printf("\nclient connection failure %m", errno);
        exit(1);
    }

    cout<<"\nSuccessful connection\n";

    //set nonblock
    set_nonblock(sock);

    std::string out;
    char* in = new char[80];
    int numRead;
    int numSent;


    while(out.compare("quit")) {

        //clear in
        for(int i=0;i<80;i++)
            in[i] = '\0';


        numRead = recv(sock, in, 80, 0);

        if(numRead > 0)
            cout<<"\nData read from server - "<<in;


        cout<<"\n";
        out.clear();
        cin>>out;
        cin.get();

        //if we typed something, send it
        if(strlen(out.c_str())) {
            numSent = send(sock, out.c_str(), strlen(out.c_str()), 0);
            cout<<"\n"<<numSent<<" bytes sent";
        }

    }   //end while


    cout<<"\nExiting normally\n";
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

每当我运行它时,服务器仍然等待我发送一些东西,然后它才会读取和输出客户端发送的内容。我希望服务器或客户端能够在我输入消息后立即发送消息,并让另一个读取并在那时输出消息。我认为非阻塞套接字是答案,但也许我只是做错了什么?

另外,我使用文件而不是我的 127.0.0.1 地址作为 sockaddr 的数据。如果这不是它应该如何正确使用,请随意说(它是如何工作的,它以前是如何处理文件的,所以我只是保持原样)。

任何帮助表示赞赏。

Nik*_*sov 5

您希望同时处理多个连接的 TCP 服务器的一般方法:

  • 使侦听套接字非阻塞
  • 将其添加到select(2)读取事件集poll(2)
  • 输入select(2)/poll(2)循环
  • 唤醒时检查它是否是侦听套接字,然后
    • accept(2)
    • 检查失败(客户端现在可能已经放弃了连接尝试)
    • 使新创建的客户端套接字非阻塞,将其添加到轮询事件集中
  • 否则,如果它是客户端套接字之一
    • 消耗输入,处理它
    • 注意EAGAIN错误代码 - 这不是真正的错误,而是表明现在没有输入
    • 如果读取零字节 - 客户端关闭连接,close(2)客户端套接字,将其从事件集中删除
  • 重新初始化事件集(省略这是一个常见错误select(2)
  • 重复循环

客户端稍微简单一点,因为您只有一个套接字。但是,处理许多连接的高级应用程序(如 Web 浏览器)通常不会阻塞connect(2)