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