赞
踩
TCP( Transmission control protocol )即传输控制协议,是一种面向连接、可靠的数据传输协议,它是为了在不可靠的互联网上提供可靠的端到端字节流而专门设计的一个传输协议。
面向连接 :数据传输之前客户端和服务器端必须建立连接
可靠的 :数据传输是有序的 要对数据进行校验
为了保证客户端和服务器端的可靠连接,TCP建立连接时必须要进行三次会话,也叫TCP三次握手,进行三次握手的目的是为了确认双方的接收能力和发送能力是否正常。
- 第一次握手(呼叫请求):
- 你(客户端):想要和某人通话,于是你拿起电话,拨打对方的号码。这个动作相当于TCP中的SYN(同步序列编号)包发送。你告诉对方(服务器):“嘿,我在这里,我想和你建立通话。”
- 第二次握手(接听确认):
- 对方(服务器):听到电话铃声后,接起电话,并对你的呼叫做出响应。这相当于TCP中的SYN-ACK(同步确认)包发送。对方(服务器)说:“我听到了,我也在这里,我们可以开始通话了。”同时,对方(服务器)也确认了你的存在,并准备好接下来的通信。
- 第三次握手(确认接听):
- 你(客户端):在听到对方的接听确认后,你回应一个确认信号,告诉对方你已经准备好开始通话了。这相当于TCP中的ACK(确认)包发送。你说:“好的,我已经准备好了,我们可以开始通话了。”
三次握手主要作用:防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误
第一次握手: 客户端向服务器端发送报文 证明客户端的发送能力正常 第二次握手:服务器端接收到报文并向客户端发送报文 证明服务器端的接收能力、发送能力正常 第三次握手:客户端向服务器发送报文 证明客户端的接收能力正常
建立TCP连接需要三次握手,终止TCP连接需要四次挥手。
张三:好的,那我先走了
李四:好的,那你走吧
李四:那我也走了?
张三:好的,你走吧
Socket的英文原义是“孔”或“插座”,网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
建立网络通信连接至少要一对端口号(socket),socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。
Python 官方关于 Socket 的函数 socket — Low-level networking interface — Python 3.12.4 documentation
TCP发送数据时,已建立好TCP连接,所以不需要指定地址。UDP是面向无连接的, 每次发送要指定是发给谁。
服务端与客户端不能直接发送列表,元组,字典。需要字节化(data)。
共有的函数 :
SOCKET函数 | 描述 |
---|---|
s.recv(bufsize[,flag]) | 接受TCP,UDP套接字的数据。数据以字符串形式返回,bufsize指定要接收的最大数据量。 |
s.send(string[,flag]) | 发送TCP,UDP数据。 |
s.sendall(string[,flag]) | 完整发送TCP数据。 |
s.recvfrom(bufsize[.flag]) | 接受UDP套接字的数据。与recv()类似,但返回值是(data,address)。 |
s.sendto(string[,flag],address) | 发送UDP数据。将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。 |
s.close() | 关闭套接字。 |
s.getpeername() | 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 |
s.getsockname() | 返回套接字自己的地址。 |
s.setsockopt(level,optname,value) | 设置给定套接字选项的值。 |
s.getsockopt(level,optname[.buflen]) | 设置给定套接字选项的值。 |
s.settimeout(timeout) | 设置套接字操作的超时期 |
s.gettimeout() | 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。 |
s.fileno() | 返回套接字的文件描述符。 |
s.setblocking(flag) | 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。 |
s.makefile() | 创建一个与该套接字相关连的文件 |
服务端代码:
- from socket import *
-
-
- tcp_server = socket(AF_INET, SOCK_STREAM)
-
- # 绑定本地信息,TCP服务器程序监听所有网络接口上的88888端口
- host_port = ('', 88888)
- tcp_server.bind(host_port)
-
- # 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的链接了
- tcp_server.listen(5)
-
- while True:
-
-
- newSocket, host_port = tcp_server.accept()
-
- while True:
-
- # 最大接收1024个字节
- recvData = newSocket.recv(1024)
-
- if len(recvData) > 0:
- print('recv:', recvData)
- else:
- break
-
- sendData = input("send:")
- newSocket.send(sendData.encode('utf8'))
-
-
- newSocket.close()
-
- tcp_server.close()

客户端代码:
- from socket import *
-
-
- tcp_client = socket(AF_INET, SOCK_STREAM)
-
- # 链接服务器
- host_port = ('127.0.0.1', 88888)
- tcp_client.connect(host_port)
-
- while True:
-
-
- sendData = input("send:")
-
- if len(sendData) > 0:
- tcp_client.send(sendData.encode('utf8'))
- else:
- break
-
- # 最大接收1024个字节
- recvData = tcp_client.recv(1024)
- print('recv:', recvData.decode('uft8'))
-
- tcp_client.close()

UDP ---(User Datagram Protocol) 用户数据报协议,是一个无连接的简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,没有超时重发等机制,所以传输速度很快。
UDP是一种面向无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
UDP特点:
UDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。 UDP传输数据时有大小限制,每个被传输的数据报必须限定在64KB之内。 UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。
【适用情况】 UDP是面向消息的协议,通信时不需要建立连接,数据的传输自然是不可靠的,UDP一般用于多点通信和实时的数据业务,比如
语音广播
视频
TFTP(简单文件传送)
大型网络游戏
相比较于TCP注重速度流畅
服务端代码
- import socket
-
- sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- # 绑定 IP 和端口号
- sk.bind(('127.0.0.1', 88888))
- while True:
-
- msg, addr = sk.recvfrom(1024)
-
- print('来自[%s:%s]的消息: %s' % (addr[0], addr[1], msg.decode('utf-8')))
-
-
- inp = input('>>>')
-
- sk.sendto(inp.encode('utf-8'), addr)
-
- sk.close()

客户端代码:
- import socket
-
-
- sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- addr = ('127.0.0.1', 88888)
- while True:
-
- msg = input('>>>')
- sk.sendto(msg.encode('utf-8'), addr)
- msg_recv, addr = sk.recvfrom(1024)
- print(msg_recv.decode('utf-8'))
-
- sk.close()
实现简单TFTP(基于UDP协议)
实现一个简单的TFTP(Trivial File Transfer Protocol)服务器和客户端基于UDP协议是一个有趣的小练习。TFTP是一个简单的文件传输协议,它使用UDP协议,并且通常用于小文件的传输。
我们值实现最基本的功能:从服务器读取文件并发送到客户端。
TFTP 服务器:
- import socket
- import struct
-
- def handle_client(sock, filename):
- # 假设文件存在且小于65535字节
- try:
- with open(filename, 'rb') as file:
- data = file.read(512) # TFTP块大小为512字节
- block_number = 1
- while data:
- # 发送DATA包:block_number + data
- block_info = struct.pack('!H', block_number) # 网络字节序的短整型
- sock.sendto(block_info + data, client_address)
-
- # 等待ACK
- ack_data, _ = sock.recvfrom(2) # 只接收block number
- ack_block_number = struct.unpack('!H', ack_data)[0]
-
- if ack_block_number != block_number:
- print("Incorrect block number in ACK")
- break
-
- block_number += 1
- data = file.read(512)
- except FileNotFoundError:
- # 发送ERROR包
- error_code = 1 # File not found
- error_msg = struct.pack('!HH', error_code, 0) # TFTP错误消息格式
- sock.sendto(error_msg, client_address)
-
- def start_tftp_server(port, root_dir):
- server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- server_socket.bind(('0.0.0.0', port))
-
- print(f"TFTP server listening on port {port}...")
-
- while True:
- data, client_address = server_socket.recvfrom(1024) # 接收RRQ或WRQ请求
- # 这里简化处理,只处理RRQ
- if data.startswith(b'\x00\x01'): # RRQ的opcode
- filename = data[2:].decode().split(b'\x00')[0]
- filepath = f"{root_dir}/{filename}"
- handle_client(server_socket, filepath)
-
- if __name__ == "__main__":
- start_tftp_server(69, './files')

TFTP 客户端:
- import socket
- import struct
-
- def request_file(server_ip, server_port, filename):
- client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-
- # 发送RRQ请求
- rrq = b'\x00\x01' + filename.encode() + b'\x00' + b'octet' + b'\x00' # mode为octet
- client_socket.sendto(rrq, (server_ip, server_port))
-
- block_number = 0
- data = b''
-
- while True:
- # 接收DATA包
- data_packet, _ = client_socket.recvfrom(514) # 512字节数据 + 2字节块号
- block_number = struct.unpack('!H', data_packet[:2])[0]
- if block_number == 0: # 假设块号为0表示文件结束或错误
- break
-
- data += data_packet[2:]
-
- # 发送ACK
- ack = struct.pack('!H', block_number)
- client_socket.sendto(ack, (server_ip, server_port))
-
- client_socket.close()
-
- # 处理接收到的数据(例如,写入文件)
- with open(filename, 'wb') as file:
- file.write(data)
-
- if __name__ == "__main__":
- request_file('127.0.0.1', 69)
- file_name = 'example.txt'
- request_file(server_addr, 69, file_name)

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。