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

原文地址:https://www.cnblogs.com/hgzero/p/8963965.html