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

 

.

posted @   wellons  阅读(186)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示