1. 程式人生 > >python 實戰之模仿開發QQ聊天軟體(三)TCP/IP伺服器與客戶端建設

python 實戰之模仿開發QQ聊天軟體(三)TCP/IP伺服器與客戶端建設

無論是p2p還是c/s還是b/s,只要用到通訊,必然是要用到今天寫的這個。

TCP/IP是網路軟體最核心的部分,缺少這個你只能當做單機遊戲玩。

TCP/IP,只需要搞清楚udp和tcp這兩個就可以了。

兩者的區別在於

udp每次傳送資訊都需要傳送ip和埠號,可以比作寄信,tcp只需要傳送一次ip和埠號直到客戶端執行結束,可以比作是打電話。

udp的保密性不好,所以在做通訊的時候基本選擇tcp,這篇文章只提tcp

tcp客戶端與伺服器的工作模式如下圖所示:

先解釋一下ip,埠號,套接字都是什麼意思:

打個比方: 朋友使用導航去你家找你玩,必然需要知道你住在哪裡。

ip就是哪個街道哪個小區哪一撞哪一單元哪一層,埠號是哪一個門。

套接字就是他出發地和目的地,也就是說套接字是兩個端點。

                                           客戶端模型

import socket

                            1.使用socket建立套接字:

tcp_client_socket = socket.socket(socket.AF_INET,socekt.SOCK_STREAM)

socket(AF_INET,SOCK_STREAM)

AF_INET決定了要用ipv4地址(32位的)與埠號(16位的)的組合

流式Socket(SOCK_STREAM)是一種面向連線的Socket

                             2.使用connect()方法連線伺服器

tcp_client_socket.connect(("192.168.0.1",8080))

("192.168.0.1",8080)是一個元組,首元素為伺服器ip地址,字元型的,8080是伺服器當前程式使用埠號,整型的

我的伺服器和客戶端是在同一個網段的,所以可以直接連線,不同網段需要使用交換機或者網線直連。(校園網wifi會組建內網,即使是同一個路由器也不行,網線直連後修改ip地址就可以了)

                             3.使用recv()、send()方法收發資料

recv_data = tcp_client_socket.recv(1024)  # 客戶端接收伺服器發來的資訊最大長度為1K
data = recv_data.decode("utf-8")    #解碼
tcp_client_socket.send("123".encode("utf-8))  # 客戶端向伺服器以utf-8的編碼格式傳送123

傳送字串的時候需要注意,如果是windows上執行的伺服器編碼格式是gbk,而linux,mac都是utf-8

                            4.使用close()方法關閉套接字

tcp_client_socket.close()

                                                   伺服器模型

                                1.使用socket建立套接字

tcp_service_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

                                2.使用bind()繫結本地資訊

tcp_service_socket.bind(("",8080))

                               3.使用listen()加入監聽等待客戶端接入

tcp_service_socket.listen(128)

                                 4.使用accept()為客戶建立新套接字並讀取使用者ip和埠號

new_client_socket,client_addr = tcp_service_socket.accpet()

這個過程可能不太好理解,大概意思就是你打電話給10086,首先是打給10086的主機,然後主機分配一個話務員給你,幫你辦理業務

                                 5.使用recv()/send()傳送資料

new_client_socket.send()
recv_data = new.client_socket.recv(1024)

這個和客戶端是沒有區別的

                                6.關閉新套接字

new_client_socket.close()

相當於客服業務結束,結束通話他所在的分機

                               7.關閉源套接字

tcp_service_socket.close()

樣例:檔案下載器

伺服器主機ip 192.168.0.1 埠號 8081

客戶端主機ip 192.168.0.2 埠由系統分配

客戶端原始碼

import socket

def main():
	# 建立套接字
	tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

	# 獲取伺服器的ip port
	# addr = ("192.168.0.1",8081)

	# 連線伺服器
	tcp_socket.connect(("192.168.0.1",8081))

	# 獲取下載檔名
	download_file_name = input("檔名")

	# 將檔名傳送到伺服器
	tcp_socket.send(download_file_name.encode("utf-8"))

	# 接受檔案中的資料
	recv_data = tcp_socket.recv(1024*1024)
	
	if recv_data:
		# 儲存接收的資料到檔案中
		with open("[新]" + download_file_name,"wb") as f:
			f.write(recv_data)

	# 關閉套接字
	tcp_socket.close()

if __name__ == "__main__":
	main()

伺服器原始碼

import socket

def send_file_2_client(new_client_socket,client_addr):
    # 接收客戶端傳送的請求
    file_name = new_client_socket.recv(1024).decode("utf-8")
    print("客戶端(%s)需要下載的檔名為:%s" % (str(client_addr),file_name))

    file_content = None
    # 開啟檔案,讀取資料
    try:
        f = open(file_name,"rb")
        file_content = f.read()
        f.close()

    except Exception as ret:
        print("沒有要下載的檔案(%s)" % file_name)

    # 傳送資料給客戶端
    if file_content:
        # new_client_socket.send("hahahah".encode("utf-8"))
        new_client_socket.send(file_content)

def main():
   
    # 建立socket
    tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)


    # 繫結本地資訊
    tcp_server_socket.bind(("",8081))

    # 加入監聽套接字等待客戶端連線
    tcp_server_socket.listen(128)

    while True:
        print("等待一個新客戶端的到來")
        # 建立新套接字與客戶端連線
        new_client_socket,client_addr = tcp_server_socket.accept()
        print("新客戶端已經到來了:%s" % str(client_addr))

        # 傳送檔案
        send_file_2_client(new_client_socket,client_addr)
        
        # 關閉套接字
        new_client_socket.close()

    tcp_server_socket.close()

if __name__ == '__main__':
    main()

當然,目前這個只能完成單點操作,加入thread可以實現多工模式