本篇文章是博主在人工智能、网络通讯等领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在**Python**: Python(1)---《TCP协议双向网络通讯---Python实现》
TCP协议双向网络通讯---Python实现
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。它工作在OSI模型的传输层,旨在确保数据在IP网络中的可靠传输。以下是对TCP协议网络通讯的详细论述:
本文主要使用**Python实现TCP协议双向网络通讯**,即服务器和客户端都可以实现信息的收发,采用多线程的方式,能够实现服务器的一对多收发信息。
一、TCP协议的基本特点
- 面向连接:TCP协议在数据传输之前,必须先在通信双方之间建立连接。这种连接是一对一的,即一个TCP连接只能有两个端点。连接建立后,双方可以开始传输数据,直到连接被关闭。(使用多线程可以实现一对多等)
- 可靠性:TCP协议通过一系列机制来确保数据的可靠传输。这些机制包括序列号、确认应答、超时重传、流量控制等。TCP协议能够确保数据无差错、不丢失、不重复,并且按序到达。
- 基于字节流:TCP协议将应用程序交下来的数据看成是一连串的无结构的字节流。TCP不关心应用程序写入数据的含义和内容,它只负责将这些数据以字节流的形式发送到对方。
二、TCP协议的连接过程
TCP协议的连接过程通常被称为“三次握手”:
- 第一次握手:客户端发送一个SYN(同步序列号)报文段到服务器,并在这个报文段中设置序列号字段的值。此时,客户端进入SYN_SENT状态。
- 第二次握手:服务器收到客户端的SYN报文段后,发送一个SYN/ACK(同步/确认)报文段给客户端,确认客户端的SYN报文段,并设置自己的序列号。此时,服务器进入SYN_RCVD状态。
- 第三次握手:客户端收到服务器的SYN/ACK报文段后,发送一个ACK(确认)报文段给服务器,确认服务器的SYN/ACK报文段。此时,客户端和服务器都进入ESTABLISHED(已建立连接)状态,TCP连接成功建立。
三、TCP协议的数据传输过程
TCP协议在数据传输过程中,通过序列号、确认应答等机制来确保数据的可靠传输。
- 序列号:TCP头部的序列号用于确保数据的有序传输。它表示在这个TCP段中的第一个字节的序号。在建立连接时,双方都随机生成一个初始序列号(ISN),并以此作为后续数据传输的基准。
- 确认应答:TCP头部中的确认应答号是接收端期望收到的下一个字节的序列号。发送端收到确认应答后,可以认为所有在此之前的序列号的包已经被接收。
- 超时重传:如果发送方在一定时间内没有收到接收方的确认应答,就会认为该报文段已经丢失,并重新发送该报文段。
- 流量控制:TCP协议通过窗口大小来控制发送方的数据流量,以适应接收方的处理能力。接收方在确认应答中告知发送方自己的窗口大小,发送方根据这个值来控制数据的发送量。
- 拥塞控制:TCP协议还通过拥塞控制机制来防止网络过载。当网络拥塞时,TCP会减慢数据的发送速率,以减轻网络的负担。
四、TCP协议的连接关闭过程
TCP协议的连接关闭过程通常被称为“四次挥手”:
- 第一次挥手:客户端发送一个FIN(结束连接)报文段给服务器,表示客户端没有数据要发送了,请求关闭连接。此时,客户端进入FIN_WAIT_1状态。
- 第二次挥手:服务器收到客户端的FIN报文段后,发送一个ACK报文段给客户端,确认收到客户端的FIN报文段。此时,服务器进入CLOSE_WAIT状态,客户端进入FIN_WAIT_2状态。
- 第三次挥手:服务器在关闭连接前,如果还有数据要发送给客户端,可以继续发送数据。当服务器没有数据要发送时,发送一个FIN报文段给客户端,表示服务器也没有数据要发送了,请求关闭连接。此时,服务器进入LAST_ACK状态。
- 第四次挥手:客户端收到服务器的FIN报文段后,发送一个ACK报文段给服务器,确认收到服务器的FIN报文段。此时,客户端和服务器都进入CLOSED状态,TCP连接成功关闭。
五、TCP协议的安全挑战与防范策略
尽管TCP协议在设计之初就考虑到了数据传输的可靠性和安全性,但随着网络技术的不断发展,TCP协议也面临着越来越多的安全挑战。例如,TCP建立连接需要经历三次握手过程,这个过程中存在被恶意利用的风险;TCP协议本身并不提供加密功能,因此数据在传输过程中容易被截获和篡改。
为了保障TCP协议的安全性,可以采取以下防范策略:
- 使用SSL/TLS等加密协议对TCP通信进行加密处理,确保数据在传输过程中的安全性和完整性。
- 通过限制SYN请求频率、限制连接数量等措施来防止SYN Flood攻击等恶意攻击。
- 对于接收到的TCP数据包,要验证其来源是否合法,防止伪造数据
六、Python编程实现
4.1TcpServer服务器端代码
import socket # 导入socket模块,用于网络通信
from threading import Thread # 导入Thread类,用于多线程处理
import time # 导入time模块,用于时间操作
import sys # 导入sys模块,用于系统相关操作
import random # 导入random模块,用于生成随机数
# 创建存储对象
class Node:
def __init__(self):
self.Name = None # 用户名
self.Thr = None # 套接字连接对象
self.flag = 0 # 标志位
class TcpServer:
user_name = {} # 存储用户信息; dict 用户名:Node对象
def __init__(self, port):
"""
初始化服务器对象
port: 服务器端口
"""
self.server_port = port # 服务器端口
self.tcp_socket = socket.socket() # tcp套接字
self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 端口重用
self.tcp_socket.bind(self.server_port)
self.sum = 0 # 总数初始值
self.flag1 = 0 # 标志位1
self.flag2 = 0 # 标志位2
def start(self):
"""
启动服务器
"""
self.tcp_socket.listen(100) # 设置服务器接受的链接数量
print(self.get_time(), "系统:等待连接")
timer = Thread(target=self.timer_to_order) # 创建定时发送线程
timer.start()
syn = Thread(target=self.syn_to_order) # 创建同步发送线程
syn.start()
while True:
try:
# 监听客户端的地址和发送的消息
conn, addr = self.tcp_socket.accept() # 返回值为套接字和网络地址
except KeyboardInterrupt: # 按下ctrl+c会触发此异常
self.tcp_socket.close() # 关闭套接字
sys.exit("\n" + self.get_time() + "系统:服务器安全退出!") # 程序直接退出,不捕捉异常
except Exception as e:
print(e)
continue
# 为当前链接创建线程
t = Thread(target=self.do_request, args=(conn, ))
t.start()
def do_request(self, conn):
"""
监听客户端传送的消息,并将该消息发送给所有用户
"""
conn_node = Node()
while True:
recv_data = conn.recv(1024).decode('utf-8').strip() # 获取客户端发来的数据
# print(recv_data)
info_list = recv_data.split(" ") # 切割命令
# print(info_list)
# 如果接收到命令为exit,则表示该用户退出,删除对应用户信息,关闭连接
if recv_data == "exit":
msg = self.get_time() + " 系统:用户" + conn_node.Name + "退出控制系统!"
print(msg)
self.send_to_other(conn_node.Name, msg)
conn.send('exit'.encode("utf-8"))
self.user_name.pop(conn_node.Name)
conn.close()
break
else:
try:
A = info_list[-2], info_list[-1]
except IndexError:
conn.send((self.get_time() + ' 系统:无法识别您的指令,请重新输入!').encode('gb2312'))
continue
if info_list[-1] == '-n':
# 新用户注册
print(self.get_time() + ' 系统:' + info_list[0] + '连接成功')
data_info = self.get_time() + ' 系统:' + info_list[0] + '加入了控制系统'
self.send_to_all(data_info)
conn.send('OK'.encode('utf-8'))
conn_node.Name = info_list[0]
conn_node.Thr = conn
self.user_name[info_list[0]] = conn_node
elif info_list[-1] == '-ta':
# 群发消息
# msg = self.get_time() + ' %s:' % conn_node.Name + ' '.join(info_list[:-1])
self.sum = self.sum + int(info_list[0])
msg = self.get_time() + ' %s:贡献' % conn_node.Name + ' '.join(info_list[:-1]) + '。总数:%d' % self.sum
self.send_to_all(msg)
elif info_list[-1] == '-tas':
conn_node.flag = 1
self.sum = self.sum + int(info_list[0])
msg = self.get_time() + ' %s:贡献' % conn_node.Name + ' '.join(info_list[:-1]) + '。总数:%d' % self.sum
self.send_to_all(msg)
def send_to_all(self, msg):
"""
对所有用户发送消息
"""
print(msg)
for i in self.user_name.values():
i.Thr.send(msg.encode('utf-8'))
def send_to_other(self, name, msg):
"""
对除了当前发送信息的用户外的其他用户发送消息
"""
# print("收到消息:" + msg)
for n in self.user_name:
if n != name:
self.user_name[n].Thr.send(msg.encode('utf-8'))
else:
continue
def get_time(self):
"""
返回当前系统时间
"""
return '[' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + ']'
# 服务器定时发送指令
def timer_to_order(self):
while True:
for i in self.user_name.values():
msg = '{}'.format(random.randint(1, 10)) + ' -ord'
print('\n给客户端发送定时指令: %s' % msg)
i.Thr.send(msg.encode('utf-8'))
time.sleep(30) # 30s定时发送一次
# 服务器同步发送指令
def syn_to_order(self):
while True:
flag_sum = 0 # 标志位总和
for key in self.user_name: # 获得字典的键
flag_sum += self.user_name[key].flag # 计算各客户端的flag位
time.sleep(0.01) # 延迟10s,防止迭代时,字典数量改变
if flag_sum == len(self.user_name) and flag_sum != 0: # 判断服务器是否都接受到flag标志
end_flag = 1 # 终值标志位
else:
end_flag = 0
if end_flag:
print("\n")
for i in self.user_name.values():
msg = '{}'.format(random.randint(1+self.sum, 10+self.sum)) + ' -ord'
print('===给客户端发送特殊指令: %s' % msg)
i.Thr.send(msg.encode('utf-8'))
i.flag = 0
if __name__ == '__main__':
HOST = "127.0.0.1" # 主机回送地址,测试使用
# HOST = '192.168.8.111' # 局域网ip地址,实际情况使用
POST = 9999
server = TcpServer((HOST, POST))
server.start()
4.2 TcpClient客户端代码
**(可以新建多个客户端)**
import socket # 导入socket模块,用于网络通信
from threading import Thread # 导入Thread类,用于多线程处理
import time # 导入time模块,用于时间操作
class TcpClient:
server_addr = ('127.0.0.1', 9999) # 服务器地址和端口
order = 2 # 初始指令数
def __init__(self):
self.tcp_cli_socket = socket.socket() # 创建TCP客户端套接字
def msg_recv(self):
"""
接收数据
"""
while True:
data = self.tcp_cli_socket.recv(1024).decode('utf-8').strip() # 接收数据并解码
info_list = data.split(" ") # 切割命令
if data == "exit":
print('客户端退出')
self.tcp_cli_socket.close()
break
if info_list[-1] == '-ord':
if int(info_list[0])/2.0 == int(int(info_list[0])/2):
print('\033[1;32m通过\033[0m') # 在控制台中打印通过信息(绿色)
print("接收可行指令,并更新标志数为:", int(info_list[0])) # 打印接收到的指令信息
self.order = int(int(info_list[0])/2.0) # 更新指令数
else:
print('不通过') # 打印不通过信息
else:
print(data) # 打印其他接收到的数据
def msg_send(self):
"""
发送数据
"""
while True:
data_info = input("请发送指令:") # 用户输入指令
if data_info == "exit":
self.tcp_cli_socket.send(data_info.encode("utf-8")) # 发送退出指令
break
else:
self.tcp_cli_socket.send((data_info + ' -ta').encode("utf-8")) # 发送带有标志的指令
def order_send(self):
"""
定时发送指令
"""
while True:
time.sleep(5) # 每隔5秒发送一次指令
self.tcp_cli_socket.send(('{}'.format(self.order) + ' -tas').encode("utf-8")) # 发送同步指令
print("给服务器发送指令:%d" % self.order) # 打印发送的指令数
def start(self):
"""
连接服务器
"""
try:
self.tcp_cli_socket.connect(self.server_addr) # 连接服务器
except Exception as e:
print("连接失败,请重试!")
self.tcp_cli_socket.close()
print(e)
return
while True:
name = input("输入客户端编号:") # 输入客户端编号
self.tcp_cli_socket.send((name + ' -n').encode('utf-8')) # 发送注册指令
data = self.tcp_cli_socket.recv(128).decode('utf-8') # 接收服务器返回信息
print(data) # 打印返回信息
if data == "OK":
print("你已成功进入服务器")
break
else:
print(data)
t = Thread(target=self.msg_recv) # 创建信息接收线程
t.start()
t1 = Thread(target=self.msg_send) # 创建信息发送线程
t1.start()
t2 = Thread(target=self.order_send) # 创建定时指令发送线程
t2.start()
if __name__ == '__main__':
client = TcpClient()
client.start()
4.3 测试结果
运行服务器端:
运行客户端:
文章若有不当和不正确之处,还望理解与指出。由于部分文字、图片等来源于互联网,无法核实真实出处,如涉及相关争议,请联系博主删除。如有错误、疑问和侵权,欢迎评论留言联系作者,或者私信联系作者。
本文转载自: https://blog.csdn.net/qq_51399582/article/details/140343724
版权归原作者 不去幼儿园 所有, 如有侵权,请联系我们删除。
版权归原作者 不去幼儿园 所有, 如有侵权,请联系我们删除。