TCP协议双向网络通讯---Python实现

       本篇文章是博主在人工智能、网络通讯等领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在Python

      Python(1)---《TCP协议双向网络通讯---Python实现》

TCP协议双向网络通讯---Python实现

目录

一、TCP协议的基本特点

二、TCP协议的连接过程

三、TCP协议的数据传输过程

四、TCP协议的连接关闭过程

五、TCP协议的安全挑战与防范策略

六、Python编程实现

4.1TcpServer服务器端代码

4.2 TcpClient客户端代码

4.3 测试结果


        TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。它工作在OSI模型的传输层,旨在确保数据在IP网络中的可靠传输。以下是对TCP协议网络通讯的详细论述:

        本文主要使用Python实现TCP协议双向网络通讯,即服务器和客户端都可以实现信息的收发,采用多线程的方式,能够实现服务器的一对多收发信息

一、TCP协议的基本特点

  1. 面向连接:TCP协议在数据传输之前,必须先在通信双方之间建立连接。这种连接是一对一的,即一个TCP连接只能有两个端点。连接建立后,双方可以开始传输数据,直到连接被关闭。(使用多线程可以实现一对多等)

  2. 可靠性:TCP协议通过一系列机制来确保数据的可靠传输。这些机制包括序列号、确认应答、超时重传、流量控制等。TCP协议能够确保数据无差错、不丢失、不重复,并且按序到达。

  3. 基于字节流:TCP协议将应用程序交下来的数据看成是一连串的无结构的字节流。TCP不关心应用程序写入数据的含义和内容,它只负责将这些数据以字节流的形式发送到对方。


 二、TCP协议的连接过程

TCP协议的连接过程通常被称为“三次握手”:

  1. 第一次握手:客户端发送一个SYN(同步序列号)报文段到服务器,并在这个报文段中设置序列号字段的值。此时,客户端进入SYN_SENT状态。

  2. 第二次握手:服务器收到客户端的SYN报文段后,发送一个SYN/ACK(同步/确认)报文段给客户端,确认客户端的SYN报文段,并设置自己的序列号。此时,服务器进入SYN_RCVD状态。

  3. 第三次握手:客户端收到服务器的SYN/ACK报文段后,发送一个ACK(确认)报文段给服务器,确认服务器的SYN/ACK报文段。此时,客户端和服务器都进入ESTABLISHED(已建立连接)状态,TCP连接成功建立。


 三、TCP协议的数据传输过程

TCP协议在数据传输过程中,通过序列号、确认应答等机制来确保数据的可靠传输。

  1. 序列号:TCP头部的序列号用于确保数据的有序传输。它表示在这个TCP段中的第一个字节的序号。在建立连接时,双方都随机生成一个初始序列号(ISN),并以此作为后续数据传输的基准。

  2. 确认应答:TCP头部中的确认应答号是接收端期望收到的下一个字节的序列号。发送端收到确认应答后,可以认为所有在此之前的序列号的包已经被接收。

  3. 超时重传:如果发送方在一定时间内没有收到接收方的确认应答,就会认为该报文段已经丢失,并重新发送该报文段。

  4. 流量控制:TCP协议通过窗口大小来控制发送方的数据流量,以适应接收方的处理能力。接收方在确认应答中告知发送方自己的窗口大小,发送方根据这个值来控制数据的发送量。

  5. 拥塞控制:TCP协议还通过拥塞控制机制来防止网络过载。当网络拥塞时,TCP会减慢数据的发送速率,以减轻网络的负担。


 四、TCP协议的连接关闭过程

TCP协议的连接关闭过程通常被称为“四次挥手”:

  1. 第一次挥手:客户端发送一个FIN(结束连接)报文段给服务器,表示客户端没有数据要发送了,请求关闭连接。此时,客户端进入FIN_WAIT_1状态。

  2. 第二次挥手:服务器收到客户端的FIN报文段后,发送一个ACK报文段给客户端,确认收到客户端的FIN报文段。此时,服务器进入CLOSE_WAIT状态,客户端进入FIN_WAIT_2状态。

  3. 第三次挥手:服务器在关闭连接前,如果还有数据要发送给客户端,可以继续发送数据。当服务器没有数据要发送时,发送一个FIN报文段给客户端,表示服务器也没有数据要发送了,请求关闭连接。此时,服务器进入LAST_ACK状态。

  4. 第四次挥手:客户端收到服务器的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  # 标志位1self.flag2 = 0  # 标志位2def 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()breakelse:try:A = info_list[-2], info_list[-1]except IndexError:conn.send((self.get_time() + ' 系统:无法识别您的指令,请重新输入!').encode('gb2312'))continueif 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 = connself.user_name[info_list[0]] = conn_nodeelif 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.sumself.send_to_all(msg)elif info_list[-1] == '-tas':conn_node.flag = 1self.sum = self.sum + int(info_list[0])msg = self.get_time() + ' %s:贡献' % conn_node.Name + ' '.join(info_list[:-1]) + '。总数:%d' % self.sumself.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:continuedef 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 = 0if 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 = 0if __name__ == '__main__':HOST = "127.0.0.1"  # 主机回送地址,测试使用# HOST = '192.168.8.111'  # 局域网ip地址,实际情况使用POST = 9999server = 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()breakif 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"))  # 发送退出指令breakelse: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)returnwhile 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("你已成功进入服务器")breakelse: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 测试结果

运行服务器端:

运行客户端:


     文章若有不当和不正确之处,还望理解与指出。由于部分文字、图片等来源于互联网,无法核实真实出处,如涉及相关争议,请联系博主删除。如有错误、疑问和侵权,欢迎评论留言联系作者,或者私信联系作者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/870249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

捷配笔记-PCB阻焊颜色对产品有什么影响?

阻焊层也称为阻焊层或阻焊剂。它是一种薄的聚合物层,应用于(PCB)。阻焊层的目的是保护PCB表面,并有助于防止焊桥。焊桥是两个导体之间的无意连接,通常是由于存在一小块焊料。需要注意的是,阻焊层被视为其单…

electron实现右键菜单保存图片功能

1.创建窗口,加载页面,代码如下: //打开窗口const {ipcMain, BrowserWindow} require("electron") const saveImage require("../ipcMain/saveImage") let win null; ipcMain.handle(on-open-event, (event, args) &g…

数字信号处理及MATLAB仿真(5)——z变换

采样的其他概念咱们后面再慢慢的讲述吧,先把z变换的程序给大家展示一下,总的来说呢,就用一个函数——ztran就行了。在 MATLAB 中,可以使用 ztrans 函数来进行 Z 变换。ztrans 函数用于对离散时间信号或系统进行 Z 变换&#xff0c…

mysql中select语句的执行顺序

执行顺序是什么? Form 这一阶段读取表的数据,并准备执行后续的操作。如果有多表连接,这一步也会涉及连接操作(INNER JOIN、LEFT JOIN、RIGHT JOIN、CROSS JOIN 等)。 ON 在进行表连接时,使用 ON 关键字指…

C++入门 模仿mysql控制台输出表格

一、 说明 控制台输出表格&#xff0c;自适应宽度 二、 源码 #include <iostream> #include <map> #include <string> #include <vector>using namespace std;void printTable(vector<vector<string>> *pTableData) {int row pTableDa…

VS2022 git拉取/推送代码错误

第一步&#xff1a;打开VS2022 第二步&#xff1a;工具->选项->源代码管理->Git 全局设置 第三步&#xff1a;加密网络提供程序设置为&#xff1a;OpenSSL 完结&#xff1a;

基于java+springboot+vue实现的大学城水电管理系统(文末源码+Lw)106

基于SpringBootVue的实现的大学城水电管理系统&#xff08;源码数据库万字Lun文流程图ER图结构图演示视频软件包&#xff09; 系统功能&#xff1a; 本大学城水电管理系统 管理员功能有个人中心&#xff0c;用户管理&#xff0c;领用设备管理&#xff0c;消耗设备管理&#x…

AV1编码标准中块分区技术详细说明

libaom 开放媒体联盟视频1&#xff08;AV1&#xff09;编解码器的框架基于一种混合视频编码结构&#xff0c;由几个主要的功能模块组成&#xff0c;如预测、变换、量化、熵编码和环路滤波。每个功能模块使用特定类型的视频编码技术处理输入数据&#xff0c;其输出被送入另一个功…

科研绘图系列:R语言金字塔图(pyramid plot)

介绍 金字塔图(Pyramid chart)是一种用于展示人口统计数据的图表,特别是用于展示不同年龄段的人口数量。这种图表通常用于展示人口结构,比如性别和年龄的分布。 特点: 年龄分层:金字塔图按年龄分层,每一层代表一个年龄组。性别区分:通常,男性和女性的数据会被分别展…

FiddlerScript Rules修改-更改发包中的cookie

直接在fiddler script editor中增加如下处理代码即可 推荐文档oSession -- 参数说明 测试笔记 看云

fork的理解

一. 注意点 1.进程是并发的&#xff0c;主进程和子进程同时进行&#xff0c;效率高2.子进程产生时是完全复制主进程的状态的&#xff0c;只有在产生修改的时候才会单独分配资源。 二. 下面程序一共应该为8个进程&#xff0c;但code的终端看到只有7个进程号的原因。因为fork返…

FastAPI 学习之路(四十一)定制返回Response

接口中返回xml格式内容 from fastapi import FastAPI, Responseapp FastAPI()# ① xml app.get("/legacy") def get_legacy_data():data """<?xml version"1.0"?><shampoo><Header>Apply shampoo here.</Header&…

【Linux】管道命令

命令执行的时候有时会输出数据&#xff0c;有的命令输出的数据太繁杂了。 那么我们怎么去筛选这些信息来得到我们所想要的格式&#xff1f; 这就牵涉到管道命令的问题了&#xff08;pipe&#xff09;&#xff0c;管道命令使用的是【|】这个界定符号。另外&#xff0c;管道命令与…

centos 安装ffmpeg

这个错误表明在你的 CentOS 系统的默认仓库中没有 ffmpeg 包。CentOS 的默认仓库通常不包含 ffmpeg&#xff0c;因为它涉及一些许可证问题。但是&#xff0c;你可以通过添加第三方仓库来安装 ffmpeg。 使用 EPEL 和 RPM Fusion 仓库 # 安装 EPEL 仓库 sudo yum install epel-…

项目管理工具评测:2024年国内外最顶级的10款项目管理工具排行

国内外涌现出众多优秀的项目管理工具&#xff0c;它们各自在功能、易用性、集成能力等方面展现出独特优势。以下是国内外顶级的10款项目管理工具&#xff1a; 一、进度猫 推荐理由&#xff1a;进度猫以其直观的任务管理和进度跟踪功能&#xff0c;成为许多团队和项目的首选…

MybatisPlus 使用教程

MyBatisPlus使用教程 文章目录 MyBatisPlus使用教程1、使用方式1.1 引入依赖1.2 构建mapper接口 2、常用注解2.1 TableName2.2 TableId2.3 TableField MyBatisPlus顾名思义便是对MyBatis的加强版&#xff0c;但两者本身并不冲突(只做增强不做改变)&#xff1a; 引入它并不会对原…

FastAPI 学习之路(三十九)对开发接口进行测试

概况 对于开发好的接口需要进行测试之后才能发布。当我们在开发的时候&#xff0c;没有提前测试&#xff0c;我们也要对我们自己的接口进行测试&#xff0c;那么FastApi自身也带有针对开发的接口进行测试的功能。我们看下FastApi官方给我们提供了什么样的支持。 接口还是基于…

笔记本硬盘数据恢复的6种方法!简单易懂

可以从笔记本电脑硬盘恢复已删除的数据吗&#xff1f; “我不小心删除了笔记本电脑硬盘上的重要数据。请问我可以在笔记本电脑硬盘上恢复已删除的数据吗&#xff1f;如果可以&#xff0c;我应该怎么做才能恢复数据呢&#xff1f;” 很多笔记本电脑用户可能会不小心地从电脑中…

JavaSE 面向对象程序设计进阶 IO 练习读取多个对象

练习读取多个对象 用序列化流将对象写入文件 import java.io.*; import java.nio.charset.Charset;public class Main {public static void main(String[] args) throws IOException, ClassNotFoundException {//序列化多个对象Person p1new Person("多多", 男,20)…

Elasticsearch 基础查询语句汇总

Elasticsearch 基础查询语句汇总 准备条件指定id查询一指定id查询二指定多个id查询区间查询模糊查询多字段查询分页查询查询总数量 准备条件 以下查询操作都基于索引crm_clue来操作&#xff0c;索引已经建过了&#xff0c;本文主要讲Elasticsearch查询语句&#xff0c;下面开始…