套接字连接()与绑定()

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

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()调用。我不是很清楚原因。

172613 次浏览

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

引自 bind()的手册页

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

从同样的 connect()

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

澄清一下,

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

来自 Wikipedia 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)使用 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);
}

我希望这能澄清两者的区别

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

为了更好地理解,让我们找出确切的绑定和连接进入图片,

索拉夫澄清了两通电话的位置,

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

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

我将进一步推荐将这些调用与 TCP/IP 握手相关联。

enter image description here

因此,谁将在这里发送 SYN,它将是 connect ()。Bind ()用于定义通信端点。

希望这个能帮上忙!

Bind 告诉正在运行的进程声明一个端口。即,它应该将自己绑定到端口80并侦听传入请求。使用 bind,您的进程将成为一个服务器。当您使用 connect 时,您告诉您的进程连接到一个已经在使用的端口。你的程序就成了客户。区别很重要: bind 需要一个不在使用中的端口(这样它就可以声明它并成为一个服务器) ,connect 需要一个已经在使用中的端口(这样它就可以连接到它并与服务器通信)

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

如果从服务器和客户端的角度来考虑有帮助的话,那么 listen()是前者的标志,而 connect()是后者的标志。bind()可以找到,也可以找不到。

如果我们假设服务器和客户机位于不同的机器上,那么理解各种函数就会变得更加容易。

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

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

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

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

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

bind()

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

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

connect()

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

当调用 connect()或发送函数时,如果没有绑定地址,Linux 会自动将套接字绑定到一个随机端口。有关技术细节,请参阅 Linux 内核源代码中的 inet_autobind()

旁注

  • listen()只是 TCP。
  • AF _ INET系列中,套接字的源地址或目标地址(struct sockaddr_in)由一个 IP 地址(参见 IP 头)和 TCP 或 UDP 端口(参见 TCPUDP头)组成。