[通信]一文读懂Socket通信原理


什么是Socket?
Socket的中文翻译过来就是“套接字” 。 套接字是什么 , 我们先来看看它的英文含义:插座 。
Socket就像一个电话插座 , 负责连通两端的电话 , 进行点对点通信 , 让电话可以进行通信 , 端口就像插座上的孔 , 端口不能同时被其他进程占用 。 而我们建立连接就像把插头插在这个插座上 , 创建一个Socket实例开始监听后 , 这个电话插座就时刻监听着消息的传入 , 谁拨通我这个“IP地址和端口” , 我就接通谁 。
【[通信]一文读懂Socket通信原理】实际上 , 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 socketimport sysif __name__ == '__main__': sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建Socket连接 sock.connect(('127.0.0.1', 8001)) # 连接服务器 while True: data = http://news.hoteastday.com/a/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 socketimport sysif __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) sys.exit(0) sock.listen(5) # 设置最大允许连接数 , 各连接和Server的通信遵循FIFO原则 while True: # 循环轮询Socket状态 , 等待访问 conn, addr = sock.accept() try: conn.settimeout(10) # 获得一个连接 , 然后开始循环处理这个连接发送的信息 # 如果要同时处理多个连接 , 则下面的语句块应该用多线程来处理 while True: data = http://news.hoteastday.com/a/conn.recv(1024) print('Get value ' + data, end='\n\n') if not data: print('Exit Server', end='\n\n') break conn.sendall('OK') # 返回数据 except socket.timeout: # 建立连接后 , 该连接在设定的时间内没有数据发来 , 就会引发超时 print('Time out') conn.close() # 当一个连接监听循环退出后 , 连接可以关掉 sock.close()conn, addr = sock.accept()


推荐阅读