服务器是否可以使用相同的套接字将响应发送给客户端?怎么样?

时间:2009-01-21 22:54:49

标签: c deadlock sockets

我正在使用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来传回响应消息吗?或者,当我在“接受”中连接到客户端的套接字时,我应该使用服务器中的客户端地址吗?

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

谢谢你提前! 问候。

9 个答案:

答案 0 :(得分:10)

应该使用相同的套接字!

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


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

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

答案 1 :(得分:8)

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

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

  1. 您可以在看到整个请求后打破服务器中的循环,而不是直到EOF才能读取。这要求客户端发送的数据以某种方式自行分隔,因此服务器可以知道它何时读取它。
  2. 您可以使用第二个连接进行回复。可能不是最好的。
  3. 您可以使用套接字的非对称关闭。让客户端shutdown(client_socket, SHUT_WR)半关闭套接字。然后服务器将看到EOF(并且循环将完成),但套接字上的另一个方向仍将打开以进行回复。

答案 2 :(得分:3)

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

答案 3 :(得分:2)

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

防火墙了解TCP连接,并且如果防火墙内的计算机启动了连接,则自动允许返回数据通过。如果您尝试从外部创建新的TCP套接字,防火墙将阻止它,除非特别允许该流量。

答案 4 :(得分:1)

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

答案 5 :(得分:1)

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

答案 6 :(得分:1)

抱歉;我没有说,但我确实试过这个

评论所在的服务器中的此代码:

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

以及评论所在的客户端中的此代码:

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

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

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

答案 7 :(得分:1)

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

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

答案 8 :(得分:0)

你试过吗?当你实际把代码放在指示的地方时,它看起来应该有效。