3

我正在尝试使用非阻塞 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;
}

客户端代码 -

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;
}

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

另外,我使用文件而不是我的 127.0.0.1 地址作为 sockaddr 的数据。如果这不是正确使用它的方式,请随意这么说(它以前使用文件的方式工作,所以我就这样保留它)。

任何帮助表示赞赏。

4

4 回答 4

5

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

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

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

于 2011-07-14T21:35:56.090 回答
3

我认为您必须尽快设置非阻塞(即获取套接字然后将其设置为非阻塞)

还要检查设置它的 fcntl 是否实际工作

于 2011-07-14T20:37:04.870 回答
3

每当我运行它时,服务器仍然等待我发送一些东西,然后它才会读取并输出客户端发送的内容。

嗯,你就是这么写的。您阻止来自标准输入的 IO,然后才进行发送/接收。

cin>>out;
cin.get();

此外,您正在使用本地套接字(AF_UNIX),它在您的文件系统中创建一个特殊文件以进行进程间通信 - 这是与 IP 不同的机制,并且绝对不是您在问题中指出的 TCP。我您可以将文件命名为127.0.0.1,但这确实没有意义,并且意味着您会感到困惑,因为这是一个 IP 环回地址。您需要将 AF_INET 用于 IP。

对于 unix 网络的优秀入门指南,我推荐http://beej.us/guide/bgnet/

如果您希望接收到的消息的显示与您的 cin 语句无关,请 fork() 关闭一个单独的进程来处理您的网络 IO,或者使用单独的线程。

您可能对 select() 感兴趣。在我看来,非阻塞套接字通常是一种 hack,正确使用 select() 或 poll() 通常是更好的设计和更灵活(更便携)。尝试

人 select_tut

了解更多信息。

于 2011-07-14T20:52:54.173 回答
0

如果你想要非阻塞 i/o,你想使用 select。您可以使用标准输入将其设置为它正在侦听的套接字之一,以及客户端套接字(只需将文件描述符 1,即标准输入,添加到 fd_set)。

http://beej.us/guide/bgnet/output/html/multipage/advanced.html

我建议阅读 beej 关于选择的内容。它看起来有点吓人,但如果您花一点时间来了解它,它确实非常有用且易于使用。

于 2011-07-14T21:11:09.613 回答