zoukankan      html  css  js  c++  java
  • 多线程实现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请求,从而有效的保证了所有的请求和回应都会被对方接收

  • 相关阅读:
    leetcode 86. Partition List
    leetcode 303. Range Sum Query
    leetcode 1310. XOR Queries of a Subarray
    leetcode 1309. Decrypt String from Alphabet to Integer Mapping
    leetcode 215. Kth Largest Element in an Array
    将numpy.ndarray写入excel
    leetcode 1021 Remove Outermost Parentheses
    leetcode 1306. Jump Game III
    leetcode 1305. All Elements in Two Binary Search Trees
    ICCV2019 oral:Wavelet Domain Style Transfer for an Effective Perception-distortion Tradeoff in Single Image Super-Resolution
  • 原文地址:https://www.cnblogs.com/hgzero/p/8963965.html
Copyright © 2011-2022 走看看