【2】Socket通信
【转自:https://www.zhihu.com/question/29637351/answer/2278820437】
1. 什么是socket
socket中文叫套接字,就是TCP/IP协议栈中不同主机应用进程之间进行双向通信的端点抽象。一个socket就是网络上进程通信的一端,提供了应用进程利用网络协议交换数据的机制。socket上联应用进程,下联操作系统TCP/IP协议栈。是应用程序通过网络协议进行通信的接口。换句话说,你能通过socket 的接口,来控制协议栈工作,从而实现网络通信,达到跨主机通信。
协议栈的上半部分有两块,分别是负责收发数据的 TCP 和 UDP 协议,它们两会接受应用层的委托执行收发数据的操作。
协议栈的下面一半是用 IP 协议控制网络包收发操作,在互联网上传数据时,数据会被切分成一块块的网络包,而将网络包发送给对方的操作就是由 IP 负责的。
此外 IP 中还包括 ICMP 协议和 ARP 协议:
- ICMP 用于告知网络包传送过程中产生的错误以及各种控制信息;
- ARP 用于根据 IP 地址查询相应的 以太网 MAC 地址;
IP 下面的网卡驱动程序负责控制网卡硬件,而最下面的网卡则负责完成实际的收发操作,也就是对网线中的信号执行发送和接收操作。
2. 使用C/S架构通讯的Socket
【转自:https://www.zhihu.com/question/29637351/answer/1934423848】
socket 一般分为 TCP 网络编程和 UDP 网络编程。
2.1 TCP 网络编程
基于 TCP 协议的客户端和服务器:
-
服务端和客户端初始化
socket
,得到文件描述符; -
服务端调用
bind
,绑定 IP 地址和端口; -
服务端调用
listen
,进行监听; -
服务端调用
accept
,等待客户端连接; -
客户端调用
connect
,向服务器端的地址和端口发起连接请求; -
服务端
accept
返回 用于传输的socket
的文件描述符; -
客户端调用
write
写入数据;服务端调用read
读取数据; -
客户端断开连接时,会调用
close
,那么服务端read
读取数据的时候,就会读取到了EOF
,待处理完数据后,服务端调用close
,表示连接关闭。
这里需要注意的是,服务端调用 accept 时,连接成功了会返回一个已完成连接的 socket,后续用来传输数据。
所以,监听的 socket 和真正用来传送数据的 socket,是「两个」 socket,一个叫作监听 socket,一个叫作已完成连接 socket。
成功连接建立之后,双方开始通过 read 和 write 函数来读写数据,就像往一个文件流里面写东西一样。
2.2 结合三次握手连接的 TCP socket
- 客户端的协议栈向服务器端发送了 SYN 包,并告诉服务器端当前发送序列号 client_isn,客户端进入 SYN_SENT 状态;
- 服务器端的协议栈收到这个包之后,和客户端进行 ACK 应答,应答的值为 client_isn+1,表示对 SYN 包 client_isn 的确认,同时服务器也发送一个 SYN 包,告诉客户端当前我的发送序列号为 server_isn,服务器端进入 SYN_RCVD 状态;
- 客户端协议栈收到 ACK 之后,使得应用程序从 connect 调用返回,表示客户端到服务器端的单向连接建立成功,客户端的状态为 ESTABLISHED,同时客户端协议栈也会对服务器端的 SYN 包进行应答,应答数据为 server_isn+1;
- 应答包到达服务器端后,服务器端协议栈使得 accept 阻塞调用返回,这个时候服务器端到客户端的单向连接也建立成功,服务器端也进入 ESTABLISHED 状态。
2.3 结合四次次挥手的 TCP socket
- 客户端调用 close,表明客户端没有数据需要发送了,则此时会向服务端发送 FIN 报文,进入 FIN_WAIT_1 状态;
- 服务端接收到了 FIN 报文,TCP 协议栈会为 FIN 包插入一个文件结束符 EOF 到接收缓冲区中,应用程序可以通过 read 调用来感知这个 FIN 包。这个 EOF 会被放在已排队等候的其他已接收的数据之后,这就意味着服务端需要处理这种异常情况,因为 EOF 表示在该连接上再无额外数据到达。此时,服务端进入 CLOSE_WAIT 状态;
- 接着,当处理完数据后,自然就会读到 EOF ,于是也调用 close 关闭它的套接字,这会使得服务端会发出一个 FIN 包,之后处于 LAST_ACK 状态;
- 客户端接收到服务端的 FIN 包,并发送 ACK 确认包给服务端,此时客户端将进入 TIME_WAIT 状态;
- 服务端收到 ACK 确认包后,就进入了最后的 CLOSE 状态;
- 客户端经过 2MSL 时间之后,也进入 CLOSE 状态;
2.4 TCP服务端程序
使用一个服务端程序监听本机TCP-60000端口,使用一个客户端程序,向本机TCP-60000发送字符串消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | # ! /usr/bin/env python3 # _*_ coding: utf-8 _*_ from socket import * # 变量名大写,约定作为配置项的意思,这里定义了三个配置变量作为实例化的参数 # 定义IP地址、端口、缓存,缓存的单位是Byte IP = '127.0.0.1' PORT = 60000 BUFLEN = 1024 # 实例化一个socket赋值给变量listen_socket # 参数AF_INET就是IP地址族 # 参数SOCK_STREAM就是TCP、参数SOCK_DGRAM就是UDP、参数SOCK_RAW是原始套接字,为TCP、UDP之外的协议提供接口 listen_socket = socket(AF_INET, SOCK_STREAM) # 为这个socket绑定IP和端口,bind的对象必须是一个包含IP和端口元组 listen_socket.bind((IP, PORT)) # 使socket处于监听状态,等待客户端的请求,只有服务端能使用监听 # 参数5表示,最多接收5个等待连接的客户端 listen_socket.listen( 5 ) print (f 'Server: 服务启动成功,在{PORT}端口,等待 Client 连接... ...' ) # 监听socket的accept方法,用来接收客户端的连接,如果没有客户端连接,就一直处于监听状态(阻塞状态)直到有客户端连接 # 一旦客户段发起连接(TCP三次握手)accept方法就会返回一个元组,一个data_socket来传输数据,一个ipaddress包含IP和PORT data_socket, ip_port = listen_socket.accept() print (f 'Server>>> 接受一个客户端{ip_port}连接... ...' ) while True : # 尝试读取客户端发来的消息 # BUFLEN指定从接收缓冲里最多读取多少Bytes,recved是一个字节串变量 recved = data_socket.recv(BUFLEN) # 如果返回空的Bytes,意味着对方关闭了连接 # 退出循环,结束消息收发 if not recved: break # 把读取的Bytes数据,转换成字符串编码,赋值给变量info,然后打印出来 info = recved.decode( 'utf-8' ) print (f 'Server>>> 收到客户端{ip_port}消息: {info}' ) # 通知客户端的字符串,编码为utf-8的Bytes类型数据 data_socket.send(f 'Server>>> 服务器{(IP, PORT)}收到消息 {info}' .encode( 'utf-8' )) data_socket.close() listen_socket.close() |
2.5 TCP客户端程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # ! /usr/bin/env python3 # _*_ coding: utf-8 _*_ from socket import * IP = '127.0.0.1' PORT = 60000 BUFLEN = 1024 # 实例化了一个socket对象,赋给变量data_socket data_socket = socket(AF_INET, SOCK_STREAM) # 调用connect方法,连接服务器,connet就是连接正在等待的服务器端的listen_socket data_socket.connect((IP, PORT)) while True : toSend = input ( 'client>>> ' ) if toSend = = 'exit' : break data_socket.send(toSend.encode( 'utf-8' )) recved = data_socket.recv(BUFLEN) if not recved: break print (recved.decode( 'utf-8' )) data_socket.close() |
运行TCP服务端程序:
运行TCP客户端程序:
查看TCP服务端程序:
TCP客户端程序输入exit退出连接:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix