12

我正在使用 Berkeley 套接字(包括:Internet 域和 Unix 域),我想知道服务器是否可以使用相同的套接字来读取请求并将响应写入客户端。或者客户端应该创建另一个套接字来等待重播,服务器在处理收到的消息后连接到它。

顺便说一句,我说的是面向连接的套接字(流套接字,TCP,...)。

这是简化的服务器代码(为了简单起见,我在这里省略了对系统调用的错误检查):

int main() {

    int server_socket, connected_socket;
    struct sockaddr_in server_addr;
    char buf[1024];
    char aux[256];
    int bytes_read;

    server_socket = socket(AF_INET, SOCK_STREAM, 0);    

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(1234);
    bind(server_socket, &server_addr, sizeof(server_addr))

    listen(server_socket, 5)

    connected_sodket = accept(server_socket, 0, 0);
    do {
        bzero(buf, sizeof(buf));
        bytes_read = read(connected_socket, buf, sizeof(buf));        
    } while (bytes_read > 0);          

    /* Here I want to use connected_socket to write the reply, can I? */

    close(connected_socket);       

    close(server_socket);

    return (EXIT_SUCCESS);
}

这是简化的客户端代码(为了简单起见,我在这里省略了对系统调用的错误检查):

int main() {

    int client_socket;
    struct sockaddr_in server_addr;

    client_socket = socket(AF_INET, SOCK_STREAM, 0);

    hp = gethostbyname("myhost");
    server_addr.sin_family = AF_INET;
    memcpy(&server_addr.sin_addr, hp->h_addr_list[0], hp->h_length);
    server_addr.sin_port = htons(1234);

    connect(client_socket, &server_addr, sizeof(server_addr));

    write(client_socket, MSG, sizeof(MSG));

    /* Here I want to wait for a response from the server using client_socket, can I? */

    close(client_socket);

    return (EXIT_SUCCESS);
}

我可以connected_socket在服务器和client_socket客户端中使用来传回响应消息吗?或者我应该使用“接受”时在服务器中获得的客户端地址连接到客户端中的套接字?

我尝试在显示评论的客户端/服务器中使用读/写,但这样两个程序都被阻止,这似乎是一个死锁。

提前感谢ins!问候。

4

9 回答 9

10

应该使用相同的套接字!

您的应用程序协议明确定义了客户端和服务器何时应该等待数据或相互发送消息;假设一个协议只有一个来自客户端的请求和一个来自服务器的响应,以下应该成立:


  • 客户端与服务器建立连接;
  • 客户端发送其请求(使用 send());
  • 客户端通过协议知道服务器会回复;因此它在同一个套接字上等待数据(recv());
  • 验证响应后,客户端可以关闭套接字。

  • 服务器接受来自客户端的连接;
  • 服务器知道第一步取决于客户端,因此它等待数据(recv());
  • 服务器验证请求;
  • 服务器现在从协议中知道客户端正在等待数据;因此它使用 send() 发送响应;
  • 服务器从协议中知道没有进一步的步骤;因此它可以关闭套接字。
于 2009-01-21T23:07:16.137 回答
8

您可以使用相同的套接字,但您的程序设置为让服务器在尝试回复之前读取客户端发送的所有内容。因此,在客户端关闭其套接字的写入端之前,服务器中的循环不会完成,因此服务器会获得 EOF(读取 0 字节),因此服务器将永远不会发回其响应。

有几种方法可以解决这个问题。

  1. 您可以在看到整个请求后中断服务器中的循环,而不是读取直到 EOF。这要求客户端发送的数据以某种方式自定界,以便服务器可以知道它何时读取所有数据。
  2. 您可以使用第二个连接进行回复。可能不是最好的。
  3. 您可以使用套接字的不对称关闭。让客户端shutdown(client_socket, SHUT_WR)半关闭套接字。然后服务器将看到 EOF(并且循环将完成),但套接字上的另一个方向仍将打开以进行回复。
于 2009-01-21T23:29:38.993 回答
3

是的,这是可能的。查看此页面以获取简单服务器(和简单客户端)的示例。请注意,服务器通常将“接受”文件描述符传递给新进程,以便它可以继续侦听更多传入连接

于 2009-01-21T23:04:04.333 回答
2

您不仅应该使用相同的套接字(如 Federico 所说),实际上还必须使用相同的套接字才能让返回的数据包通过防火墙。

防火墙知道 TCP 连接,如果防火墙内的机器启动了连接,它会自动允许返回的数据通过。相反,如果您尝试从外部创建一个新的 TCP 套接字,防火墙将阻止它,除非该流量被特别允许。

于 2009-01-21T23:31:13.467 回答
1

是的,SOCK_STREAM 套接字是双向的。您应该能够在连接的每一侧读取和写入相同的套接字。手册页socket(2)对此有更多详细信息。

于 2009-01-21T23:01:12.747 回答
1

是的你可以。TCP 套接字是双向的。只需使用相同的 read() 和 write() 函数。还要确保检查对 connect()、read()、write() 等的所有调用的错误情况,因为您无法控制网络上发生的情况。

于 2009-01-21T23:01:31.903 回答
1

对不起; 我没有说,但我确实像这样尝试过

注释所在的服务器中的此代码:

write(connected_socket, "Ok, I got it", sizeof("Ok, I got it"));

以及注释所在的客户端中的此代码:

read(client_socket, buf, sizeof(buf));

两个程序都被阻止,当我杀死客户端时,服务器会显示它收到的消息(在服务器调用read之后我有一个 printf )。

我尝试使用 send 和 recv (都带有 0 标志)而不是 read 和 write 并且它没有改变。

于 2009-01-21T23:14:52.733 回答
1

在您当前的设置中,服务器会尝试读取,直到客户端关闭套接字,而客户端在服务器应答之前不会关闭套接字。因此,您有一种“僵局”。服务器没有停下来读取,希望有更多的数据到达,而客户端无法告诉服务器它已经完成了。

您需要某种方式让服务器识别请求已完成,而连接仍处于打开状态。例如,如果您通过换行符终止您的请求,服务器可以检查它是否收到完整的行,然后停止阅读并发送答案。

于 2009-01-21T23:26:42.103 回答
0

你试过了吗?当您实际将代码放在指示的位置时,它看起来应该可以工作

于 2009-01-21T23:00:42.297 回答