一文读懂Socket通信原理

什么是Socket?
Socket的中文翻译过来就是“套接字” 。套接字是什么 , 我们先来看看它的英文含义:插座 。
Socket就像一个电话插座 , 负责连通两端的电话 , 进行点对点通信 , 让电话可以进行通信 , 端口就像插座上的孔 , 端口不能同时被其他进程占用 。而我们建立连接就像把插头插在这个插座上 , 创建一个Socket实例开始监听后 , 这个电话插座就时刻监听着消息的传入 , 谁拨通我这个“IP地址和端口” , 我就接通谁 。
实际上 , Socket是在应用层和传输层之间的一个抽象层 , 它把TCP/IP层复杂的操作抽象为几个简单的接口 , 供应用层调用实现进程在网络中的通信 。Socket起源于UNIX , 在Unix一切皆文件的思想下 , 进程间通信就被冠名为文件描述符(file desciptor) , Socket是一种“打开—读/写—关闭”模式的实现 , 服务器和客户端各自维护一个“文件” , 在建立连接打开后 , 可以向文件写入内容供对方读取或者读取对方内容 , 通讯结束时关闭文件 。
另外我们经常说到的Socket所在位置如下图:

一文读懂Socket通信原理

文章插图
Socket通信过程
Socket保证了不同计算机之间的通信 , 也就是网络通信 。对于网站 , 通信模型是服务器与客户端之间的通信 。两端都建立了一个Socket对象 , 然后通过Socket对象对数据进行传输 。通常服务器处于一个无限循环 , 等待客户端的连接 。
一图胜千言 , 下面是面向连接的TCP时序图:
一文读懂Socket通信原理

文章插图
客户端过程:
客户端的过程比较简单 , 创建Socket , 连接服务器 , 将Socket与远程主机连接(注意:只有TCP才有“连接”的概念 , 一些Socket比如UDP、ICMP和ARP没有“连接”的概念) , 发送数据 , 读取响应数据 , 直到数据交换完毕 , 关闭连接 , 结束TCP对话 。
import socket
import sys
if __name__ == '__main__':
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建Socket连接
sock.connect(('127.0.0.1', 8001)) # 连接服务器
while True:
data = input('Please input data:')
if not data:
break
try:
sock.sendall(data)
except socket.error as e:
print('Send Failed...', e)
sys.exit(0)
print('Send Successfully')
res = sock.recv(4096) # 获取服务器返回的数据 , 还可以用recvfrom()、recv_into()等
print(res)
sock.close()sock.sendall(data)
这里也可用send()方法:不同在于sendall()在返回前会尝试发送所有数据 , 并且成功时返回None , 而send()则返回发送的字节数量 , 失败时都抛出异常 。
服务端过程:
咱再来聊聊服务端的过程 , 服务端先初始化Socket , 建立流式套接字 , 与本机地址及端口进行绑定 , 然后通知TCP , 准备好接收连接 , 调用accept()阻塞 , 等待来自客户端的连接 。如果这时客户端与服务器建立了连接 , 客户端发送数据请求 , 服务器接收请求并处理请求 , 然后把响应数据发送给客户端 , 客户端读取数据 , 直到数据交换完毕 。最后关闭连接 , 交互结束 。
import socket
import sys
if __name__ == '__main__':
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建Socket连接(TCP)
print('Socket Created')
try:
sock.bind(('127.0.0.1', 8001)) # 配置Socket , 绑定IP地址和端口号
except socket.error as e:
print('Bind Failed...', e)
【一文读懂Socket通信原理】sys.exit(0)
sock.listen(5) # 设置最大允许连接数 , 各连接和Server的通信遵循FIFO原则
while True: # 循环轮询Socket状态 , 等待访问
conn, addr = sock.accept()
try:
conn.settimeout(10) # 获得一个连接 , 然后开始循环处理这个连接发送的信息
# 如果要同时处理多个连接 , 则下面的语句块应该用多线程来处理
while True:


推荐阅读