【问题标题】:socket connect() vs bind()套接字连接()与绑定()
【发布时间】:2015-01-16 20:37:58
【问题描述】:

connect()bind() 系统调用都将套接字文件描述符“关联”到一个地址(通常是一个 ip/端口组合)。他们的原型是这样的:-

int connect(int sockfd, const struct sockaddr *addr,
               socklen_t addrlen);

int bind(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);

两次调用之间的确切区别是什么?什么时候应该使用connect(),什么时候应该使用bind()

具体来说,在一些示例服务器客户端代码中,发现客户端正在使用connect(),而服务器正在使用bind() 调用。原因对我来说并不完全清楚。

【问题讨论】:

  • 一句话:bind是本地地址,connect是远程地址。

标签: c sockets network-programming


【解决方案1】:

为了更好地理解,让我们来看看到底是哪里绑定和连接的,

进一步定位两个调用,如 Sourav 所述,

bind() 将套接字与其本地地址相关联[这就是服务器端绑定的原因,以便客户端可以使用该地址连接到服务器。] connect() 用于连接到远程 [server] 地址,这就是为什么在客户端,使用 connect [read as: connect to server]。

由于特定的角色和相应的实现,我们不能互换使用它们(即使我们在同一台机器上有客户端/服务器)。

我将进一步建议关联这些调用 TCP/IP 握手。

所以,谁会在这里发送 SYN,它将是 connect()。而 bind() 用于定义通信端点。

希望这会有所帮助!

【讨论】:

  • 谢谢兄弟。有了图表,一切都可以快速理解。如果我们使用 udp,您能说出这里有什么区别吗?
  • accept()
    应移到
    块下方,直到来自客户端的连接
  • 我认为 p2p 网络中的所有节点都应该使用绑定,对吗?
  • 这张图很有帮助!谢谢
【解决方案2】:

一班人: bind() 到自己的地址,connect() 到远程地址。

引用bind()的手册页

bind() 将 addr 指定的地址分配给文件描述符 sockfd 引用的套接字。 addrlen 指定 addr 指向的地址结构的大小(以字节为单位)。传统上,此操作称为“为套接字分配名称”。

并且,来自connect()的相同

connect() 系统调用将文件描述符 sockfd 引用的套接字连接到 addr 指定的地址。

为了澄清,

  • bind() 将套接字与其本地地址相关联[这就是为什么 服务器端binds,以便客户端可以使用该地址进行连接 到服务器。]
  • connect() 用于连接远程 [server] 地址,即 为什么是客户端,使用connect [读作:连接到服务器]。

【讨论】:

  • 那么,比如说,如果服务器和客户端进程都运行在同一台机器上,它们可以互换使用吗?
  • @SiddharthaGhosh 不。也许客户端和服务器在同一台机器上,但它们仍然不同的进程,对吧?这两个 API 都有自己的用途。他们从来不是interchangeable
  • 在这种情况下,本地和远程究竟是什么意思?
  • @SiddharthaGhosh local -> 进程本身,remote --> 另一个进程。
  • @SouravGhosh 所以这意味着我无法在客户端指定要绑定的端口?
【解决方案3】:

bind 告诉正在运行的进程声明一个端口。也就是说,它应该将自己绑定到端口 80 并侦听传入的请求。使用绑定,您的进程将成为服务器。当你使用 connect 时,你告诉你的进程连接到一个已经在使用的端口。您的流程成为客户。区别很重要:bind 想要一个未使用的端口(以便它可以声明它并成为服务器),而 connect 想要一个已经在使用的端口(因此它可以连接到它并与服务器通信)

【讨论】:

    【解决方案4】:

    如果您将connect()listen() 视为对应物,而不是connect()bind(),我认为这将有助于您理解。这样做的原因是您可以在两者之前调用或省略bind(),尽管在connect() 之前调用它或者在listen() 之前不调用它很少是一个好主意。

    如果从服务器和客户端的角度来考虑的话,listen() 是前者的标志,connect() 是后者。 bind() 可以找到 - 或找不到 - 在任何一个上。

    如果我们假设我们的服务器和客户端在不同的机器上,那么理解各种功能就会变得更容易。

    bind() 在本地起作用,也就是说,它将调用它的机器上的连接末端绑定到请求的地址,并将请求的端口分配给您。无论该机器是客户端还是服务器,它都会这样做。 connect() 启动到服务器的连接,也就是说它从客户端连接到服务器上请求的地址和端口。该服务器几乎肯定会在listen() 之前调用bind(),以便您能够知道使用connect() 连接到它的地址和端口。

    如果您不调用bind(),当您调用connect()(客户端)或listen()(服务器)时,将在本地计算机上为您隐式分配和绑定端口和地址。然而,这是两者的副作用,而不是它们的目的。以这种方式分配的端口是临时的。

    这里很重要的一点是客户端不需要绑定,因为客户端连接到服务器,因此即使您使用的是临时端口,服务器也会知道客户端的地址和端口,而不是绑定到具体的东西。另一方面,虽然服务器可以在不调用bind() 的情况下调用listen(),但在这种情况下,它们需要发现分配给它们的临时端口,并将其传达给它想要连接到它的任何客户端。

    我假设当您提到connect() 时,您对 TCP 感兴趣,但这也会延续到 UDP,在第一个 sendto() 之前不调用 bind()(UDP 是无连接的)也会导致端口和要隐式分配和绑定的地址。一个你不能在没有绑定的情况下调用的函数是recvfrom(),它会返回一个错误,因为没有分配的端口和绑定地址,就没有什么可以接收(或者太多,取决于你如何解释没有绑定)。

    【讨论】:

      【解决方案5】:

      来自维基百科http://en.wikipedia.org/wiki/Berkeley_sockets#bind.28.29

      connect():

      connect() 系统调用将由其文件描述符标识的套接字连接到由该主机在参数列表中的地址指定的远程主机。

      某些类型的套接字是无连接的,最常见的是用户数据报协议套接字。对于这些套接字,connect 具有特殊含义:发送和接收数据的默认目标设置为给定地址,从而允许在无连接套接字上使用 send() 和 recv() 等函数。

      connect() 返回一个表示错误代码的整数:0 表示成功,-1 表示错误。

      绑定():

      bind() 将一个套接字分配给一个地址。当使用 socket() 创建一个套接字时,它只被赋予了一个协议族,而没有分配一个地址。这种与地址的关联必须通过 bind() 系统调用执行,然后套接字才能接受到其他主机的连接。 bind() 接受三个参数:

      sockfd,一个描述符,表示要在其上执行绑定的套接字。 my_addr,指向 sockaddr 结构的指针,表示要绑定到的地址。 addrlen,一个 socklen_t 字段,指定 sockaddr 结构的大小。 Bind() 成功时返回 0,发生错误时返回 -1。

      示例: 1.)使用连接

      #include <stdio.h>
      #include <sys/socket.h>
      #include <netinet/in.h>
      #include <string.h>
      
      int main(){
        int clientSocket;
        char buffer[1024];
        struct sockaddr_in serverAddr;
        socklen_t addr_size;
      
        /*---- Create the socket. The three arguments are: ----*/
        /* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
        clientSocket = socket(PF_INET, SOCK_STREAM, 0);
      
        /*---- Configure settings of the server address struct ----*/
        /* Address family = Internet */
        serverAddr.sin_family = AF_INET;
        /* Set port number, using htons function to use proper byte order */
        serverAddr.sin_port = htons(7891);
        /* Set the IP address to desired host to connect to */
        serverAddr.sin_addr.s_addr = inet_addr("192.168.1.17");
        /* Set all bits of the padding field to 0 */
        memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);  
      
        /*---- Connect the socket to the server using the address struct ----*/
        addr_size = sizeof serverAddr;
        connect(clientSocket, (struct sockaddr *) &serverAddr, addr_size);
      
        /*---- Read the message from the server into the buffer ----*/
        recv(clientSocket, buffer, 1024, 0);
      
        /*---- Print the received message ----*/
        printf("Data received: %s",buffer);   
      
        return 0;
      }
      

      2.)绑定示例:

      int main()
      {
          struct sockaddr_in source, destination = {};  //two sockets declared as previously
          int sock = 0;
          int datalen = 0;
          int pkt = 0;
      
          uint8_t *send_buffer, *recv_buffer;
      
          struct sockaddr_storage fromAddr;   // same as the previous entity struct sockaddr_storage serverStorage;
          unsigned int addrlen;  //in the previous example socklen_t addr_size;
          struct timeval tv;
          tv.tv_sec = 3;  /* 3 Seconds Time-out */
          tv.tv_usec = 0;
      
          /* creating the socket */         
          if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) 
              printf("Failed to create socket\n");
      
          /*set the socket options*/
          setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval));
      
          /*Inititalize source to zero*/
          memset(&source, 0, sizeof(source));       //source is an instance of sockaddr_in. Initialization to zero
          /*Inititalize destinaton to zero*/
          memset(&destination, 0, sizeof(destination));
      
      
          /*---- Configure settings of the source address struct, WHERE THE PACKET IS COMING FROM ----*/
          /* Address family = Internet */
          source.sin_family = AF_INET;    
          /* Set IP address to localhost */   
          source.sin_addr.s_addr = INADDR_ANY;  //INADDR_ANY = 0.0.0.0
          /* Set port number, using htons function to use proper byte order */
          source.sin_port = htons(7005); 
          /* Set all bits of the padding field to 0 */
          memset(source.sin_zero, '\0', sizeof source.sin_zero); //optional
      
      
          /*bind socket to the source WHERE THE PACKET IS COMING FROM*/
          if (bind(sock, (struct sockaddr *) &source, sizeof(source)) < 0) 
              printf("Failed to bind socket");
      
          /* setting the destination, i.e our OWN IP ADDRESS AND PORT */
          destination.sin_family = AF_INET;                 
          destination.sin_addr.s_addr = inet_addr("127.0.0.1");  
          destination.sin_port = htons(7005); 
      
          //Creating a Buffer;
          send_buffer=(uint8_t *) malloc(350);
          recv_buffer=(uint8_t *) malloc(250);
      
          addrlen=sizeof(fromAddr);
      
          memset((void *) recv_buffer, 0, 250);
          memset((void *) send_buffer, 0, 350);
      
          sendto(sock, send_buffer, 20, 0,(struct sockaddr *) &destination, sizeof(destination));
      
          pkt=recvfrom(sock, recv_buffer, 98,0,(struct sockaddr *)&destination, &addrlen);
          if(pkt > 0)
              printf("%u bytes received\n", pkt);
          }
      

      我希望能澄清区别

      请注意,您声明的套接字类型将取决于您的要求,这一点非常重要

      【讨论】:

        【解决方案6】:

        太长了;不要阅读:区别在于是否设置了源(本地)或目标地址/端口。简而言之,bind() 设置源,connect() 设置目标。无论是 TCP 还是 UDP。

        bind()

        bind() 设置套接字的本地(源)地址。这是接收数据包的地址。由套接字发送的数据包携带此作为源地址,因此其他主机将知道将其数据包发送回哪里。

        如果不需要接收,则套接字源地址是无用的。像 TCP 这样的协议需要启用接收才能正确发送,因为目标主机会在一个或多个数据包到达时发回确认(即确认)。

        connect()

        • TCP 处于“已连接”状态。 connect() 触发 TCP 代码尝试建立到另一端的连接。
        • UDP 没有“连接”状态。 connect() 仅在未指定地址时将默认地址设置为发送数据包的地址。当不使用connect() 时,必须使用sendto()sendmsg() 包含目标地址。

        connect()或者一个send函数被调用,并且没有绑定地址时,Linux会自动将socket绑定到一个随机端口。有关技术细节,请查看 Linux 内核源代码中的inet_autobind()

        旁注

        • listen() 仅适用于 TCP。
        • AF_INET 系列中,套接字的源或目标地址 (struct sockaddr_in) 由 IP 地址(参见 IP header)和 TCP 或 UDP 端口(参见 TCP 和 @ 987654324@ 标头)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-02-27
          • 1970-01-01
          • 1970-01-01
          • 2014-06-30
          • 1970-01-01
          • 2020-08-06
          • 2010-12-01
          相关资源
          最近更新 更多