|
本篇文章是博主在人工智能、网络通讯等领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在Python:  ython(1)---《TCP协议双向网络通讯---Python实现》TCP协议双向网络通讯---Python实现目录一、TCP协议的基本特点二、TCP协议的连接过程三、TCP协议的数据传输过程四、TCP协议的连接关闭过程五、TCP协议的安全挑战与防范策略六、Python编程实现4.1TcpServer服务器端代码4.2 TcpClient客户端代码4.3测试结果 TCP(TransmissionControlProtocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。它工作在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请求频率、限制连接数量等措施来防止SYNFlood攻击等恶意攻击。对于接收到的TCP数据包,要验证其来源是否合法,防止伪造数据六、Python编程实现4.1TcpServer服务器端代码importsocket#导入socket模块,用于网络通信fromthreadingimportThread#导入Thread类,用于多线程处理importtime#导入time模块,用于时间操作importsys#导入sys模块,用于系统相关操作importrandom#导入random模块,用于生成随机数#创建存储对象classNode:def__init__(self):self.Name=None#用户名self.Thr=None#套接字连接对象self.flag=0#标志位classTcpServer: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#标志位2defstart(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()whileTrue:try:#监听客户端的地址和发送的消息conn,addr=self.tcp_socket.accept()#返回值为套接字和网络地址exceptKeyboardInterrupt:#按下ctrl+c会触发此异常self.tcp_socket.close()#关闭套接字sys.exit("\n"+self.get_time()+"系统:服务器安全退出!")#程序直接退出,不捕捉异常exceptExceptionase:print(e)continue#为当前链接创建线程t=Thread(target=self.do_request,args=(conn,))t.start()defdo_request(self,conn):"""监听客户端传送的消息,并将该消息发送给所有用户"""conn_node=Node()whileTrue:recv_data=conn.recv(1024).decode('utf-8').strip()#获取客户端发来的数据#print(recv_data)info_list=recv_data.split("")#切割命令#print(info_list)#如果接收到命令为exit,则表示该用户退出,删除对应用户信息,关闭连接ifrecv_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]exceptIndexError:conn.send((self.get_time()+'系统:无法识别您的指令,请重新输入!').encode('gb2312'))continueifinfo_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_nodeelifinfo_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)elifinfo_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)defsend_to_all(self,msg):"""对所有用户发送消息"""print(msg)foriinself.user_name.values():i.Thr.send(msg.encode('utf-8'))defsend_to_other(self,name,msg):"""对除了当前发送信息的用户外的其他用户发送消息"""#print("收到消息:"+msg)forninself.user_name:ifn!=name:self.user_name[n].Thr.send(msg.encode('utf-8'))else:continuedefget_time(self):"""返回当前系统时间"""return'['+time.strftime("%Y-%m-%d%H:%M:%S",time.localtime())+']'#服务器定时发送指令deftimer_to_order(self):whileTrue:foriinself.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定时发送一次#服务器同步发送指令defsyn_to_order(self):whileTrue:flag_sum=0#标志位总和forkeyinself.user_name:#获得字典的键flag_sum+=self.user_name[key].flag#计算各客户端的flag位time.sleep(0.01)#延迟10s,防止迭代时,字典数量改变ifflag_sum==len(self.user_name)andflag_sum!=0:#判断服务器是否都接受到flag标志end_flag=1#终值标志位else:end_flag=0ifend_flag:print("\n")foriinself.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客户端代码 (可以新建多个客户端)importsocket#导入socket模块,用于网络通信fromthreadingimportThread#导入Thread类,用于多线程处理importtime#导入time模块,用于时间操作classTcpClient:server_addr=('127.0.0.1',9999)#服务器地址和端口order=2#初始指令数def__init__(self):self.tcp_cli_socket=socket.socket()#创建TCP客户端套接字defmsg_recv(self):"""接收数据"""whileTrue:data=self.tcp_cli_socket.recv(1024).decode('utf-8').strip()#接收数据并解码info_list=data.split("")#切割命令ifdata=="exit":print('客户端退出')self.tcp_cli_socket.close()breakifinfo_list[-1]=='-ord':ifint(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)#打印其他接收到的数据defmsg_send(self):"""发送数据"""whileTrue:data_info=input("请发送指令:")#用户输入指令ifdata_info=="exit":self.tcp_cli_socket.send(data_info.encode("utf-8"))#发送退出指令breakelse:self.tcp_cli_socket.send((data_info+'-ta').encode("utf-8"))#发送带有标志的指令deforder_send(self):"""定时发送指令"""whileTrue:time.sleep(5)#每隔5秒发送一次指令self.tcp_cli_socket.send(('{}'.format(self.order)+'-tas').encode("utf-8"))#发送同步指令print("给服务器发送指令:%d"%self.order)#打印发送的指令数defstart(self):"""连接服务器"""try:self.tcp_cli_socket.connect(self.server_addr)#连接服务器exceptExceptionase:print("连接失败,请重试!")self.tcp_cli_socket.close()print(e)returnwhileTrue:name=input("输入客户端编号:")#输入客户端编号self.tcp_cli_socket.send((name+'-n').encode('utf-8'))#发送注册指令data=self.tcp_cli_socket.recv(128).decode('utf-8')#接收服务器返回信息print(data)#打印返回信息ifdata=="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测试结果运行服务器端:运行客户端: 文章若有不当和不正确之处,还望理解与指出。由于部分文字、图片等来源于互联网,无法核实真实出处,如涉及相关争议,请联系博主删除。如有错误、疑问和侵权,欢迎评论留言联系作者,或者私信联系作者。
|
|