socket通信之bind函数

bind函数的原型如下:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

使用如下:

... ...
// bind port
struct sockaddr_in bindaddr;
bindaddr.sin_family = AF_INET;
bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bindaddr.sin_port = htons(3000);
if(-1 == bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr))) {printf("bind error");return -1;
}
... ...

bind函数地址

bind的地址我们使用了一个宏叫INADDR_ANY,关于这个宏的解释如下:

如果应用程序不关心bind绑定的ip地址,可以使用INADDR_ANY,这样底层的(协议栈)服务会自动选择一个合适的ip地址,这样使在一个有多个网卡机器上选择ip地址问题变得简单。

也就是说INADDR_ANY相当于地址0.0.0.0。假设我们在一台机器上开发一个服务器程序,使用bind函数时,我们有多个ip地址可以选择。首先,这台机器对外访问的ip地址是120.55.94.78,这台机器在当前局域网的地址是 192.168.1.104;同时这台机器有本地回环地址127.0.0.1

如果你指向本机上可以访问,那么你bind函数中的地址就可以使用127.0.0.1(INADDR_LOOPBACK);如果你的服务只想被局域网内部机器访问,bind函数的地址可以使用192.168.1.104;如果希望这个服务可以被公网访问,你就可以使用地址0.0.0.0或INADDR_ANY。

ip地址10.0.4.129在代码中需要写成0x0a000481,将ip地址转换为一个uint32_t类型的数字。

bindaddr.sin_addr.s_addr = htonl(0x0a000481);

bind函数端口号

网络通信程序的基本逻辑是客户端连接服务器,即从客户端的地址:端口连接到服务器地址:端口上,在上面的例子中,服务器端的端口号使用3000,那客户端连接时的端口号是多少呢?TCP通信双方中一般服务器端端口号是固定的,而客户端端口号是连接发起时由操作系统随机分配的(不会分配已经被占用的端口)。端口号是一个C short类型的值,其范围是0~65535,知道这点很重要,所以我们在编写压力测试程序时,由于端口数量的限制,在某台机器上网卡地址不变的情况下压力测试程序理论上最多只能发起六万五千多个连接。注意我说的是理论上,在实际情况下,由于当时的操作系统很多端口可能已经被占用,实际可以使用的端口比这个更少,例如,一般规定端口号在1024以下的端口是保留端口,不建议用户程序使用。

如果将bind函数中的端口号设置成0,那么操作系统会随机给程序分配一个可用的侦听端口,当然服务器程序一般不会这么做,因为服务器程序是要对外服务的,必须让客户端知道确切的ip地址和端口号。

很多人觉得只有服务器程序可以调用bind函数绑定一个端口号,其实不然,在一些特殊的应用中,我们需要客户端程序以指定的端口号去连接服务器,此时我们就可以在客户端程序中调用bind函数绑定一个具体的端口。

我们用代码来实际验证一下上面所说的,为了能看到连接状态,我们将客户端和服务器关闭socket的代码注释掉,这样连接会保持一段时间。

客户端代码不绑定端口

服务器端代码如下:

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>int main() {// create socketint listenfd = socket(AF_INET, SOCK_STREAM, 0);if(-1 == listenfd) {printf("create socket error");return -1;}// bind port struct sockaddr_in bindaddr;bindaddr.sin_family = AF_INET;bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);bindaddr.sin_port = htons(3000);if(-1 == bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr))) {printf("bind error");return -1;}// start listenif (listen(listenfd, 2) == -1) {printf("listem error");return -1;}while (1) {struct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);// accept connectionint clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);}//close socketclose(listenfd);return 0;
}

客户端代码如下:

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>int main() {// create socketint clientfd = socket(AF_INET, SOCK_STREAM, 0);if(-1 == clientfd) {printf("create socket error");return -1;}// connect server struct sockaddr_in serveraddr;serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");;serveraddr.sin_port = htons(3000);if(-1 == connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) {printf("connect error");return -1;}// send dataint ret = send(clientfd, "hello", strlen("hello"), 0);if (ret != strlen("hello")){printf("send data error");return -1;}// receive datachar recvBuf[32] = {0};ret = recv(clientfd, recvBuf, 32, 0);if (ret > 0) {printf("receive data from server: %s", recvBuf);} else {printf("receive data error: %s", recvBuf);}sleep(30);return 0;
}

先启动server,再启动三个客户端。然后通过lsof命令查看当前机器上的TCP连接信息,结果如下所示:

# lsof -i -Pn
COMMAND     PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
server.ou 59672  root    3u  IPv4 414148      0t0  TCP *:3000 (LISTEN)
server.ou 59672  root    4u  IPv4 414149      0t0  TCP 127.0.0.1:3000->127.0.0.1:7492 (ESTABLISHED)
server.ou 59672  root    5u  IPv4 414150      0t0  TCP 127.0.0.1:3000->127.0.0.1:7493 (ESTABLISHED)
server.ou 59672  root    6u  IPv4 414155      0t0  TCP 127.0.0.1:3000->127.0.0.1:7494 (ESTABLISHED)
client.ou 59675  root    3u  IPv4 413364      0t0  TCP 127.0.0.1:7492->127.0.0.1:3000 (ESTABLISHED)
client.ou 59683  root    3u  IPv4 414154      0t0  TCP 127.0.0.1:7493->127.0.0.1:3000 (ESTABLISHED)
client.ou 59690  root    3u  IPv4 414159      0t0  TCP 127.0.0.1:7494->127.0.0.1:3000 (ESTABLISHED)

上面的结果显示,server进程(进程ID是59672)在3000端口开启侦听,有三个client进程(进程ID分别是59675、59683、59690)分别通过端口号7492、7493、7494连到server进程上的,作为客户端的一方,端口号是系统随机分配的。

客户端绑定端口号0

服务器端代码保持不变,客户端代码在connect前添加如下代码:

struct sockaddr_in bindaddr;
bindaddr.sin_family = AF_INET;
bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
// bind 0
bindaddr.sin_port = htons(0);
if (bind(clientfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)
{printf("bind error");return -1;
}

我们再次编译客户端程序,并启动三个client进程,然后用lsof命令查看机器上的TCP连接情况,结果如下所示:

# lsof -i -Pn
COMMAND     PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
server.ou 60108  root    3u  IPv4 414240      0t0  TCP *:3000 (LISTEN)
server.ou 60108  root    4u  IPv4 414241      0t0  TCP 127.0.0.1:3000->127.0.0.1:7495 (ESTABLISHED)
server.ou 60108  root    5u  IPv4 414242      0t0  TCP 127.0.0.1:3000->127.0.0.1:7496 (ESTABLISHED)
server.ou 60108  root    6u  IPv4 414246      0t0  TCP 127.0.0.1:3000->127.0.0.1:7497 (ESTABLISHED)
client.ou 60111  root    3u  IPv4 413401      0t0  TCP 127.0.0.1:7495->127.0.0.1:3000 (ESTABLISHED)
client.ou 60113  root    3u  IPv4 414245      0t0  TCP 127.0.0.1:7496->127.0.0.1:3000 (ESTABLISHED)
client.ou 60115  root    3u  IPv4 414249      0t0  TCP 127.0.0.1:7497->127.0.0.1:3000 (ESTABLISHED)

通过上面的结果,我们发现三个client进程使用的端口号仍然是系统随机分配的,也就是说绑定0号端口和没有绑定效果是一样的。

客户端绑定一个固定端口

服务器端代码保持不变,客户端代码中绑定一个固定端口20000:

// bind 20000
bindaddr.sin_port = htons(20000);

再次重新编译程序,先启动一个客户端后,我们看到此时的TCP连接状态:

# lsof -i -Pn
server.ou 60412  root    3u  IPv4 414277      0t0  TCP *:3000 (LISTEN)
server.ou 60412  root    4u  IPv4 414278      0t0  TCP 127.0.0.1:3000->127.0.0.1:20000 (ESTABLISHED)
client.ou 60415  root    3u  IPv4 414279      0t0  TCP 127.0.0.1:20000->127.0.0.1:3000 (ESTABLISHED)

通过上面的结果,我们发现client进程确实使用20000号端口连接到server进程上去了。这个时候如果我们再开启一个client进程,我们猜想由于端口号20000已经被占用,新启动的client会由于调用bind函数出错而退出,我们实际验证一下:

# ./client.out
bind error

结果确实和我们预想的一样。

另外,Linux的nc命令有个-p选项,这个选项的作用就是nc在模拟客户端程序时,可以使用指定端口号连接到服务器程序上去,实现原理相信读者也明白了。我们还是以上面的服务器程序为例,这个我们不用我们的client程序,改用 nc命令来模拟客户端:

# nc -v -p 30000 127.0.0.1 3000
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to 127.0.0.1:3000.
... ...

-p选项指定客户端绑定的端口号,-v选项表示输出nc命令连接的详细信息。

我们用lsof命令来验证一下我们的 nc 命令是否确实以30000端口号连接到server进程上去了。

# lsof -i -Pn
COMMAND     PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
server.ou 60412  root    3u  IPv4 414277      0t0  TCP *:3000 (LISTEN)
server.ou 60412  root    5u  IPv4 414280      0t0  TCP 127.0.0.1:3000->127.0.0.1:30000 (ESTABLISHED)
nc        60590  root    3u  IPv4 413493      0t0  TCP 127.0.0.1:30000->127.0.0.1:3000 (ESTABLISHED)

结果确实如我们期望的一致。

socket通信之bind函数相关推荐

  1. socket通信之listen函数

    socket通信之listen函数 listen函数原型如下: #include <sys/types.h> #include <sys/socket.h>int listen ...

  2. socket 通信关于bind那点事

    结论: 1.采用TCP通信时,客户端不需要bind()他自己的IP和端口号,而服务器必须要bind()自己本机的IP和端口号; 2.若采用UDP通信时(这里是有客户端和服务器之分才这么说的,若是指定特 ...

  3. 操作系统实验报告9:进程间通信—管道和 socket 通信

    操作系统实验报告9 实验内容 实验内容:进程间通信-管道和 socket 通信. 编译运行课件 Lecture11 例程代码: alg.11-3-socket-input-2.c alg.11-4-s ...

  4. socket通信函数的深入分析

    平时我们可能会用 socket 通信做个作业交给老师,一般情况下,都是拷贝一段代码,ip 地址改下,端口号改下也就可以了,即使是会写 socket 通信程序,甚至是使用 socket 做一个文件传输的 ...

  5. socket不能bind请求的地址_深入浅出讲解:php的socket通信

    对TCP/IP.UDP.Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵.那么我想问: 什么是TCP/IP.UDP? Socket在哪里呢? Socket是什么呢? 你 ...

  6. socket通信函数

    TCP协议 - CSDN博客 https://blog.csdn.net/coolwriter/article/details/79178950 1.网络中进程之间如何通信? 本地的进程间通信(IPC ...

  7. socket通信简介(概念、函数、原理)

    "一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信 ...

  8. Socket通信bind错误

    Socket非正常退出导致的bind错误 在Vx下进行Socket无连接通信时,由于在发送消息进程中使用ctrl+c强制中止,导致下一次再进行发送时会出现bind()出现错误.原因在于非正常退出时,没 ...

  9. socket通信常用函数

    转自网上资料 accept(接受socket连线) 相关函数 socket,bind,listen,connect 表头文件 #include<sys/types.h> #include& ...

最新文章

  1. 技术图文:如何实现 DataTable 与模型类 List 的相互转换?
  2. java学习与总结:计算机网络
  3. 初学c++基础知识——第一个c++程序
  4. java项目_好程序员Java分享从入门到服务端项目开发的过程
  5. 几时几分几秒怎么写_头总是一阵一阵眩晕是怎么回事?眩晕症该如何治疗?
  6. AFNetworking下 http 改 https后遇到出现Error Domain=NSURLErrorDomain Code=-999 已取消 错误...
  7. 循环在c语言中的表示什么作用,《C语言中的for循环》教案
  8. python创建一个有序链表_Python实现合并两个有序链表的方法示例
  9. Codeforces Round #352 (Div. 1) B. Robin Hood
  10. Java Android 代码片段收集
  11. linux中删除用户显示已登录,linux下用户及用户组:查看,新增,删除
  12. 为什么苏联打下了如此强的数学基础,俄罗斯却至今无法成为AI强国?
  13. flash spi 野火_野火stm32-SPI
  14. kaggle之共享单车案例
  15. QNAP 威联通 NAS的个人使用经验 篇二:QTS系统各功能讲解
  16. 解决TOC与目录导航冲突问题
  17. sklearn专题四:降维算法
  18. Chapter007-FPGA学习之IIC总线EEPROM读取
  19. 高速相机在企业生产领域的应用效果
  20. 好程序员Python培训分享Python程序员面试技巧

热门文章

  1. mac一直ReportCrash
  2. java中的算法(一致性hash算法和数据结构的问题)
  3. [Leetcode学习-c++java]Count Sorted Vowel Strings
  4. lr 1 语法分析器c语言,LR语法分析器
  5. C语言高墙高,院子里有两棵树。因为有高墙庇护,一棵树长得高大挺直。而另一棵...
  6. 显示器分辨率一直跳_台式机的屏幕总是闪烁是怎么回事 怎么调电脑分辨率
  7. 升级Big Sur系统后指纹解锁出现问题怎么办
  8. 群晖moments套件识别拍摄日期为1970错误的解决方法
  9. 逻辑强化(04)真假推理 答案解析
  10. selenium自动获取王者荣耀英雄海报并保存到本地