多线程实现tcp聊天服务器
Posted Pray
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程实现tcp聊天服务器相关的知识,希望对你有一定的参考价值。
1. 多线程tcp server & client
1.1 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()
1.2 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()
1.3 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()
2. tcp通信过程
2.1 三次握手
- 此过程中:
- 第一次握手,客户端先发一个SYN请求并附带一个J的值给服务端
- 第二次握手,服务端收到请求后解堵塞,发送一个SYN请求并附带一个K值,还发送了第一次握手后对客户端的响应包并附带在之前接收到的J值的基础上加上1,即J+1
- 第三次握手,客户端收到服务端发来的SYN请求和K值后,再发送一个K+1的响应包给服务端
- 至此,三次握手成功完成,客户端和服务端之间成功建立tcp链接
2.2 四次挥手
- 此过程中:
- 第一次挥手:客户端调用了close,发送一个结束请求附带一个x+2的值,和一个y+1的响应包给服务端
- 第二次挥手:服务端发送x+3的响应包给客户端(其实每次的响应包的附带值都是在之前接收到的seq的值上加上1的结果)
- 第三次挥手:服务端调用close,发送一个结束seq附带一个y+1的值给客户端,此时服务端成功断开连接
- 第四次挥手:客户端接收到服务端的响应包和FIN请求后,回递一个y+2的响应包给服务端,此时的客户端进入time_wait状态,即继续等待2MSL的时间再完全断开链接(至于为什么要等待2MSL的时间,请看下文的MSL详解 ^_^ )
-
要点:在四次挥手的过程中,哪一方先调用close, 哪一方就会在第三次挥手后继续等待2MSL的时间
2.3 关于tcp通信过程中的2MSL
- 2MSL即为在四次挥手的第三次过程中,先发起中断连接的一方将会继续等待2倍的MSL时间后再完全中断tcp连接
- MSL即为一个数据包在网络上存活的最长时间,即数据包从被发送到被接收所经历的最长时间
等待2倍的MSL时间就是因为防止服务端没收到最后一次的ACK,即在2MSL的时间内,若服务端没收到最后的ACK,在超时时间(MSL)后,服务端会认为客户端没收到第三次挥手中的FIN,这时服务端会再发一份FIN,这时一共经历了2MSL的时间,而客户端此时等待了2MSL的时间,正好可以接收这一次服务端重发的FIN请求,从而有效的保证了所有的请求和回应都会被对方接收。
以上是关于多线程实现tcp聊天服务器的主要内容,如果未能解决你的问题,请参考以下文章