【问题标题】:How do you keep a socket connection open indefinitely in C?如何在 C 中无限期地保持套接字连接打开?
【发布时间】:2014-06-02 11:36:33
【问题描述】:

我正在尝试使用 Beej 的套接字指南中的代码在 Linux 中实现 C 套接字服务器,该指南位于此处:

http://beej.us/guide/bgnet/examples/server.c

这行得通,我已经用 C# 编写了一个 Windows 客户端来与它通信。一旦客户端连接,我让它向服务器发送一个字节数组,服务器读取它,然后发回一个字节数组。这行得通。

但是,在此之后,如果我让客户端尝试发送另一个字节数组,我会收到一个 Windows 弹出窗口,提示“已建立的连接已被主机中的软件中止。”然后我必须再次与客户端重新连接。我想无限期地保持连接打开,直到客户端发送断开连接命令,但是尽管阅读了 Beej 的指南,但我似乎没有得到它。目前我什至没有尝试实现断开连接命令,我只是尝试保持连接打开,直到我关闭服务器。

我已经尝试删除 Beej 代码中的 close() 调用:

while(1) {  // main accept() loop
    sin_size = sizeof their_addr;
    new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
    if (new_fd == -1) {
        perror("accept");
        continue;
    }

    inet_ntop(their_addr.ss_family,
        get_in_addr((struct sockaddr *)&their_addr),
        s, sizeof s);
    printf("server: got connection from %s\n", s);

    if (!fork()) { // this is the child process
        close(sockfd); // child doesn't need the listener
        ProcessRequest(new_fd); // this is not Beej's code, I've replaced his code here (which was a simple string send()) with a function call that does a read() call, processes some data, then sends back a byte array to the client using send().
        close(new_fd);
        exit(0);
    }
    close(new_fd);  // parent doesn't need this
}

但这只是让我陷入“套接字接受:错误的文件描述符”的无限循环(我尝试同时删除 close(new_fd) 行和同时删除行,以及 close(sockfd) 行。 任何更精通 C 套接字编程的人都可以给我一个提示我应该在哪里寻找吗?谢谢。

【问题讨论】:

  • 您确定服务器已断开连接吗?我不确定从客户端抛出的消息是否表明对等关闭。 Wireshark 捕获应该可以确认这一点。

标签: c sockets


【解决方案1】:

原因

客户端面临错误的原因是服务器父级或服务器子级的close(new_fd)

解决方案

在任何时候,服务器都可能收到两种事件:

  1. 来自新客户端的连接请求
  2. 来自现有客户端的数据 服务器必须尊重他们两个。有两种(主要)方法来处理这个问题。

解决方法一

将服务器设计为并发服务器。在 Beej 的指南中是

7.2. select()—Synchronous I/O Multiplexing

http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#select 由于OP的方法不是这个,所以我们不再进一步探索。

解决方法2

在服务器上,fork() 每个客户端一个进程。这是 OP 采用的方法,我们在这里进行探索。本质上,它是对 OP 代码中的 ProcessRequest() 函数进行微调。这是一个草图。

void ProcessRequest( int new_fd ) {
    char buffer[ N ];
    for( ; ; ) {     // infinite loop until client disconnects or some error

        int const recvLen = recv( new_fd, buffer, sizeof buffer, 0 );
        if( recvLen == 0 ) { break; }    // client disconnected
        else if( recvLen == -1 ) { perror( "recv" ); break; }

        int const sendLen = send( new_fd, buffer, recvLen, 0 );
        if( sendLen == -1 ) { perror( "send" ); break; }
        // TODO if( sendLen < recvLen ) then send() in loop
    }
}

注意

我很抱歉四个小时后得到了半生不熟的解决方案。在我编辑答案时,我失去了与 stackoverflow.com 的连接,持续了几个小时。

【讨论】:

  • 恐怕不行。当我第二次尝试从客户端向服务器发送数据时,我仍然会断开连接。
  • 真的没有必要把它弄得这么复杂,因为服务器已经在每个连接上分叉了。 ProcessRequest 只需要一个循环(根据@EJP 的回答)。
  • @AndrewMedico:你说得对,我正在编辑答案,当时我失去了与 SO 的连接几个小时。请查看更新后的答案并随时发表评论。
【解决方案2】:

accept() 问题的原因是 sockfd 无效。你一定是在某个地方关闭了它。请注意,如果您遇到此类错误,您不应该只是继续重试,就好像它没有发生一样。

客户端问题的原因是您只在 ProcessRequest() 中处理一个请求,正如其名称所暗示的那样,正如您在评论中所描述的那样。使用循环,读取请求直到 recv() 返回零或发生错误。

【讨论】:

  • 我不打算投反对票,但我知道人们会怎么说这不能回答问题。
  • 可能,但是在第一次迭代中发送/接收是如何工作的?
  • 但这是否意味着如果客户端暂停发送请求,recv 将返回 0?客户端不会不断地发送请求,只有当客户端的用户明确提出请求时。另外,我以为它已经在一个while循环中了?
  • @mrrrow 不,不会。当对等方关闭连接时,recv() 返回零。不是以前。它在一个 while 循环中接受连接并处理来自每个连接的单个响应。他需要另一个。
  • 非常感谢!一个简单的 do/while (res>0) 循环围绕 ProcessRequest 并更改 void ProcessRequest 以将整数值返回到 'res' (int res;res = ProcessRequest(new_fd)) 就可以了。现在这完全有道理。非常感谢您的帮助。
猜你喜欢
  • 2012-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-25
  • 1970-01-01
  • 1970-01-01
  • 2012-11-23
相关资源
最近更新 更多