udp通讯中的connect()和bind()函数

本文收录于微信公众号「 LinuxOK 」,ID为:Linux_ok,关注公众号第一时间获取更多技术学习文章。

udp是一个基于无连接的通讯协议,通讯基本模型如下:
这里写图片描述

可以看出,不论是在客户端还是服务器,connect()似乎用不上,bind()在客户端也用不上,但是事实并非如此。

####1. udp客户端使用connect()函数####
udp客户端建立了socket后可以直接调用sendto()函数向服务器发送数据,但是需要在sendto()函数的参数中指定目的地址/端口,但是可以调用connect()函数先指明目的地址/端口,然后就可以使用send()函数向目的地址发送数据了,因为此时套接字已经包含目的地址/端口,也就是send()函数已经知道包含目的地址/端口。

//用sendto()函数发送数据的udp客户端程序
int main(int argc, char *argv[])
{
	int sd;
	struct sockaddr_in svr_addr;
	int ret;
	socklen_t addrlen = sizeof(struct sockaddr_in);
	char buf[BUFSZ] = {};
	
	if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		perror("socket");
		exit(EXIT_FAILURE);
	}
	
	//sendto()函数需要指定目的端口/地址
	svr_addr.sin_family = AF_INET;
	svr_addr.sin_port = htons(PORT);
	svr_addr.sin_addr.s_addr = inet_addr("192.168.1.166");
	
	while (1)
	{	
		memset(buf, 0, BUFSZ);
		printf("ple input: ");
		fgets(buf, BUFSZ, stdin);
		sendto(sd, buf, BUFSZ, 0, (struct sockaddr* )&svr_addr, addrlen);
		
		ret = recvfrom(sd, buf, BUFSZ, 0, (struct sockaddr* )&svr_addr, &addrlen);
		printf("client: IPAddr = %s, Port = %d, buf = %s\n", inet_ntoa(svr_addr.sin_addr), ntohs(svr_addr.sin_port), buf);	
	}
	
	close(sd);	
	return 0;
}

send()函数发送数据的udp客户端程序:

//用send()函数发送数据的udp客户端程序
int main(int argc, char *argv[])
{
	int sd;
	struct sockaddr_in svr_addr;
	int ret;
	socklen_t addrlen = sizeof(struct sockaddr_in);
	char buf[BUFSZ] = {};
	
	if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		perror("socket");
		exit(EXIT_FAILURE);
	}
	
	//先调用connect()函数,为套接字指定目的地址/端口
	svr_addr.sin_family = AF_INET;
	svr_addr.sin_port = htons(PORT);
	svr_addr.sin_addr.s_addr = inet_addr("192.168.1.166");
	connect(sd, (struct sockaddr* )&svr_addr, addrlen);
	
	while (1)
	{	
		memset(buf, 0, BUFSZ);
		printf("ple input: ");
		fgets(buf, BUFSZ, stdin);
		//sendto(sd, buf, BUFSZ, 0, (struct sockaddr* )&svr_addr, addrlen);
		send(sd, buf, BUFSZ, 0);
		
		ret = recvfrom(sd, buf, BUFSZ, 0, (struct sockaddr* )&svr_addr, &addrlen);
		printf("client: IPAddr = %s, Port = %d, buf = %s\n", inet_ntoa(svr_addr.sin_addr), ntohs(svr_addr.sin_port), buf);	
	}
	
	close(sd);	
	return 0;
}

####2. udp客户端程序使用bind()函数####
udp服务器调用了bind()函数为服务器套接字绑定本地地址/端口,这样使得客户端的能知道它发数据的目的地址/端口,服务器如果单单接收客户端的数据,或者先接收客户端的数据(此时通过recvfrom()函数获取到了客户端的地址信息/端口)再发送数据,客户端的套接字可以不绑定自身的地址/端口,因为udp在创建套接字后直接使用sendto(),隐含操作是,在发送数据之前操作系统会为该套接字随机分配一个合适的udp端口,将该套接字和本地地址信息绑定。
但是,如果服务器程序就绪后一上来就要发送数据给客户端,那么服务器就需要知道客户端的地址信息和端口,那么就不能让客户端的地址信息和端口号由客户端所在操作系统分配,而是要在客户端程序指定了。怎么指定,那就是用bind()函数:

//为客户端绑定端口和地址信息
int main(int argc, char *argv[])
{
	int sd;
	struct sockaddr_in svr_addr, cli_addr;
	int ret;
	socklen_t addrlen = sizeof(struct sockaddr_in);
	char buf[BUFSZ] = {};
	
	if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		perror("socket");
		exit(EXIT_FAILURE);
	}
	
	//绑定地址信息
	cli_addr.sin_family = AF_INET;
	cli_addr.sin_port = htons(9693);
	cli_addr.sin_addr.s_addr = 0;
	if ((ret = bind(sd, (struct sockaddr* )&cli_addr, addrlen)) < 0)
	{
		perror("bind");
		exit(EXIT_FAILURE);
	}
	
	svr_addr.sin_family = AF_INET;
	svr_addr.sin_port = htons(PORT);
	svr_addr.sin_addr.s_addr = inet_addr("192.168.1.166");
	
	while (1)
	{	
		memset(buf, 0, BUFSZ);
		printf("ple input: ");
		fgets(buf, BUFSZ, stdin);
		sendto(sd, buf, BUFSZ, 0, (struct sockaddr* )&svr_addr, addrlen);
		
		ret = recvfrom(sd, buf, BUFSZ, 0, (struct sockaddr* )&svr_addr, &addrlen);
		printf("client: IPAddr = %s, Port = %d, buf = %s\n", inet_ntoa(svr_addr.sin_addr), ntohs(svr_addr.sin_port), buf);	
	}
	
	close(sd);
	
	return 0;
}

###3. udp服务器程序使用connect()函数####
如上所述,connect()函数可以用来指明套接字的目的地址/端口号,那么若udp服务器可以使用connect,将导致服务器只接受这特定一个主机的请求。

<think>嗯,用户现在问的是TCPUDPbind的作用区别。之前他们问过Python的recvfrom用法,现在可能是在深入理解网络编程中的socket相关概念。我需要先回忆一下bind函数在两种协议中的使用场景不同点。 首先,bind函数的作用是将socket绑定到一个特定的地址端口上,这样其他主机才能通过这个地址端口进行通信。无论是TCP还是UDPbind的基本作用应该是一样的,就是绑定本地端点。但根据协议的不同,可能在使用上有一些区别。 比如,TCP是面向连接的,通常服务器端需要先bind一个端口,然后listenaccept客户端的连接。而客户端通常不需要显式调用bind,系统会自动分配端口。但是在UDP中,不管是服务器还是客户端,如果想要接收数据,可能需要bind,因为UDP是无连接的,发送接收数据报需要明确的地址。 不过,用户可能想知道更详细的区别,比如是否必须调用bind,在两种协议中的必要性如何。另外,可能还要提到多网卡情况下的绑定,比如绑定到0.0.0.0特定IP的区别。 另外,之前用户的问题涉及到recvfrom,是UDP的方法,所以他们在学习网络编程,可能对TCPUDP的区别有一定了解,但现在具体到bind的作用区别,需要更详细的解释。 需要注意的误区是,TCP客户端是否需要bind。一般情况下,客户端不需要调用bindconnect时会自动分配端口,但如果有特殊需求,比如固定客户端端口,也可以调用bind。而UDP中,如果只是发送数据,可以不bind,但接收的话必须bind,否则系统随机分配端口,对方无法回传数据。 可能还需要比较TCPUDP在使用bind时的典型代码结构,比如TCP服务器需要bind后listen,而UDP服务器bind后直接recvfrom。此外,端口重用的问题,比如SO_REUSEADDR选项在TCPUDP中的不同表现。 需要确保回答结构清晰,分点说明作用区别,可能用表格对比更直观。同时,用户之前的问题得到过代码示例,这次可能也需要提供一些代码片段说明不同情况下的bind使用,比如TCP服务器绑定,UDP服务器绑定,以及客户端的情况。 另外,用户可能想知道bind在不同协议中的异常情况,比如端口被占用时的处理,或者多线程下同时bind的问题。不过可能这些属于高级话题,但作为基础,应该先解释清楚基本作用区别。 总结下来,回答的结构应该是先解释bind的作用,再分别说明在TCPUDP中的应用场景注意事项,然后对比两者的区别,最后可能加上常见问题或示例代码。</think>--- ### **TCP 与 UDP 中 `bind()` 的作用** `bind()` 方法用于将 **socket 绑定到特定的本地地址(IP + 端口)**,使其他设备能够通过该地址与当前程序通信。其核心作用如下: - **定义通信端点**:明确本机哪个网卡(IP)端口接收/发送数据 - **固定端口**(可选):避免系统随机分配端口,便于其他程序定向连接 - **多网卡选择**:指定监听特定网络接口(如`0.0.0.0`监听所有网卡,`127.0.0.1`仅限本机) --- ### **TCP UDP 使用 `bind()` 的区别** | **场景** | **TCP** | **UDP** | |------------------------|----------------------------------------|----------------------------------------| | **服务器端** | 必须显式调用`bind()`绑定端口 | 必须显式调用`bind()`绑定端口 | | **客户端** | 通常无需`bind()`,由系统自动分配端口 | 若需接收回复,需`bind()`固定端口 | | **典型代码结构** | `bind()` → `listen()` → `accept()` | `bind()` → `recvfrom()`/`sendto()` | | **端口复用** | 需设置`SO_REUSEADDR`避免`TIME_WAIT`冲突| 可直接复用端口 | --- ### **具体使用场景** #### **1. TCP 中的 `bind()`** - **服务器端**:必须绑定固定端口供客户端连接 ```python # TCP 服务器示例 import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('0.0.0.0', 8080)) # 绑定所有网卡的8080端口 server.listen() ``` - **客户端**:通常不调用`bind()`,由系统分配临时端口 ```python # TCP 客户端(自动分配端口) client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(('server_ip', 8080)) # 系统自动选择本地端口 ``` #### **2. UDP 中的 `bind()`** - **服务器端**:必须绑定端口以接收数据 ```python # UDP 服务器示例 import socket server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server.bind(('0.0.0.0', 8888)) data, addr = server.recvfrom(1024) # 接收数据 ``` - **客户端**:若需接收回复,需显式绑定端口 ```python # UDP 客户端(绑定固定端口) client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) client.bind(('0.0.0.0', 54321)) # 明确本地端口 client.sendto(b"Hello", ('server_ip', 8888)) ``` --- ### **关键区别总结** 1. **必要性**: - TCP 服务器必须`bind()`,客户端通常不绑定 - UDP 服务器必须`bind()`,客户端若需接收数据也应绑定 2. **端口冲突**: - TCP 因存在连接状态(如`TIME_WAIT`),需设置`SO_REUSEADDR`以复用端口 - UDP 无连接状态,可直接复用端口 3. **通信模式**: - TCP 的`bind()`是建立连接的前提 - UDP 的`bind()`仅确定数据接收端点 --- ### **常见问题** #### **Q1: 不调用`bind()`会怎样?** - **TCP**:客户端由系统随机分配端口,服务器不绑定则无法监听 - **UDP**:发送数据时系统分配临时端口,但无法接收回复(除非对方知道你的随机端口) #### **Q2: 如何绑定所有可用网卡?** 使用 `0.0.0.0` 作为IP: ```python sock.bind(('0.0.0.0', 8080)) # 监听所有网络接口 ``` #### **Q3: 如何解决`Address already in use`错误?** 对 TCP socket 设置端口复用: ```python sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('0.0.0.0', 8080)) ``` --- ### **总结** - `bind()` 的本质是为 socket **明确本地通信端点**,其必要性取决于协议类型角色(服务端/客户端) - TCP 依赖`bind()`建立连接通道,UDP 依赖`bind()`确定数据接收点 - 实际开发中,UDP 客户端若需双向通信,通常需显式绑定端口
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值