簡體   English   中英

BSD Sockets - 如何使用非阻塞 sockets?

[英]BSD Sockets - How to use non-blocking sockets?

我正在嘗試使用非阻塞 TCP sockets。 問題是他們仍然在阻塞。 代碼如下 -

服務器代碼 -

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

每當我運行它時,服務器仍然等待我發送一些東西,然后它才會讀取客戶端發送的內容和 output。 我希望服務器或客戶端能夠在我鍵入消息后立即發送消息,並讓另一個讀取並在當時 output 消息。 我認為非阻塞 sockets 是答案,但也許我只是做錯了什么?

另外,我使用文件而不是我的 127.0.0.1 地址作為 sockaddr 的數據。 如果這不是正確使用它的方式,請隨意這么說(它以前使用文件的方式工作,所以我就這樣保留它)。

任何幫助表示贊賞。

您希望同時處理多個連接的 TCP 服務器的一般方法:

  • 使偵聽套接字非阻塞
  • 將其添加到select(2)poll(2)讀取事件集
  • 進入select(2) / poll(2)循環
  • 在喚醒時檢查它是否是監聽套接字,然后
    • accept(2)
    • 檢查失敗(客戶端現在可能已經放棄了連接嘗試)
    • 使新創建的客戶端套接字非阻塞,將其添加到輪詢事件集中
  • 否則,如果它是客戶端 sockets 之一
    • 消耗輸入,處理它
    • 注意EAGAIN錯誤代碼 - 這不是真正的錯誤,但表明現在沒有輸入
    • 如果讀取零字節 - 客戶端關閉連接, close(2)客戶端套接字,將其從事件集中刪除
  • 重新初始化事件集(省略這是select(2)的常見錯誤)
  • 重復循環

客戶端稍微簡單一些,因為您只有一個套接字。 處理許多連接的高級應用程序(例如 web 瀏覽器)通常會執行非阻塞connect(2)

我認為您必須盡快設置非阻塞(即獲取套接字然后將其設置為非阻塞)

還要檢查設置它的 fcntl 是否實際工作

每當我運行它時,服務器仍然等待我發送一些東西,然后它才會讀取客戶端發送的內容和 output。

嗯,你就是這么寫的。 您從標准輸入阻止 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() 感興趣。 在我看來,非阻塞 sockets 通常是一個 hack,正確使用 select() 或 poll() 通常是更好的設計和更靈活(並且更便攜)。 嘗試

人 select_tut

了解更多信息。

如果你想要非阻塞 i/o,你想使用 select。 您可以使用 stdin 將其設置為它正在偵聽的 sockets (只需將文件描述符 1,即 stdin,添加到 fd_set)之一。

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

我建議閱讀 beej 對 select 的評價。 它看起來有點嚇人,但如果您花一點時間來了解它,它確實非常有用且易於使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM