多線程實現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請求,從而有效的保證了所有的請求和回應都會被對方接收

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM