作者主要是为了用python构建实时网络通信程序。
概念性的东西越简单越好理解,因此,下面我从晚上摘抄的概念 + 我的理解。
什么是网络通信?
更确切地说,网络通信是两台计算机上的两个进程之间的通信。比如,浏览器进程和新浪服务器上的某个Web服务进程在通信,而QQ进程是和腾讯的某个服务器上的某个进程在通信。
而网络编程就是针对网络通信的编程。
1.网络编程基础
1.1 基本概念与协议
TCP/IP协议栈
网络编程的基础是 理解数据在网络中的传输过程,这通常通过OSI模型和TCP/IP协议栈进行解释。而TCP/IP协议栈则是实际应用中更为广泛的模型,包含物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。
计算机为了联网,需要规定通信协议,一开始是不统一的,相互不同协议之间的计算机交互是困难的。因此,就出现了互联网协议簇,任何网络只要遵守这个协议,就可以连入互联网。
互联网协议,包括上百种协议,最重要的当属TCP/IP协议,因此,大家把互联网协议简称TCP/IP协议。
通信的时候,双方必须知道对方的标识,好比发邮件必须知道对方的邮件地址。互联网上每个计算机的唯一标识就是IP地址,类似123.123.123.123
。如果一台计算机同时接入到两个或更多的网络,比如路由器,它就会有两个或多个IP地址,所以,IP地址对应的实际上是计算机的网络接口,通常是网卡。
IP协议负责把数据从一台计算机通过网络发送到另一台计算机。数据被分割成一小块一小块,然后通过IP包发送出去。由于互联网链路复杂,两台计算机之间经常有多条线路,因此,路由器就负责决定如何把一个IP包转发出去。IP包的特点是按块发送,途径多个路由,但不保证能到达,也不保证顺序到达。
TCP协议则是建立在IP协议之上的。TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。TCP协议会通过握手建立连接,然后,对每个IP包编号,确保对方按顺序收到,如果包丢掉了,就自动重发。
许多常用的更高级的协议都是建立在TCP协议基础上的,比如用于浏览器的HTTP协议、发送邮件的SMTP协议等。
一个TCP报文除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。
端口有什么作用?在两台计算机通信时,只发IP地址是不够的,因为同一台计算机上跑着多个网络程序。一个TCP报文来了之后,到底是交给浏览器还是QQ,就需要端口号来区分。每个网络程序都向操作系统申请唯一的端口号,这样,两个进程在两台计算机之间建立网络连接就需要各自的IP地址和各自的端口号。
一个进程也可能同时与多个计算机建立链接,因此它会申请很多端口。
TCP
TCP (Transmission Control Protocol) 是面向连接的、提供端到端可靠的数据流(flow of data)。TCP 提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
“面向连接”就是在正式通信前必须要与对方建立起连接。这一过程与打电话很相似,先拨号振铃,等待对方摘机说“喂”,然后才说明是谁。
端到端的数据流,建立在IP协议之上,提供超时重发、重复数据校验、流量控制灯基本功能。
保证数据能从一端到另一端。
三次握手
TCP 是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。一个 TCP 连接必须要经过三次“握手”才能建立起来,简单的讲就是:
- 主机 A 向主机 B 发出连接请求数据包:“我想给你发数据,可以吗?”;
- 主机 B 向主机 A 发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你来吧”;
- 主机 A 再发出一个数据包确认主机 B 的要求同步:“好的,我来也,你接着吧!”
三次“握手”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机 A 才向主机 B 正式发送数据。
A和B要通过 TCP通信的流程:
A 连接B,B被连接允许A连接,A接受到响应确认连接B。 -> A和B可以通信了。
UDP
UDP (User Datagram Protocol) 面向无连接的,主机发送独立的数据报(datagram)给其他主机,不保证数据到达。由于 UDP 在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。
而无连接是一开始就发送信息(严格说来,这是没有开始、结束的),只是一次性的传递,是先不需要接受方的响应,因而在一定程度上也无法保证信息传递的可靠性了,就像写信一样,我们只是将信寄出去,却不能保证收信人一定可以收到。
通信时,可以不用与目标主机建立连接,就能发送数据。
A和B通信,直接发送就可以,不用管对方是否 "可连接"。
TCP 是面向连接的,有比较高的可靠性, 一些要求比较高的服务一般使用这个协议,如FTP、Telnet、SMTP、HTTP、POP3 等,而 UDP 是面向无连接的,使用这个协议的常见服务有 DNS、SNMP、QQ 等。对于 QQ 必须另外说明一下,QQ2003 以前是只使用UDP协议的,其服务器使用8000端口,侦听是否有信息传来,客户端使用 4000 端口,向外发送信息(这也就不难理解在一般的显IP的QQ版本中显示好友的IP地址信息中端口常为4000或其后续端口的原因了),即QQ程序既接受服务又提供服务,在以后的 QQ 版本中也支持使用 TCP 协议了。
端口
一般来说,一台计算机具有单个物理连接到网络。数据通过这个连接去往特定的计算机。然而,该数据可以被用于在计算机上运行的不同应用。那么,计算机知道哪个应用程序转发数据?通过使用端口。
在互联网上传输的数据是通过计算机的标识和端口来定位的。计算机的标识是 32-bit 的 IP 地址。端口由一个 16-bit 的数字。
在诸如面向连接的通信如 TCP,服务器应用将套接字绑定到一个特定端口号。这是向系统注册服务用来接受该端口的数据。然后,客户端可以在与服务器在服务器端口会合,如下图所示:
TCP 和 UDP 协议使用的端口来将接收到的数据映射到一个计算机上运行的进程。
在基于数据报的通信,如 UDP,数据报包中包含它的目的地的端口号,然后 UDP 将数据包路由到相应的应用程序,如本图所示的端口号:
端口号取值范围是从 0 到 65535 (16-bit 长度),其中范围从 0 到 1023 是受限的,它们是被知名的服务所保留使用,例如 HTTP (端口是 80)和 FTP (端口是20、21)等系统服务。这些端口被称为众所周知的端口(well-known ports)。您的应用程序不应该试图绑定到他们。
嗯 ...,网络通信具体来讲,是两个进程之间的通信,很显然我们只要目标主机的IP是没法跟对方通信的,因此我们还必须知道目标进程运行占用的端口,通过 ip + port来与其通信。
2. Socket
Socket(套接字):是在网络上运行两个程序之间的双向通信链路的一个端点。socket绑定到一个端口号,使得 TCP 层可以标识数据最终要被发送到哪个应用程序。
正常情况下,一台服务器在特定计算机上运行,并具有被绑定到特定端口号的 socket。服务器只是等待,并监听用于客户发起的连接请求的 socket 。
在客户端:客户端知道服务器所运行的主机名称以及服务器正在侦听的端口号。建立连接请求时,客户端尝试与主机服务器和端口会合。客户端也需要在连接中将自己绑定到本地端口以便于给服务器做识别。本地端口号通常是由系统分配的。
如果一切顺利的话,服务器接受连接。一旦接受,服务器获取绑定到相同的本地端口的新 socket ,并且还具有其远程端点设定为客户端的地址和端口。它需要一个新的socket,以便它可以继续监听原来用于客户端连接请求的 socket 。
在客户端,如果连接被接受,则成功地创建一个套接字和客户端可以使用该 socket 与服务器进行通信。
客户机和服务器现在可以通过 socket 写入或读取来交互了。
端点是IP地址和端口号的组合。每个 TCP 连接可以通过它的两个端点被唯一标识。这样,你的主机和服务器之间可以有多个连接。
Socket是对互联网通信协议(如TCP/IP)的封装和应用,它提供了一个调用接口(API),通过这个接口我们可以使用这些协议栈来与目标计算机进行通信。
在具体的编程语言中,针对不同的通信协议会有不同的Socket实现。当你需要与目标计算机进行基于某个协议的通信时,你可以调用相应协议的Socket实现。这些Socket实现会根据协议的要求进行数据的封装和解封装,以便你能够与目标计算机进行通信。
因此,通过创建针对特定协议的Socket实现,你就可以使用Socket进行基于该协议的通信了。
具体举一下例子:
如果抖音有一个通信协议,并且针对Java提供了一个具体的SDK,那么这个SDK中应该包含了一个Socket的实现,用于与抖音服务器进行通信。
当你创建这个Socket时,你可以使用SDK提供的API来与抖音服务器建立连接、发送和接收数据。SDK会根据抖音的通信协议对数据进行封装和解封装,以便你能够与抖音服务器进行通信。
因此,通过创建这个Socket,你就可以使用SDK提供的API与抖音服务器进行通信了。
服务器指一个管理资源并为用户提供服务的计算机软件,通常分为文件服务器、数据库服务器和应用程序服务器。严格来说,服务器本身就是计算机硬件,并在其中运行的管理软件的一种管理资源,就如同电脑。按硬件还会分塔式服务器、卡片机、小型机。是一个大的概念,任何一台电脑安装了软件的服务器端就可以叫服务器。
2.Python网络编程
Socket
python提供了内置socket库,从而能够进行网路通信。下面我们直接展示一个案例。
下面简单利用了sockert库,来建立一个服务器,并且建立了一个客户端,去访问服务器并进行数据交互。
来源于菜鸟编程: Python 网络编程 | 菜鸟教程 (runoob.com)
Server.py
import socket
if __name__ == '__main__':
# 建立一个服务端
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 6999)) # 绑定要监听的端口
server.listen(5) # 开始监听 表示可以使用五个链接排队
while True: # conn就是客户端链接过来而在服务端为期生成的一个链接实例
conn, addr = server.accept() # 等待链接,多个链接的时候就会出现问题,其实返回了两个值
print(conn, addr)
while True:
try:
data = conn.recv(1024) # 接收数据
print('recive:', data.decode()) # 打印接收到的数据
conn.send(data.upper()) # 然后再发送数据
except ConnectionResetError as e:
print('关闭了正在占线的链接!')
break
conn.close()
client.py
import socket
if __name__ == '__main__':
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 声明socket类型,同时生成链接对象
client.connect(('localhost', 6999)) # 建立一个链接,连接到本地的6969端口
while True:
# addr = client.accept()
# print '连接地址:', addr
msg = '欢迎访问菜鸟教程!' # strip默认取出字符串的头尾空格
client.send(msg.encode('utf-8')) # 发送一条信息 python3 只接收btye流
data = client.recv(1024) # 接收一个信息,并指定接收的大小 为1024字节
print('recv:', data.decode()) # 输出我接收的信息
client.close() # 关闭这个链接
WebSocket
PS:以下全引用于HTML 菜鸟编程: HTML5 WebSocket | 菜鸟教程 (runoob.com)
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
以下 API 用于创建 WebSocket 对象。
我们可以看出,websocket协议是基于tcp协议的全双工通信协议。
它,允许服务端主动向客户端推送数据。而且,只需要一次握手,就可建立一次长连接。
与传统 AJAX轮训方法实现推送技术相比,发送请求的次数减少,(数据每一次更新,就代表一次ajax的请求和响应,这是相对比较浪费带宽资源的,通过websocket,客户端和服务端可以建立一次长连接完成数据的交互。)