简单分析一下socket中的bind

原文链接:http://www.cnblogs.com/nightwatcher/archive/2011/07/03/2096717.html

在最开始接触bind的时候,只是在写基于tcp的server端的时候,知道在listen之前需要先bind一下,用来确保socket能在某个固定的端口监听。而bind的时候,函数参数中的端口填自己将要绑定的端口就行;而IP地址,需要填本机的IP,但是也可以用一个宏INADDR_ANY代替,用这个宏就可以不用查找本机的IP,它就可以代替本机的IP。当时只觉得这个INADDR_ANY比较神奇,但是由于当时觉得用起来很方便,也没出啥问题,也就没有再深究。

  但是最近在做RTSP服务器的时候,有种特殊的应用,导致我不得不对bind这个函数仔细地看一下。
  我们知道无论是UDP还是TCP,socket都会与一个本地的IP和端口想对应,我们往往把这个IP和端口称之为socket的源地址和源端口。当我们作为客户端利用socket去发送数据时,很少会去考虑这个源地址和源端口到底是什么,我们更关心的是它的目的地址和端口。我们往往只有在监听的时候,才去考虑这个源端口,所以我们在监听的时候会去用bind。当我们bind之后,内核就会将这个socket的源端口锁定到我们设定的端口上。但是这就有一个问题,这个bind绑定端口,是将本来没有源端口的socket绑定到我们指定的端口上,还是将一个已经分配了端口的socket重定向到我们指定的端口上呢?
  在《UNIX网络编程》这本书中提到:“如果一个TCP客户或者服务器未曾调用bind捆绑一个端口,当调用connect或listen时,内核就要为相应的套接字选择一个临时接口。”从这句话中可以判断出,其实在调用socket函数创建socket时,内核还并未给socket分配源地址和源端口。而对于UDP,我猜测在调用sendto发送数据时,在未捆绑端口的情况下,内核也会随机分配端口。
  而我遇到的特殊应用要求我在用UDP发送数据之前要告诉对方我的发送端口,这也就意味着我在sendto之前必须要捆绑端口,因此我在发送数据之前就得调用bind函数绑定一下端口了。但是我就在想内核既然有随机分配端口的能力,而我需要的也只是让它绑定一下而不用绑定在固定端口的业务,socket中应该能够提供这种业务。然后果然我发现bind就具备这种能力,当bind的参数中端口地址为0的时候,这时候就是由内核分配端口。这样我就不用考虑端口地址重复的问题,而放心的把这个问题交给内核处理了。
  就在发现bind的这个机制的同时,我发现其实bind对于源地址也同样具备这种处理方式,当系统具有多IP(多网卡)的情况,当我们把bind函数中的ip参数置0时,就是由内核自己选择分配IP。而之前一直觉得很神奇的INADDR_ANY其实一点也不神奇,它的值其实就是0。所以当我们只有单一IP的时候,我们就可以用INADDR_ANY去代替那个单一的IP,因为内核分配的时候只能选择这一个IP。从而造成了INADDR_ANY就是本机IP的现象。
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编写简单的浏览器和 Web 服务器需要掌握以下技能: 1. 熟悉 HTTP 协议,了解请求和响应的格式和内容; 2. 掌握 Socket 编程,能够使用 Python 等语言进行网络编程; 3. 了解 HTML 和 CSS,能够构建简单的网页; 4. 掌握 HTTP 服务器框架,例如 Flask、Django 等。 下面是一个简单的例子,展示如何编写一个 Web 服务器并用浏览器请求它: ``` # 导入 socket 库 import socket # 定义服务器地址和端口号 HOST, PORT = '', 8888 # 创建一个 socket 对象 listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置 socket 的属性,使其可以重复使用地址 listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定服务器地址和端口号 listen_socket.bind((HOST, PORT)) # 开始监听客户端请求 listen_socket.listen(1) print(f'Serving HTTP on port {PORT} ...') while True: # 接收客户端请求 client_connection, client_address = listen_socket.accept() # 接收客户端发送的数据 request_data = client_connection.recv(1024) # 打印客户端发送的请求数据 print(request_data.decode('utf-8')) # 构造响应数据 response_body = '<html><body><h1>Hello, World!</h1></body></html>' response_headers = [ ('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', str(len(response_body))) ] response_headers_bytes = ''.join(f'{k}: {v}\r\n' for k, v in response_headers).encode('utf-8') response = b'HTTP/1.1 200 OK\r\n' + response_headers_bytes + b'\r\n' + response_body.encode('utf-8') # 发送响应数据给客户端 client_connection.sendall(response) # 关闭客户端连接 client_connection.close() ``` 上面的代码创建了一个 Web 服务器,监听在本机的 8888 端口上。当有客户端连接到该端口时,服务器会接收客户端发送的请求数据,并根据请求数据构造响应数据,最后发送响应数据给客户端。请求数据和响应数据都是 HTTP 协议格式的。 为了测试该服务器,我们可以打开浏览器,输入 `http://localhost:8888`,然后按下回车键。这样浏览器会向服务器发送一个 HTTP GET 请求。服务器接收到请求之后,会返回一个包含 "Hello, World!" 的 HTML 页面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值