【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发送字符串消息。

# ! /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客户端程序

# ! /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退出连接:

 

 

 

posted @ 2022-11-03 10:02  闪亮可可仙  阅读(398)  评论(0编辑  收藏  举报