0

C/C++ で書かれたサーバーがあります。接続のラッパーを次のように設定しました。

    //START WRAPPER
    void Server::init_address(int port)
    {
memset(&(this->serv_addr), 0, sizeof(this->serv_addr));
this->serv_addr.sin_family = AF_INET;
this->serv_addr.sin_port = htons(port);
this->serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    }


    int Server::w_socket()
    {
int retv;
retv = socket(PF_INET, SOCK_STREAM, 0);
//FIXME
//fcntl(retv, F_SETFL, O_NONBLOCK);
if(retv == -1)
{
    std::string err_msg(strerror(errno));
    err_msg = "[socket] " + err_msg;
    throw err_msg;
}
else
{
    int reuse_opt = 1;

    if(setsockopt(retv, SOL_SOCKET, SO_REUSEADDR, &reuse_opt, sizeof(int))==-1)
    {
        perror("setsockopt error");
        exit(1);
    }
    return retv;
}
    }

    void Server::w_bind()
    {
int retv;
retv = bind(this->sock_fd,
        (struct sockaddr*) &(this->serv_addr),
        sizeof(this->serv_addr));

if(retv == -1)
{
    std::string err_msg(strerror(errno));
    err_msg = "[bind] " + err_msg;
    throw err_msg;
}
    }

    void Server::w_listen()
    {
int retv;
retv = listen(this->sock_fd, 3);
if (retv == -1)
{
    std::string err_msg(strerror(errno));
    err_msg = "[listen] " + err_msg;
    throw err_msg;
}
    }

    int Server::w_accept(struct sockaddr_in* client_addr)
    {
int retv;
int socklen = sizeof(sockaddr_in);

retv = accept(this->sock_fd, (struct sockaddr*)client_addr, (socklen_t*)&socklen);
if(retv == -1)
{
    std::string err_msg(strerror(errno));
    err_msg = "[accept] " + err_msg;
    throw err_msg;
}
else
{
    return retv;
}
            }

    int Server::recvtimeout(int s, char *buf, int len, int timeout)
    {
fd_set fds;
int n;
struct timeval tv;
// set up the file descriptor set
FD_ZERO(&fds);
FD_SET(s, &fds);
// set up the struct timeval for the timeout
tv.tv_sec = timeout;
tv.tv_usec = 0;
// wait until timeout or data received
n = select(s+1, &fds, NULL, NULL, &tv);
if (n == 0){
    return -2; // timeout!
}
if (n == -1){
    return -1; // error
}
// data must be here, so do a normal recv()
return recv(s, buf, len, 0);
    }
    // END WRAPPER

私の目標は、ノンブロッキング ソケット モードを有効にすることです。fcntl(retv, F_SETFL, O_NONBLOCK);を実行しようとしました。Beej マニュアルが言ったように、しかし、私はエラーを受け取ります: [ accept] リソースを一時的に利用できませ ん。

したがって、この問題を解決してノンブロッキング ソケット モードを有効にする方法がわかりません。

4

2 に答える 2

5

ソケットが非ブロックであるため、エラーが発生します。

そのエラーが発生している間、ビジーループを実行します(またはのいずれかがいつ返されるかを確認しerrnoacceptください-1)。もう1つの推奨される解決策は、 を使用してソケットが読み取り可能になったことを確認し、 を呼び出すことです。EWOULDBLOCKEAGAINselectaccept

編集:それを行う方法select

リッスンしているソケットまたは接続されているソケットが読み取れるかどうかをチェックする、より高いレベルのイベント ループが必要です。リッスンしているソケットが読み取り可能な場合は、新しい接続を受け入れることができ、接続されたソケットが読み取り可能な場合は、そこから読み取ることができます。

何かのようなもの:

for (;;)
{
    fd_set readset;

    FD_ZERO(&readset); 
    FD_SET(listening_socket, &readset);
    int maxfd = listening_socket;

    if (connected_socket >= 0)
    {
        FD_SET(connected_socket, &readset);
        maxfd = max(maxfd, connected_socket);
    }

    // NULL timeout (5-th argument) means wait until event
    select(maxfd + 1, &readset, NULL, NULL, NULL);

    if (FD_ISSET(listening_socket, &readset))
    {
        accept_new_connection(listening_socket);
    }

    if (connected_socket >= 0 && FD_ISSET(connected_socket, &readset))
    {
        if (!read_from_socket(connected_socket))
        {
            close(connected_socket);
            connected_socket = -1;
        }
    }
}

複数のソケットが接続されている場合は、それらを単純なリンク リストに入れ、ループで追加/チェックします。それらが閉じられたときにリストから削除します。

于 2012-05-29T07:59:26.173 に答える
1

あなたのソリューションではselect、接続ソケットに call を使用していますが、 には同じものを使用していませんlisten socket

listen socketもノンブロッキングに変更した場合は、listen socketを呼び出す前にselect for を使用する必要がありますaccept

于 2012-05-29T08:05:01.793 に答える