网络通讯协议与套接字
一、基本概念
-
网络
网络就是一种辅助双方或者多方能够连接在一起的工具, 为了让在不同的电脑上运行的软件,之间能够互相传递数据,就需要借助网络的功能
-
IP
地址-
IP
地址的概念和作用地址就是用来标记地点的, 用来在网络中标记一台电脑,比如192.168.1.1;在本地局域网上是唯一的。
-
IPV4
和IPV6
的主要区别IPv4地址的概念是在1980年代初期提出的。即使有新版本的IP地址,IPv4地址仍然是Internet用户使用最广泛的地址; IPv4 可以分配40亿个唯一地址,随着互联网的发展。2011年IP地址已经枯竭。 IPv4是32位IP地址; 分为四段,每段8位,以十进制形式表示,每段之间以点隔开,例如:192.168.0.1 IPv6是128位IP地址,分为八段,每段16位,以十六进制形式表示,每段之间以冒号隔开,例如:ffff:ffff:ffff:ffff:ffff:ffff:ffff:0001. 可以创建大约3.4×10e38个地址。
-
IP
地址的分类 (IPV4
) -
私有
IP
在这么多网络
IP
中,国际规定有一部分IP
地址是用于我们的局域网使用,也就是属于私网
IP
,不在公网中使用的,它们的范围是:10.0.0.0~10.255.255.255 172.16.0.0~172.31.255.255 192.168.0.0~192.168.255.255
-
说明
IP
地址127.0.0.1~127.255.255.255用于回路测试,- 127.0.0.1可以代表本机
IP
地址,可是使用 ping 127.0.0.1 测试本机的网卡和IP
协议 - 查看本机
IP
地址ipconfig
或ifconfig
-
-
端口port
-
什么是端口
端口就好一个房子的门,是出入这间房子的必经之路; 用于区分一个主机下的不同网络服务。
-
端口号
端口是通过端口号来标记的,端口号只有整数,范围是从0到65535
-
端口的分配
-
知名端口
知名端口是众所周知的端口号,范围从0到1023
80 端口分配给HTTP服务 21 端口分配给FTP服务
-
动态端口
动态端口的范围是从1024到65535,一般不固定分配某种服务,而是动态分配
-
-
二、网络通讯层次模型
-
数据在网络中的传输过程
- 数据在传输的过程中是以包的形式传输的,所以数据包是一个信息单位,作为一个整体,从网络中的一个设备传送给另一个设备。
- 数据包创建于应用层,然后经过下面六层的一层层包装,以增加自己的信息(信头),最后通过网线或光纤发给对方的物理层,然后又再向上一次一次的拆包。
三、Socket
-
什么是socket
socket(简称 套接字) 是进程间通信的一种方式,它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页、QQ 聊天、收发 email 等等
-
创建套接字
-
在 Python 中 使用socket 模块的函数 socket 就可以完成
import socket socket.socket(AddressFamily, Type)
-
参数说明
- Address Family:可以选择
AF_INET
(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
- Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于
TCP
协议)或者SOCK_DGRAM
(数据报套接字,主要用于UDP
协议)
- Address Family:可以选择
-
-
创建套接字代码演练
import socket # 创建tcp的套接字 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建udp的套接字 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 关闭套接字 s.close()
-
套接字使用流程
- 创建套接字对象
- 使用套接字发送/接收数据
- 关闭套接字
四、UDP
通信
-
UDP
通讯模型 -
UDP
通讯流程分析- 创建
UDP
套接字对象 - 绑定端口(客户端不需要绑定)
- 发送/接收 数据
- 关闭套接字
- 创建
-
代码演练
# 【本机环境运行】 # 使用网络调试助手作为客户端 from socket import * udp_socket = socket(AF_INET, SOCK_DGRAM) # 绑定端口(一般作为服务器才绑定) udp_socket.bind(("127.0.0.1", 8888)) dest_addr = ("127.0.0.1", 8080) send_data = input("please input send data: ") # win 默认按gbk编码 udp_socket.sendto(send_data.encode("utf8"), dest_addr) # 接收客户端的数据 recv_data = udp_socket.recvfrom(1024) content, addr = recv_data print(addr) print(content.decode("utf8")) udp_socket.close()
五、TCP
通信
-
TCP
通讯模型 -
UDP
通讯流程分析- 作为服务器
- 创建TCP套接字对象
- bind绑定
IP
和端口 - 设置套接字为监听套接字
- 等待客户端连接(连接成功得到服务套接字和客户端地址)
- 使用服务套接字接收/发送数据
- 关闭监听套接字和服务套接字
- 作为客户端
- 创建TCP套接字对象
- 连接服务器
- 使用套接字发送/接收数据
- 关闭套接字
- 作为服务器
-
代码演练
-
服务器实现
# 【本机环境运行】 from socket import * # 创建TCP套接字 tcp_server_socket = socket(AF_INET, SOCK_STREAM) # 绑定IP和端口 tcp_server_socket.bind(("127.0.0.1", 8888)) # 将套接字设置为监听状态 tcp_server_socket.listen(128) # 等待连接请求,获取服务套接字 client_socket, client_addr = tcp_server_socket.accept() while True: recv_data = client_socket.recv(1024).decode() # 客户端调用close; recv_data为 '' if not recv_data: break print(f"{client_addr} : {recv_data}") # 关闭服务套接字和监听套接字 client_socket.close() tcp_server_socket.close()
-
-
客户端实现
# 【本机环境运行】 from socket import * # 创建TCP套接字 tcp_client_socket = socket(AF_INET, SOCK_STREAM) # 连接TCP服务器 tcp_client_socket.connect(("127.0.0.1", 8888)) while True: send_data = input("please enter the sent data: ") if send_data == "exit": break tcp_client_socket.send(send_data.encode()) # 关闭套接字 tcp_client_socket.close()
六、TCP的3次握手和4次挥手
七、TCP长连接和短连接
-
短连接
建立连接 —— 数据传输 —— 关闭连接 ... 建立连接 —— 数据传输 —— 关闭连接
-
长连接
建立连接 —— 数据传输 ...(保持连接)... 数据传输 —— 关闭连接
八、UDP
与TCP
通信区别
- 面向连接(确认有创建三方交握,连接已创建才作传输。)
- 有序数据传输
- 重发丢失的数据包
- 舍弃重复的数据包
- 无差错的数据传输
- 阻塞/流量控制
九、说明
TCP
服务器一般情况下都需要绑定,否则客户端找不到这个服务器TCP
客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的IP
、PORT
等信息就好,本地客户端可以随机TCP
服务器中通过listen
可以将socket
创建出来的主动套接字变为被动的,这是做TCP
服务器时必须要做的- 当客户端需要链接服务器时,就需要使用
connect
进行链接,UDP
是不需要链接的而是直接发送,但是TCP
必须先链接,只有链接成功才能通信 - 当一个
TCP
客户端连接服务器时,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务 - listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而
accept
返回的新套接字是标记这个新客户端的 - 关闭
listen
后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信。 - 关闭
accept
返回的套接字意味着这个客户端已经服务完毕 - 当客户端的套接字调用
close
后,服务器端会recv
解堵塞,并且返回的长度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线