socket bind学习
转https://blog.csdn.net/qq_43403759/article/details/114727885
如果tcp一端没有绑定套接字的话,那么在调用connec(客户)t或listen(服务器)的时候,内核会自动分配端口绑定,且会自动绑定本机的IP地址给套接字sockfd。在《UNIX网络编程》这本书中提到:“如果一个TCP客户或者服务器未曾调用bind捆绑一个端口,当调用connect或listen时,内核就要为相应的套接字选择一个临时接口。”从这句话中可以判断出,其实在调用socket函数创建socket时,内核还并未给socket分配源地址和源端口。而对于UDP,我猜测在调用sendto发送数据时,在未捆绑端口的情况下,内核也会随机分配端口。其实bind对于源地址也同样具备这种处理方式,当系统具有多IP(多网卡)的情况,当我们把bind函数中的ip参数置0时,就是由内核自己选择分配IP。当bind的参数中端口地址为0的时候,这时候就是由内核分配端口。这样我就不用考虑端口地址重复的问题,而放心的把这个问题交给内核处理了。神奇的INADDR_ANY它的值其实就是0。所以当我们只有单一IP的时候,我们就可以用INADDR_ANY去代替那个单一的IP,因为内核分配的时候只能选择这一个IP。从而造成了INADDR_ANY就是本机IP的现象。
1.
函数的声明: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
(1)参数 sockfd ,需要绑定的socket。
(2)参数 addr ,存放了服务端用于通信的地址和端口。
(3)参数 addrlen ,表示 addr 结构体的大小
(4)返回值:成功则返回0 ,失败返回-1,错误原因存于 errno 中。如果绑定的地址错误,或者端口已被占用,bind 函数一定会报错,否则一般不会返回错误。
2.函数的作用: 服务端用于将把用于通信的地址和端口绑定到 socket 上。所以可以猜出,这个函数的参数应该包含:用于通信的 socket 和服务端的 IP 地址和端口号。ip地址和端口号是放在 socketaddr_in 结构体里面的。
3.服务端示例代码
#include <stdio.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <netdb.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <arpa/inet.h>
9 int main(int argc,char *argv[])
10 {
11 if (argc!=2)
12 {
13 printf("Using:./server port\nExample:./server 5005\n\n"); return -1;
14 }
15 // 第1步:创建服务端的socket。
16 int listenfd;
17 if ( (listenfd = socket(AF_INET,SOCK_STREAM,0))==-1)
18 {
19 perror("socket"); return -1;
20 }
21 // 第2步:把服务端用于通信的地址和端口绑定到socket上。
22 struct sockaddr_in servaddr; // 服务端地址信息的数据结构。
23 memset(&servaddr,0,sizeof(servaddr));
24 servaddr.sin_family = AF_INET; // 协议族,在socket编程中只能是AF_INET。
25 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 任意ip地址。 //servaddr.sin_addr.s_addr = inet_addr("192.168.190.134"); // 指定ip地址。
26 servaddr.sin_port = htons(atoi(argv[1])); // 指定通信端口。
27 if (bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 )
28 {
29 perror("bind");
30 close(listenfd);
31 return -1;
32 }
33
34 // 第3步:把socket设置为监听模式。
35 if (listen(listenfd,5) != 0 )
36 {
37 perror("listen");
38 close(listenfd);
39 return -1;
40 }
41 // 第4步:接受客户端的连接。
42 int clientfd; // 客户端的socket。
43 int socklen=sizeof(struct sockaddr_in); // struct sockaddr_in的大小
struct sockaddr_in clientaddr; // 客户端的地址信息。
clientfd=accept(listenfd,(struct sockaddr *)&clientaddr,(socklen_t*)&socklen);
44 printf("客户端(%s)已连接。\n",inet_ntoa(clientaddr.sin_addr)); // 第5步:与客户端通信,接收客户端发过来的报文后,回复ok。
45 char buffer[1024];
46 while (1)
47 {
48 int iret;
49 memset(buffer,0,sizeof(buffer));
50 if ( (iret=recv(clientfd,buffer,sizeof(buffer),0))<=0) // 接收客户端的请求报文。
51 {
52 printf("iret=%d\n",iret); break;
53 }
54 printf("接收:%s\n",buffer);
55 strcpy(buffer,"ok");
56 if ( (iret=send(clientfd,buffer,strlen(buffer),0))<=0) // 向客户端发送响应结果。
57 {
58 perror("send");
59 break;
60 }
61 printf("发送:%s\n",buffer);
62 }
63 // 第6步:关闭socket,释放资源。
64 close(listenfd); close(clientfd);
65 }
.服务端 socket 的 SO_REUSEADDE 属性
1.服务端程序的端口释放后可能会处于 TIME_WAIT 状态(等待),要等待两分钟后才能被再次使用, 解决方法:设置 SO_REUSEADDE 选项,让端口释放后立即可以被再次使用。
2.设置 SO_REUSEADDE 选项,把这段代码写入服务端程序。
int opt = 1; unsigned int len = sizeof(opt);
setsockopt(listenfd,SOL_SOCKET,REUSEADDR,&opt,len);
.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?