套接字connect()vs bind()


121

两者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);

2个通话之间的确切区别是什么?什么时候应该使用connect(),什么时候使用bind()

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


19
一句话:bind是本地地址,connect是远程地址。
SHR 2014年

Answers:


230

为了更好地理解,让我们找出确切的绑定和连接出现在哪里,

Sourav明确指出,除了定位两个电话外,

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

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

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

在此处输入图片说明

因此,谁将在此处发送SYN,它将是connect()。而bind()用于定义通信端点。

希望这可以帮助!!


1
多谢兄弟。使用该图,所有内容都可以快速理解。如果我们使用的是udp,您能告诉我们有什么区别吗?
2015年

8
accept()<br>应该移到<br>块下,直到客户端连接
tschodt

我认为p2p网络中网络中的所有节点都应使用绑定,对吗?
kapil

46

一线: bind()到自己的地址,connect()到远端的地址。

从的手册页引用 bind()

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

并且,从相同的 connect()

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

澄清,

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

因此,如果服务器和客户端进程都在同一台计算机上运行,​​是否可以互换使用?
Siddhartha Ghosh 2014年

1
@SiddharthaGhosh号。也许客户端和服务器在同一台机器上,但是它们仍然不同的进程,对吗?两种API都有自己的用途。他们从不interchangeable
Sourav Ghosh 2014年

在这种情况下,本地和远程到底是什么意思?
Siddhartha Ghosh 2014年

@SiddharthaGhosh- local>进程本身remote->其他进程
Sourav Ghosh,2014年

@SouravGhosh,这意味着我无法指定要在客户端绑定到的端口?
陈恒琪

12

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


9

从Wikipedia http://en.wikipedia.org/wiki/Berkeley_sockets#bind.28.29

connect():

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

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

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

bind():

bind()将套接字分配给一个地址。使用socket()创建套接字时,只会为其分配协议族,而不会分配地址。在套接字可以接受到其他主机的连接之前,必须使用bind()系统调用执行与地址的这种关联。bind()采用三个参数:

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

示例:1.)使用Connect

#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);
    }

我希望可以澄清差异

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


9

我认为,如果您将connect()listen()视为对等,而不是connect()和,将对您的理解有所帮助bind()。这样做的原因是可以先调用或省略bind(),尽管先调用connect()或不先调用都不是个好主意listen()

如果有助于从服务器和客户端方面进行思考,那么listen()这就是前者和connect()后者的标志。bind()可以在任一上找到-或找不到。

如果我们假设服务器和客户端位于不同的计算机上,则更容易理解各种功能。

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

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

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

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


1

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

bind()

bind()设置套接字的本地(源)地址。这是接收数据包的地址。套接字发送的数据包将其作为源地址,因此另一台主机将知道将其数据包发回何处。

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

connect()

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

connect()调用或发送函数且未绑定地址时,Linux自动将套接字绑定到随机端口。有关技术细节,请查看inet_autobind()Linux内核源代码。

旁注

  • listen() 仅TCP。
  • AF_INET系列中,套接字的源地址或目标地址(struct sockaddr_in)由IP地址(请参阅IP标头)和TCP或UDP端口(请参阅TCPUDP标头)组成。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.