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

发布于 2024-11-24 02:50:03 字数 3695 浏览 13 评论 0原文

我正在尝试使用非阻塞 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 的数据。如果这不是正确使用它的方式,请随意这么说(它的工作方式与以前处理文件的方式相同,所以我就这样保留它)。

任何帮助表示赞赏。

I am trying to use non-blocking TCP sockets. The problem is that they are still blocking. The code is below -

server code -

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

client code -

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

Whenever I run it, the server still waits for me to send something before it will read and output what the client has sent. I want either the server or client to be able to send the message as soon as I type it, and have the other read and output the message at that time. I thought non-blocking sockets was the answer, but maybe I am just doing something wrong?

Also, I was using a file instead of my 127.0.0.1 address as the sockaddr's data. If that is not how it should be properly used, feel free to say so (it worked how it worked previously with a file so I just kept it like that).

Any help is appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

幸福%小乖 2024-12-01 02:50:03

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

  • 使监听套接字成为非阻塞,
  • 将其添加到 select(2)民意调查(2) read 事件集
  • 进入 select(2)/poll(2) 唤醒
  • 循环检查是否是监听套接字,然后
    • 接受(2)< /代码>
    • 检查是否失败(客户端现在可能已放弃连接尝试)
    • 使新创建的客户端套接字成为非阻塞,并将其添加到轮询事件集中
  • 如果它是客户端套接字之一,则将其添加到其他 轮询事件集中
    • 使用输入并进行处理
    • 留意EAGAIN< /code>错误代码 - 这并不是真正的错误,而是表示现在没有输入
    • 如果读取零字节 - 客户端关闭连接,close(2) 客户端套接字,将其从事件集中删除
  • 重新初始化事件集(忽略这是 select(2) 的常见错误)
  • 重复循环

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

General approach for a TCP server where you want to handle many connections at the same time:

  • make listening socket non-blocking
  • add it to select(2) or poll(2) read event set
  • enter select(2)/poll(2) loop
  • on wakeup check if it's the listening socket, then
    • accept(2)
    • check for failure (the client might've dropped the connection attempt by now)
    • make newly created client socket non-blocking, add it to the polling event set
  • else, if it's one of the client sockets
    • consume input, process it
    • watch out for EAGAIN error code - it's not really an error, but indication that there's no input now
    • if read zero bytes - client closed connection, close(2) client socket, remove it from event set
  • re-init event set (omitting this is a common error with select(2))
  • repeat the loop

Client side is a little simpler since you only have one socket. Advanced applications like web browsers that handle many connections often do non-blocking connect(2) though.

我很坚强 2024-12-01 02:50:03

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

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

cin>>out;
cin.get();

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

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

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

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

man select_tut

了解更多信息。

Whenever I run it, the server still waits for me to send something before it will read and output what the client has sent.

Well, that is how you wrote it. You block on IO from stdin, and then and only then do you send/receive.

cin>>out;
cin.get();

Also, you are using a local socket (AF_UNIX) which creates a special file in your filesystem for interprocess communication - this is a different mechanism than IP, and is definitely not TCP as you indicate in your question. I suppose you could name the file 127.0.0.1, but that really doesn't make sense and implies confusion on your part, because that is an IP loopback address. You'll want to use AF_INET for IP.

For an excellent starter guide on unix networking, I'd recommend http://beej.us/guide/bgnet/

If you want the display of messages received to be independant of your cin statements, either fork() off a seperate process to handle your network IO, or use a separate thread.

You might be interested in select(). In my opinion non-blocking sockets are usually a hack, and proper usage of select() or poll() is generally much better design and more flexible (and more portable). try

man select_tut

for more information.

疧_╮線 2024-12-01 02:50:03

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

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

I think you have to set non-block sooner (ie get the socket then set it non block)

also check that the fcntl to set it actually worked

洛阳烟雨空心柳 2024-12-01 02:50:03

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

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

我建议阅读 beej 关于 select 的说法。它看起来有点吓人,但如果您花一点时间了解它,它确实很有用且易于使用。

If you want non-blocking i/o, you want to use select. You can set it with stdin as one of the sockets it is listening on, along with the client sockets (just add file descriptor 1, which is stdin, to the fd_set).

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

I would recommend reading through what beej has to say about select. It looks a little intimidating but is really useful and simple to use if you take a little time to wrap your head around it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文