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

news2024/9/17 7:43:23

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

运行服务器端:

运行客户端:


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

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

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

相关文章

基于uni-app与图鸟UI的知识付费小程序模板

一、项目概述 在知识经济蓬勃发展的背景下,移动互联网成为知识传播与消费的重要渠道。本项目旨在利用前沿的前端技术栈——uni-app及高效UI框架图鸟UI,打造一款集多功能于一体的、面向广大求知者的知识付费平台移动端模板。该模板旨在简化开发流程&…

《大明混一图》: 令人叹为观止的古代世界地图

关注我们 - 数字罗塞塔计划 - 《大明混一图》是我国目前保存尺寸最大、最完整、年代最久远,且由中国人自己绘制的世界地图,2003年10月被列入《中国档案文献遗产名录》,现保存于中国第一历史档案馆。据学者们研究,这幅地图大约是…

【SD教程】全网最详细的AI绘画提示词语法讲解!(附提示词插件包)

AI绘画提示词如何写?对于入门的小伙伴来说这是一个大问题,提示词写的好,才能有高质量的作品,那今天给大家详细讲解一下,建议点赞收藏! 文末附提示词插件 一、SD提示词基础 AI绘画提示词基本规则 1、提示…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《计及负荷时空特性的高速公路链式微网光-储-充容量优化配置方法》

本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

政企单位光纤资源高效管理与优化策略

引言 随着信息技术的飞速发展,政企单位对于通信基础设施的管理要求日益提高。然而,传统的管理模式,如Excel表格记录和纸质审批流程,已难以满足当前复杂多变的业务需求。在此背景下,我们实施了光纤管理的数字化转型项目…

仕考网:公务员如何备考申论

在备战公务员考试的申论部分时,掌握一定的技巧,遵循特定的步骤是至关重要的。以下是一些备考策略,希望能帮助到大家: 1. 掌握考试大纲和命题趋势 在考试大纲中明确了题目的类型和可能涉及的主题范围,考生可以聚焦到关…

免费录制视频的软件,推荐3款,总有一款适合你!

在数字化时代,视频录制与分享已成为日常生活和工作中的重要组成部分。无论是录制游戏过程、教程讲解还是网络会议,一款好用的录制视频软件能够帮助我们更便捷地实现这个目标。然而,许多录制视频的软件都是收费的,这对于很多人来说…

【操作系统】手把手带你搭建DNS服务器!

DNS服务器 DNS服务器指域名系统或者域名服务。域名系统为Internet上的主机分配域名地址和IP地址,用户使用域名地址,该系统就会自动把域名地址转为IP地址。域名服务是运行域名系统的Internet工具。执行域名服务的服务器称之为DNS服务器,通过DN…

在创建jsp项目中解决无法连接数据库以及junit问题

💻博主现有专栏: C51单片机(STC89C516),c语言,c,离散数学,算法设计与分析,数据结构,Python,Java基础,MySQL,linux&#xf…

入驻长沙!全球数据资产理事会长江中游中心挂牌成立

在全球数据资产化浪潮的推动下,长江中游地区迎来了其在数字经济领域的重要里程碑。 近日,《数据资产长江中游生态论坛暨数据资产入表和价值转化研讨会》于长沙圆满落幕,会上各产业专家、企业高管,围绕数据产品开发、数据资产融资…

小程序复制功能不可用 setClipboardData:fail no permission

先上图 用户协议剪切板也更新但是依旧报错了 最后在公众平台通知里发现是用户之前小程序有规格被封禁了该功能

IP地址SSL证书的申请流程-五大步骤教你如何申请

网站需要申请SSL证书,没有域名或者不方便提供域名,只有IP地址也可以申请SSL证书。特别需要注意的是,为IP地址申请ssl证书是需要开放443或者80端口,一般开放几分钟用来验证IP管理权即可! 申请IP地址的SSL证书&#xff…

13--memcache与redis

前言:数据库读取速度较慢一直是无法解决的问题,大型网站应对的方式主要是使用缓存服务器来缓解这种情况,减少数据库访问次数,以提高动态Web等应用的速度、提高可扩展性。 1、简介 Memcached/redis是高性能的分布式内存缓存服务器…

阿里面试:canal+MQ,会有乱序的问题吗?

阿里面试:canalMQ,会有乱序的问题吗? 尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中,最近有小伙伴拿到了一线互联网企业如阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题…

一个 Docker 搭建的自动化电视剧下载和管理工具

大家好,我是CodeQi! 一个标准的“追剧狂人”。每周都有新剧上线,每天都要时刻关注各大影视平台的更新,这无疑是一项体力与脑力并存的艰巨任务。 于是,我决定为自己打造一个自动化的电视剧下载和管理工具。作为一个程序员,用 Docker 搭建这种自动化工具简直是小菜一碟。…

Vue组件通信props和$emit用法

父传子&#xff0c;通过props 子传父&#xff0c;通过$emit App.vue <template><div class"app" style"border: 3px solid #000; margin: 10px">我是APP组件<!-- 1.给组件标签&#xff0c;添加属性方式 赋值 --><!-- 添加属性传值 …

PointCloudLib GridMinimum获取栅格最低点 C++版本

测试效果 简介 在点云库(Point Cloud Library, PCL)中,如果你想要获取一个栅格(Grid)内的最低点,这通常意味着你需要先对点云数据进行某种形式的栅格化处理,然后在每个栅格内寻找最低的点。 测试代码 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointC…

2024透明加密软件最新推荐丨11款好用的透明加密软件

在数字化时代&#xff0c;文件与数据安全愈发重要&#xff0c;保护信息安全刻不容缓。文件加密软件应运而生&#xff0c;成为了信息安全的重要帮手。透明加密技术凭借众多的优点备受青睐。那么&#xff0c;什么是透明加密技术呢&#xff1f; 透明加密技术是一种在不影响用户正…

旷野之间11 - 开源 SORA 已问世!训练您自己的 SORA 模型!

​​​​​ 目前最接近 SORA 的开源模型是 Latte,它采用了与 SORA 相同的 Vision Transformer 架构。Vision Transformer 究竟有何独特之处?它与之前的方法有何不同? Latte 尚未开源其文本转视频训练代码。我们复制了论文中的文本转视频训练代码,并将其提供给任何人使用,…

帕金森小伙伴必看!这些维生素让你的生活更“稳“定!

&#x1f308; 嗨&#xff0c;亲爱的小伙伴们&#xff01;今天来跟大家聊聊帕金森病患者应该补充哪些维生素的小知识&#xff0c;让我们的身体更健康&#xff0c;生活更美好哦&#xff01;&#x1f609; &#x1f34e; 维生素C 你知道吗&#xff1f;维生素C不仅能让我们的皮肤…