• 多线程实现tcp聊天服务器


     

    多线程tcp  server & client 

    tcp服务端(多线程):

     1 from socket import *
     2 from threading import Thread
     3 
     4 def client(socket_client, msg_addr):
     5     print(">>>有新客户端连接<<<")
     6     try:
     7         while True:
     8             # 接受客户端发来的信息
     9             msg = socket_client.recv(1024)
    10             if msg:
    11                 print("%s--> %s" % (msg_addr, msg.decode('utf-8')))
    12             else:
    13                 print(msg_addr)
    14                 print("客户端已断开连接...")
    15                 break
    16     except:
    17         socket_client.close()
    18 
    19 
    20 def main():
    21     #建立一个套接字, AF_INET 表示遵从IPv4协议,SOCK_STREAM(流) 表示使用的是tcp协议传输
    22     # 若使用UDP协议传输, 则使用SOCK_DGRAM(数据报)
    23     server = socket(AF_INET, SOCK_STREAM)
    24 
    25     # 重复使用绑定的信息
    26     # 此处若服务端成为tcp四次挥手的第一次,那么服务端最后将等待 2MSL 的时间来接受客户端可能发来的ack
    27     # 在这段时间内,若服务器想重复执行,之前被占用的端口等服务不被释放,导致服务器不能被执行
    28     #此处加上这句话则解决了这个问题
    29     server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    30 
    31     msg_server = ("localhost", 7788)
    32     # 绑定本地的7788端口
    33     server.bind(msg_server)
    34     #开始监听
    35     #此处的listen中的值表示处于半连接和以连接状态的client总和
    36     server.listen(5)
    37     try:
    38         while True:
    39             #与客户端建立连接
    40             # socket_client表示为这个客户端创建出了包含tcp三次握手信息的新的套接字
    41             # msg_addr 包含这个客户端的信息
    42             socket_client, msg_addr = server.accept()
    43             #为每一个连接服务端的客户端创建出一个单独的线程,并传入上面的两个参数
    44             t = Thread(target=client, args=(socket_client,msg_addr))
    45             #在多线程中,创建的socket都共用一个socket_client,所以此时这个套接字不能被关闭
    46             #线程只是在cpu的某一个核心中不断的重复切换调用函数而已,所以数据其实都是一份,也是多线程中的数据可以共享的原因
    47             t.start()
    48 
    49     finally:
    50         server.close()
    51 
    52 if __name__ == "__main__":
    53     main()

     tcp服务端(多进程):

     1 from socket import *
     2 from multiprocessing import *
     3 
     4 def client(socket_client, msg_addr):
     5     print(">>>有新客户端连接<<<")
     6     try:
     7         while True:
     8             msg = socket_client.recv(1024)
     9             if msg:
    10                 print("%s--> %s" % (msg_addr, msg.decode('utf-8')))
    11             else:
    12                 print(msg_addr)
    13                 print("客户端已断开连接...")
    14                 break
    15     except:
    16         socket_client.close()
    17 
    18 
    19 def main():
    20     server = socket(AF_INET, SOCK_STREAM)
    21     server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    22 
    23     msg_server = ("localhost", 7788)
    24 
    25     server.bind(msg_server)
    26 
    27     server.listen(5)
    28     try:
    29         while True:
    30             socket_client, msg_addr = server.accept()
    31             t = Process(target=client, args=(socket_client,msg_addr))
    32             #创建一个子进程时,会向子进程中复制一份资源,所以主进程中的套接字可以关闭
    33             #在多进程中,创建一个进程即表示占用某一个cpu的资源,创建的进程比较多时,这些进程就会在cpu之间轮流占用
    34             t.start()
    35             socket_client.close()
    36 
    37     finally:
    38         server.close()
    39 
    40 if __name__ == "__main__":
    41     main()

     tcp客户端:

     1 from socket import *
     2 
     3 client = socket(AF_INET,SOCK_STREAM)
     4 
     5 server_msg = ("localhost",7788)
     6 #连接服务器
     7 client.connect(server_msg)
     8 
     9 while True:
    10     send = input("要发送的文本内容:")
    11     if send == 'q':
    12         break
    13 
    14     else:
    15         client.send((send).encode("utf-8"))
    16         print("发送成功!")
    17 
    18 client.close()

    关于tcp通信过程中的三次握手、四次挥手的过程

    三次握手:


    此过程中:

    第一次握手,客户端先发一个SYN请求并附带一个J的值给服务端

    第二次握手,服务端收到请求后解堵塞,发送一个SYN请求并附带一个K值,还发送了第一次握手后对客户端的响应包并附带在之前接收到的J值的基础上加上1,即J+1

    第三次握手,客户端收到服务端发来的SYN请求和K值后,再发送一个K+1的响应包给服务端

    至此,三次握手成功完成,客户端和服务端之间成功建立tcp链接

    四次挥手:

    此过程中:

    第一次挥手:客户端调用了close,发送一个结束请求附带一个x+2的值,和一个y+1的响应包给服务端

    第二次挥手:服务端发送x+3的响应包给客户端(其实每次的响应包的附带值都是在之前接收到的seq的值上加上1的结果)

    第三次挥手:服务端调用close,发送一个结束seq附带一个y+1的值给客户端,此时服务端成功断开连接

    第四次挥手:客户端接收到服务端的响应包和FIN请求后,回递一个y+2的响应包给服务端,此时的客户端进入time_wait状态,即继续等待2MSL的时间再完全断开链接(至于为什么要等待2MSL的时间,请看下文的MSL详解     ^_^     )

    要点:在四次挥手的过程中,哪一方先调用close, 哪一方就会在第三次挥手后继续等待2MSL的时间

    tcp通信过程中的2MSL的问题:

    2MSL即为在四次挥手的第三次过程中,先发起中断连接的一方将会继续等待2倍的MSL时间后再完全中断tcp连接

    MSL即为一个数据包在网络上存活的最长时间,即数据包从被发送到被接收所经历的最长时间

    等待2倍的MSL时间就是因为防止服务端没收到最后一次的ACK,即在2MSL的时间内,若服务端没收到最后的ACK,在超时时间(MSL)后,服务端会认为客户端没收到第三次挥手中的FIN,这时服务端会再发一份FIN,这时一共经历了2MSL的时间,而客户端此时等待了2MSL的时间,正好可以接收这一次服务端重发的FIN请求,从而有效的保证了所有的请求和回应都会被对方接收

  • 相关阅读:
    PHPsession实现用户登陆功能
    asp.net core mvc基于Redis实现分布式锁,C# WebApi接口防止高并发重复请求,分布式锁的接口幂等性实现
    OpenSSL 下载和私钥证书、CERTIFICATE证书生成
    Java byte[] 转C# byte[]
    如何在Etherscan.io 部署ETH以太坊智能合约 如何在15分钟内创建你的加密货币
    JavaScript 关于金额、数字验证的正则表达式
    web api .net C# mvc API返回XML文档的解析并取值
    C#和PHP加密结果一致的DES加密解密算法。php实现和c#一致的DES加密解密
    C#常用的图片处理方法-图片剪切、图片压缩、多图合并代码
    BitMap 图片格式与Base64Image格式互转方法
  • 原文地址:https://www.cnblogs.com/hgzero/p/8963965.html
Copyright © 2020-2023  润新知