计算机网络 02:应用层(4)

146 阅读8分钟

Socket programming with UDP and TCP

网络协议由三个要素组成,分别是语义、语法和时序。

  • 语义是解释控制信息每个部分的含义,它规定了需要发出何种控制信息,以及完成的动作与做出什么样的响应;
  • 语法是用户数据与控制信息的结构与格式,以及数据出现的顺序;
  • 时序是对事件发生顺序的详细说明。

人们形象地将这三个要素描述为:语义表示要做什么,语法表示要怎么做,时序表示做的顺序。

Socket programming

Goal: learn how to build client/server applications that communicate using sockets.

Two socket types for two transport services:

  • UDP: unreliable datagram(数据报文,数据包)
  • TCP: reliable, byte stream-oriented(以流为导向)

1_bjlwOQvdSYGktTtPuGVldQ

This photo is a great visual representation of what is going on between the server and the client. In a Connection-oriented system, the server and client send these SYN, SYN-ACK and ACK messages to ensure the packet is successfully received, if this pattern(模式) is broken (i.e. one of these is not sent) then the packet can be re-sent.

UDP 与 TCP 比较

为了更直观地比较 TCP 与 UDP 的异同,笔者将其整理成以下表格:

TCPUDP
连接模式面向连接(单点通信)无连接(多点通信)
传输可靠性可靠不可靠
通信模式基于字节流基于数据报
报头结构复杂(至少 20 字节)简单(8 字节)
传输速度
资源需求
到达顺序保证不保证
流量控制
拥塞控制
应用场合大量数据传输少量数据传输
支持的应用层协议Telnet、FTP、SMTP、HTTPDNS、DHCP、TFTP、SNMP

Client/server socket: UDP

Properties of UDP:

UDP - Client and Server example programs in Python | Pythontic.com

  • The UDP does not provide guaranteed(放心的,可靠的) delivery of message packets. If for some issue in a network if a packet is lost it could be lost forever.
  • Since there is no guarantee of assured(有把握的) delivery of messages, UDP is considered an unreliable protocol.
  • The underlying mechanisms(底层机制) that implement(实现) UDP involve no connection-based communication. There is no streaming of data between a UDP server or and an UDP Client.
  • An UDP client can send "n" number of distinct packets to an UDP server and it could also receive "n" number of distinct packets as replies from the UDP server.
  • Since UDP is connectionless protocol the overhead(负载,开销) involved in UDP is less compared to a connection based protocol like TCP.

UDP Client Server Communication

UDP 具体实现:

【Python】UDP/TCP_种花家 de 小红帽的博客-CSDN 博客

面向无连接型:无需对端是否存在,发送端可随时发送数据

特点:无连接,资源开销小,传输速度快,每个数据包最大是 64k,适用于广播应用

缺陷:传输数据不可靠,容易丢包;没有流量控制,需要接收方及时接收数据,否则会写满缓冲区

UDP 网络流程

img

  1. 保证 UPD 服务端的正常启动,进入到 recvfrom() 模式,阻塞等到客户端发送数据
  2. 开启 UDP 客户端,校准 IP 地址,通过 sendto() 模块进行数据发送
  3. 当服务端接收到接收到客户端发送来的数据,进行数据处理,并将应答数据发送给客户端
  4. 客户端接收到应答数据,可进行数据处理或重复发送数据,也可退出进程

UDP Serve 服务端

 # coding=utf-8
 from socket import *
 ​
 # 1. 创建套接字
 udp_socket = socket(AF_INET, SOCK_DGRAM)
 ​
 # 2. 绑定本地的相关信息,如果一个网络程序不绑定,系统会随机分配
 #    ip地址和端口号,如果不指明ip,则表示本机的任何一个ip
 #    如果不指明端口号,则每次启动都是随机生成端口号
 local_addr = ('', 12345)
 udp_socket.bind(local_addr)
 ​
 while True:
     # 3. 阻塞等待接收对方发送的信息
     #    1024表示本次接收的最大字节数
     recv_data = udp_socket.recvfrom(1024)
 ​
     # 4. 显示接收到的数据,并解码为gbk
     print(recv_data)
     print(recv_data[0].decode('utf-8'))
 ​
     # 5. 发送应答信息
     # ip_addr = recv_data[1][0]
     # port = recv_data[1][1]
     addr = recv_data[1]
     data = '信息已收到'
     udp_socket.sendto(data.encode('utf-8'), addr)
 ​
 udp_socket.close()

UDP Client 客户端

 import socket
 ​
 # 1. 创建upd套接字
 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 ​
 # 2. 准备服务端地址与端口号
 #   127.0.0.1 代表自身ip地址,可向自身发送信息,也可指定ip地址发送信息
 #   端口号随便填写一个未被占用的端口即可
 #   Linux环境有65535个端口号,前1024个端口号是系统端口号,系统端口号不能直接使用
 addr = ('127.0.0.1', 12345)
 ​
 while True:
     # 3. 从键盘获取数据
     data = input('请输入信息:')
 ​
     # 4. 通过sendto()发送信息到指定进程中
     udp_socket.sendto(data.encode('utf-8'), addr)
 ​
     # 5. 通过recvfrom()阻塞等待获取应答数据
     recv_data = udp_socket.recvfrom(1024)
 ​
     # 6. 处理应答数据,进行打印
     print(recv_data)
     print(recv_data[0].decode('utf-8'))
 ​
 udp_socket.close()

运行结果:(先运行服务端,再运行客户端)

img

Client/server socket: TCP

【Python】UDP/TCP_种花家 de 小红帽的博客-CSDN 博客

面向有连接型:双方先建立连接才能进行数据传输

特点:

  • 双方都必须为该连接分配系统内核资源
  • 完成数据交换后,双方必须断开连接,以释放系统资源
  • 这种连接是一对一的,不适用于广播应用
  • TCP 提供可靠的数据传输,无差别、不丢失、不重复,且按序到达
  • 相比于 UPD,TCP 数据传输速度慢、对系统资源要求较高
  • TCP 适合发送大量数据,UDP 适合发送少量数据
  • TCP 有流量控制,UPD 无流量控制

TCP 网络流程

img

TCP Serve 服务端

from socket import *

# 1. 创建tcp套接字
tcp_serve_socket = socket(AF_INET, SOCK_STREAM)

# 2. 设置socket选项,程序退出后,端口会自动释放
tcp_serve_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, True)

# 3. 本地信息,第二个为端口
addr = ('', 12345)

# 4. 绑定地址
tcp_serve_socket.bind(addr)

# 5. 设置监听
#    使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的
#    参数代表等待连接时间最多60秒
tcp_serve_socket.listen(60)

# 6. 如果有新的客户端来连接服务,就产生一个新的套接字,专门为这个客户端服务
#    client_socket用来为这个客户端服务
#    原来的tcp_serve_socket就可以专门用来等待其他新用户的连接
client_socket, client_addr = tcp_serve_socket.accept()

# 7. 阻塞等待客户端发送的信息
recv_data = client_socket.recv(1024)
print("接收到信息:", recv_data.decode('gbk'))

# 8. 发送应答信息
string = '已收到信息'
client_socket.send(string.encode('gbk'))

client_socket.close()

TCP Client 客户端

import socket

# 1. 创建TCP的套接字
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2. 目标ip信息
ip = input('请输入服务端ip:')
port = int(input('请输入服务端port:'))

# 3. 连接服务器
tcp_client_socket.connect((ip, port))

# 4. 提示用户输入数据
data = input('请输入要发送的信息:')

# 5. 编码
tcp_client_socket.send(data.encode('gbk'))

# 6. 接收服务端的应答数据
recv_data = tcp_client_socket.recv(1024)
print('收到应答数据:', recv_data.decode('gbk'))

# 7. 关闭套接字
tcp_client_socket.close()

运行结果:

运行结果

如果忘记设置端口的关闭,非正常退出会导致端口一直被占用

linux 环境在终端执行 ps aux | grep py 查看运行的进程,然后 kill -9 pid 杀掉进程

建立连接(三次握手)

三次握手

SYN:连接请求 ACK:确认 FIN:关闭连接 seq:报文信号 ack:确认信号

  1. 第一次握手:client 标志位 SYN 置 1,随机产生一个 seq=J,并将该数据包发送给 serve,client 进入 SYN_SENT 状态,等待 serve 确认
  2. 第二次握手:serve 收到数据包后由标志位 SYN=1 知道 client 请求建立连接,serve 将 SYN 和 ACK 都置 1,ack(number)=J+1,+1 是逻辑加一(加密),随机产生一个值 seq=K,并将该数据包发送给 client 以确认连接请求,serve 进入 SYN_RECV 状态
  3. 第三次握手:client 收到确认,检查 ack 是否为 J+1(解密),如果正确则将标志位 ACK 置 1,ack=K+1,并将该数据包发送给 serve,serve 检查 ack 是否为 K+1,如果正确则建立连接成功,client 和 serve 同时进入 ESTABLISHED 状态,完成三次握手,随后 client 和 serve 之间可以传输数据

断开连接(四次挥手)

四次挥手

  1. 第一次挥手:client 发送一个 FIN,用来关闭 client 到 serve 的数据传送
  2. 第二次挥手:serve 收到 FIN 后,发送一个 ACK 给 client,确认序号为收到序号 + 1,表示还有剩余数据未传送完
  3. 第三次挥手:serve 发送一个 FIN,用来关闭 serve 到 client 的数据传送
  4. 第四次挥手:client 收到 FIN 后,接着发送一个 ACK 给 serve,确认序号为收到信号 + 1

Q: TCP 传输中的 Segment 是什么?

A: 是该协议负责传输的数据单元的专用名词。在 TCP 协议中,分段(segment)是指将传输的数据分割成多个较小的部分,以便更有效地在网络上传输。每个分段包含在 TCP 首部中的控制信息,如端口号、序列号和确认号等,以及在数据字段中携带的有效负载部分。分段的大小可以根据不同的网络条件进行调整,以最大程度地减少重传或延迟。在接收方,TCP 会将收到的分段重新组合成完整的数据,以保证传输的准确性和完整性。