【问题标题】:Unix socket loopsUnix 套接字循环
【发布时间】:2013-10-17 00:08:33
【问题描述】:

我正在尝试实现一对能够通过 Unix 套接字交换信息的服务器/客户端程序。问题是客户端不断被服务器重置并循环重新运行,并且在第一次之后我无法向套接字发送/接收数据。现在我有这样的东西:

服务器:

int main(int argc, char const *argv[]){
    unix_socket server;
    server.initSocket(DEFAULT_SOCKET_PATH,SERVER_MODE);
    server.wait();

    bool first=true;

    for (int i = 0; i < 30; ++i)
    {

            //send & receive data

        server.closeSocket();
        first=false;
    }

    return 0;
}

客户:

int loop_controller(){
  unix_socket client;
  client.initSocket(DEFAULT_SOCKET_PATH,CLIENT_MODE);

  // receive & send data

  client.closeSocket();
}


int main () {

  for (int i = 0; i < 30; ++i){
    loop_controller();
  }  

  return 0;
}

unix_socket 类的 initSocket()wait()closeSocket() 成员(服务器模式 0,模式 1对于客户):

void unix_socket::initSocket(const char* sock_path, const int sc_mode){

    if (sc_mode==0){
        if (mode != sc_mode) mode=sc_mode;
        if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            //exit(1);
        }
        // ---

        local.sun_family = AF_UNIX;
        strcpy(local.sun_path, sock_path);
        unlink(local.sun_path);
        len = strlen(local.sun_path) + sizeof(local.sun_family);
        if (bind(sock, (struct sockaddr *)&local, len) == -1) {
            close(sock);
            perror("bind"); 
                    //exit(1);
        }
        // ---

    } else if(sc_mode==1) {
        if (mode != sc_mode) mode=sc_mode;

        if ((client_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            //exit(1);
        }
        // ---
    remote.sun_family = AF_UNIX;
        strcpy(remote.sun_path, sock_path);
        len = strlen(remote.sun_path) + sizeof(remote.sun_family);
        if (connect(client_sock, (struct sockaddr *)&remote, len) == -1) {
            perror("connect");
            //exit(1);
        }
        // ---
    } else printf("Invalid sc_mode argument: %d\n",sc_mode);
}
//...

void unix_socket::wait(){
    if (mode==0){
        if (listen(sock, 5) == -1) {
            perror("listen");
            //exit(1);
        }
        t = sizeof(remote);
        if ((client_sock = accept(sock, (struct sockaddr *)&remote, &t)) == -1) {
            perror("accept");
            //exit(1);
        }
    } else printf("Invalid call to wait(): mode %d\n, must be 0",mode);
}
//...

void unix_socket::closeSocket(){
    //close(sock);
    close(client_sock); 
}
//...

但是,当使用 Valgrind 运行这两个程序时,我收到了 SIGPIPE 错误:

==5384== 
==5384== Process terminating with default action of signal 13 (SIGPIPE)
==5384==    at 0x573DDA2: send (send.c:28)
==5384==    by 0x405910: unix_socket::sendMsg(char, double) (in  .../server)
==5384==    by 0x405B6C: unix_socket::sendVectorXd(Eigen::Matrix<double, -1, 1, 0, -1, 1> const&) (in .../server)
==5384==    by 0x402044: main (in .../server)
==5384== 

我猜在关闭/重新打开套接字时某些事情没有正确完成。什么是正确的程序?

【问题讨论】:

  • 在服务器端,您所说的重新打开是在哪里?我所看到的只是循环的第一遍结束。
  • 如果您尝试写入已经关闭(并且阻塞?)的套接字,您将获得SIGPIPE。大多数应用程序可以很好地使用signal(SIGPIPE, SIG_IGN) [尽管不要从您的库中这样做] 来忽略此类信号
  • 此外,您的perror() 调用应在操作失败后立即进行。当您执行close(sock) 时,errnoclose() 设置以指示其成功或失败,因此您将获得由close() 设置的errno 而不是bind()

标签: c++ sockets


【解决方案1】:

在您的循环中,您只accept()ing 一次。循环体的结尾表示客户端套接字已关闭,但它永远不会在下一次迭代中再次accept()ed。

你的循环应该是这样的:

server.startListening(); // bind, listen and friends

for (int i = 0; i < 30; i++) {
    server.acceptClient(); // Call accept
    // insert byte pushing routines here..
    server.closeSocket(); // call close() on socket returned by accept()
}

【讨论】:

  • 每次客户调用close()时,我是否还需要再次listen?我是否还需要关闭服务器套接字(在服务器端)
  • 我在每个循环结束时关闭套接字时出现随机(无法复制)SIGPIPE 错误,但如果我不关闭套接字或只是忽略@,一切正常987654327@错误,你知道这是为什么吗?
  • 不,你只需要听一次。 listen() 告诉内核为当前套接字打开一个接受队列。 accept() 基本上会从该队列中弹出下一个项目
  • 作为应用程序,您应该处理SIGPIPE。您可能还希望检查accept 等的返回值。您可能仍在尝试在关闭的套接字上执行 I/O,或者更糟的是,关闭不同的 FD