Java&Python的TCP&UDP通訊-網路程式設計

Harvey丶北極熊發表於2020-11-01

PS:資料傳輸-Java和python的TCP&UDP程式設計。

Java TCP&UDP 通訊-網路程式設計

1.網路程式設計的目的
  • 實現資源的共享
  • 實現資料的傳輸和下載
1.1軟體的架構

網路的通訊都是基於軟體結構實現的:C/S結構 和 B/S結構

  • C/S架構 全稱為Client /Server 架構,就是客服端和伺服器端
    • 常見的程式像QQ、迅雷、IDEA等軟體
    • 優點 :客戶體驗好,因為客戶需要把客服端下載下來
    • 缺點:需要下載 後期需要維護比較麻煩
  • B/S架構 全稱為Browser/Server 架構 ,就是瀏覽器和伺服器架構
    • 常見的瀏覽器:谷歌、火狐等
    • 常見的軟體:博學谷、京東、淘寶
    • 優點 :方便 在網頁上就可以直接瀏覽這些伺服器,不需要下載
    • 缺點: 網路不好 客戶體驗效果極差
1.2網路通訊的三要素

網通通訊必須要有一定約定,不然無法實現計算機之間的通訊

  1. 協議:
    • 計算機網路客戶端與服務端通訊必須事先約定和彼此遵守的通訊規則
    • HTTP , FTP , TCP , UDP , SSH , SMTP
  2. IP地址:
    • ip地址全稱(Internet Protocol Address)網際網路協議地址
    • IP 地址分為兩種
      • IPv4:4個位元組,32位組成 192.168.70.70 每一位取值0-225
      • IPv6: 可以實現為所有裝置分配IP 128位
    • 本地的IP地址:127.0.0.1或者localhost

注意:只獲得IP地址的話,能連線到那個計算機,並不能連結到具體的應用

3.埠號:

  • 埠號就用來唯一標識裝置中的程式也就是應用程式
  • 用兩個位元組表示的整數,它的取值範圍是0~65535
    • 0~1023之間的埠號用於一些知名的網路服務和應用
    • 普通的應用程式需要使用1024以上的埠號

總結:利用這三要素,就可以實現網路的通訊

1.3網路通訊的分層和協議
應用層  :應用程式(QQ,微信,瀏覽器),可能用到的協議(HTTP,FTP,SMTP)通常程式設計師只需要關心這一層
傳輸層  :TCP/IP協議 - UDP協議    計算機網路工程師需要精通的協議
網路層  :IP協議  封裝自己的IP和對方的IP和埠
資料鏈路層 : 進入到硬體(網)
1.4 InetAddress類

在java中我們用InetAddress類來代表IP地址的物件,

這個類不能建立例項,通過靜態方法返回IP地址物件。

InetAddress類成員方法:

  • static InetAddress getLocalHost() 獲得本地主機IP地址物件
  • static InetAddress getByName(String host) 根據IP地址字串或主機名獲得對應的IP地址物件
  • String getHostName() 獲得主機名
  • String getHostAddress() 獲得IP地址字串

測試程式碼:

package com.net;

import java.io.IOException;
import java.net.InetAddress;

public class InetAddressDemo01 {
    public static void main(String[] args) throws IOException {
        //獲取IP地址物件
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost.getHostAddress());// 獲取ip地址
        System.out.println(localHost.getHostName()); //獲取主機名

       //用域名獲取域名ip物件
        InetAddress ip2 = InetAddress.getByName("www.baidu.com");
        System.out.println(ip2.getHostAddress());
        System.out.println(ip2.getHostName());

        // 判斷是否能通: ping  3s之前測試是否可通
        System.out.println(ip2.isReachable(3000)); // ping
    }
}

執行結果:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-rUT312Sh-1604207341186)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午9.22.54.png)]

1.5 UDP通訊的使用介紹

1.UDP協議的特點:

  • 面向無連線的協議
  • 傳送端只管傳送,不確認對方是否能收到
  • 基於資料包進行資料傳輸
  • 傳送資料的包的大小限制64KB以內
  • 因為面向無連線,速度快,但是不可靠,會丟失資料!

2.UDP協議相關的兩個類:

DatagramPacket類構造器的詳細解析:

  1. 根據構造器引數的不同,它分為傳送端構造器,和接收端使用的構造器
    1.1傳送端用的構造器:
    new DatagramPacket(byte[] buf, int length, InetAddress address, int port) 用來建立傳送端物件
    buf:要傳送的內容,位元組陣列
    length:要傳送內容的長度,單位是位元組
    address:接收端的IP地址物件
    port:接收端的埠號

    1.2接受端用的構造器:
    new DatagramPacket(byte[] buf, int length) 建立接收端的資料包物件
    buf:用來儲存接收到內容
    length:能夠接收內容的長度

  2. 實現思路:

      1. 建立一個DatagramPacket(資料包物件)物件用來封裝要傳送的資料,他只是儲存封裝資料,傳送資料要用DatagramSocket類
      2. 建立一個服務端接收的類,接收客服端傳送的資料
    
    package com.net;
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    public class UDPClientDemo01 {
        public static void main(String[] args) throws IOException {
            System.out.println("客服端啟動");
            byte[] bytes = "你好,吃了沒".getBytes();//要傳送的內容
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 9966);
            DatagramSocket socket = new DatagramSocket();
            socket.send(packet); //傳送packet物件
            socket.close();//關閉資源
        }
    }
    
    
    package com.net;
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.SocketException;
    public class UDPServerDemo02 {
        public static void main(String[] args) {
            System.out.println("伺服器端啟動");
            byte[] bytes = new byte[1024*64]; //建立一個接收客戶都端的資料包物件
            DatagramPacket packet = new DatagramPacket(bytes,bytes.length);
            //建立一個接收端的物件
            DatagramSocket socket;
                try {
                    socket = new DatagramSocket(9966);//這個埠號要和傳送端的一致
                    //開始接收
                    socket.receive(packet);
                    int len = packet.getLength();//獲取接收的長度
                    String str = new String(bytes,0,len);
                    System.out.println(str);
                    System.out.println("伺服器端已收到");
    
                    //服務端還可以獲取發來資訊的客戶端的IP和埠
                    String ip = packet.getAddress().getHostAddress();
                    int port = packet.getPort();
                    System.out.println("傳送端的ip地址"+ip+"\n"+"對方的埠號是:"+port);
                    socket.close();
                } catch (SocketException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }
    
    

執行結果:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-dGDcONxR-1604207341187)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午9.24.28.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-AJxCkhMC-1604207341187)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午9.24.38.png)]

1.6 TCP通訊協議詳解:
  1. TCP/IP協議是安全可靠的傳輸協議,只有連線伺服器成功之後,才會傳送資料,

多用於檔案上傳和下載, 郵件傳送和接收,遠端登入,因為我們要確保不丟失資料

  1. TCP/IP協議的特點:
  • 面向連線的協議
  • 只能由客戶端主動傳送資料給伺服器端,伺服器端接收到資料之後,可以給客戶端響應資料
  • 通過三次握手建立連線,連線成功形成資料傳輸通道
  • 通過四次揮手斷開連線
  • 基於IO流進行資料傳輸
  • 傳輸資料大小沒有限制
  • 因為面向連線的協議,速度慢,但是是可靠的協議
  1. Java中TCP相關的類:Socket and ServerSoket

Socket 類的構造方法:

  • Socket(String host, int port) 傳入IP地址和埠號

  • 注意事項:只要執行該方法,就會立即連線指定的伺服器程式,如果連線不成功,則會丟擲異常,如果連線成功,則表示三次握手通過。

    Socket類常用方法:

  • OutputStream getOutputStream(); 獲得位元組輸出流物件

  • InputStream getInputStream(); 獲得位元組輸入流物件

ServerSoket代表伺服器物件:

  • public ServerSocket(int port) 獲取客服端的埠號

  • public Socket accept() 接收客服端的連線請求 ,成功連線返回Socket物件

    程式碼實現:

package com.net;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;

public class ClientDemo01 {
    public static void main(String[] args) throws IOException {
        //1.建立客服端的物件
        Socket socket = new Socket("LocalHost", 9966);
        //2.獲取從socket管道中獲得位元組輸出流
        OutputStream outputStream = socket.getOutputStream();
        //3.把低階流轉換成高階輸出流
        PrintStream ps = new PrintStream(outputStream);
        //傳送訊息
        ps.println("你好伺服器,我是客服端");
        ps.flush();//重新整理
        System.out.println("客服端傳送資料完畢~");
    }
}

package com.net;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo01 {
    public static void main(String[] args) throws IOException {
        System.out.println("服務端開始啟用");
        //1.建立伺服器端的物件
        ServerSocket serverSocket = new ServerSocket(9966);
        //2.開始等待接收客服端的socket連線
        Socket acceptSocket = serverSocket.accept();
        //3.連線成功,獲取socket的位元組輸入流
        InputStream inputStream = acceptSocket.getInputStream();
        //4.把位元組輸入流轉為字元輸入流
        Reader reader = new  InputStreamReader(inputStream);
        //5..把字元輸入流包裝成緩衝字元輸入流
        BufferedReader br = new BufferedReader(reader);
        //6.按照一行一行的讀  以為傳送的是一行一行傳送的
        String line;
        if((line = br.readLine())!=null){
            System.out.println(line);
        }
    }
}

執行結果:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-MVKYRC9v-1604207341188)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午9.26.33.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-GbX3XJRf-1604207341188)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午9.26.55.png)]

改進客服端手動發資料

package com.net;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientDemo01 {
    public static void main(String[] args) throws IOException {
        //1.建立客服端的物件
        Socket socket = new Socket("LocalHost", 9966);
        //2.獲取從socket管道中獲得位元組輸出流
        OutputStream outputStream = socket.getOutputStream();
        //3.把低階流轉換成高階輸出流
        PrintStream ps = new PrintStream(outputStream);
        //一直髮傳送訊息
        while (true){
            Scanner scanner = new Scanner(System.in);
            System.out.print("客服端:");
            ps.println(scanner.nextLine());//一行一行的寫
            ps.flush();
        }
    }
}

package com.net;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo01 {
    public static void main(String[] args) throws IOException {
        System.out.println("服務端開始啟用");
        //1.建立伺服器端的物件
        ServerSocket serverSocket = new ServerSocket(9966);
        //2.開始等待接收客服端的socket連線
        Socket acceptSocket = serverSocket.accept();
        //3.連線成功,獲取socket的位元組輸入流
        InputStream inputStream = acceptSocket.getInputStream();
        //4.把位元組輸入流轉為字元輸入流
        Reader reader = new  InputStreamReader(inputStream);
        //5..把字元輸入流包裝成緩衝字元輸入流
        BufferedReader br = new BufferedReader(reader);
        //6.一直讀
        String line;
        while ((line = br.readLine())!= null){
            System.out.println(line);
        }
    }
}

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-phCS9TgZ-1604207341189)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午9.29.27.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-rgImg1XE-1604207341189)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午9.29.37.png)]

多執行緒 實現多個客服端傳送資料
package com.net;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class ClientDemo01 {
    public static void main(String[] args) throws IOException {
        //1.建立客服端的物件
        Socket socket = new Socket("LocalHost", 9966);
        //2.獲取從socket管道中獲得位元組輸出流
        OutputStream outputStream = socket.getOutputStream();
        //3.把低階流轉換成高階輸出流
        PrintStream ps = new PrintStream(outputStream);
        //一直髮傳送訊息
        while (true){
            Scanner scanner = new Scanner(System.in);
            System.out.print("客服端:");
            ps.println(scanner.nextLine());//一行一行的寫
            ps.flush();
        }
    }
}

package com.net;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo02 {
    public static void main(String[] args) throws IOException {
        System.out.println("伺服器端啟用");
        //1.獲取伺服器端物件
        ServerSocket serverSocket = new ServerSocket(9966);
        //2.迴圈來接收不同執行緒的客服端物件
        while(true){
            Socket acceptSocket = serverSocket.accept();
            Thread thread = new ServerReaderThread(acceptSocket);
            thread.start();
        }
    }
}
class ServerReaderThread extends Thread{
    Socket socket;
    public ServerReaderThread(Socket acceptSocket) {
        this.socket = acceptSocket;
    }
    @Override
    public void run() {
        try {
            //3.獲取socket管道的位元組輸入流
            InputStream inputStream = socket.getInputStream();
            //4.包裝成緩衝流
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            //5.讀取資料
            String line ;
            while ((line = br.readLine())!=null){
                System.out.print(socket.getRemoteSocketAddress());//獲取當前執行緒的IP地址
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println(socket.getRemoteSocketAddress()+"下線線了~");
        }
    }
}

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-LDYCRz8F-1604207341190)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午9.31.26.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-bOxIKOCR-1604207341190)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午9.31.35.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-jKzYwYH9-1604207341191)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午9.31.45.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-e0Kihn8J-1604207341192)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午9.31.56.png)]

Python TCP

TCP伺服器的建立可以歸納這幾步:

  • 建立 socket(套接字)
  • 繫結 socket 的 IP 地址和埠號
  • 監聽客戶端的連線請求
  • 接受客戶端的連線請求
  • 與客戶端對話
  • 關閉連線

TCP客戶端的建立可總結為這幾步:

  • 建立 socket(套接字)
  • 連線伺服器 socket
  • 與伺服器對話
  • 關閉連線

這裡需要注意的是 TCP 客戶端連線到伺服器的 IP 和埠號必須是 TCP 伺服器的 IP 和監聽的埠號,伺服器呼叫 listen() 開始監聽埠,然後呼叫 accept() 時刻準備接受客戶端的連線請求,此時伺服器處於阻塞狀態,直到伺服器監聽到客戶端的請求後,接收請求並建立連線為止。

TCP 客戶端

建立 socket 連線,可以這樣做:

# 匯入socket庫
import socket
# 建立一個socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立連線
s.connect(("127.0.0.1", 6000))

建立 socket 時,第一個引數 socket.AF_INET 表示指定使用 IPv4 協議,如果要使用 IPv6 協議,就指定為 socket.AF_INET6。SOCK_STREAM 指定使用面向流的 TCP 協議。然後我們呼叫 connect() 方法,傳入 IP 地址(或者域名),指定埠號就可以建立連線了。

接下來我們就可以向伺服器傳送資料了:

s.send(b'Hello, Mr Right!')

接收資料時,呼叫 recv(max) 方法,一次最多接收指定的位元組數,因此,在一個 while 迴圈中反覆接收,直到 recv() 返回空資料,表示接收完畢,退出迴圈。

# 接收資料
buffer = []
while True:
    # 每次最多接收1k位元組
    d = s.recv(1024)
    if d:
        buffer.append(d)
    else:
        break
data = b''.join(buffer)

最後,我們需要關閉連線,很簡單:

s.close()

TCP 伺服器

相比於客戶端,伺服器端稍微複雜一些,需要先繫結一個 IP 地址和埠號,然後監聽客戶端的請求,收到請求後丟到一個執行緒去處理。

建立 socket 跟客戶端方法一樣:

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

接下來需要繫結監聽地址和埠:

s.bind(('127.0.0.1', 6000))

然後就可以開始監聽埠了,監聽時需要傳入一個引數,指定等待連線的最大數量:

s.listen(5)

接下來就是無限迴圈等待客戶端的連線,直到有連線請求過來,就用一個執行緒去處理:

while True:
    # 接受一個新連線
    sock, addr = s.accept()
    # 建立新執行緒來處理TCP連線
    t = threading.Thread(target=tcplink, args=(sock, addr))
    t.start()

這裡為什麼需要多執行緒處理呢?想象一下菜鳥驛站,如果裡面只有一個人的話,那麼多個人寄件就需要排隊,一個個來;但是如果有多個人的話,那麼每個人都可以處理一個寄件請求。

我們來看一下處理客戶端請求的方法:

# 處理tcp連線
def tcplink(conn, addr):
    print("Accept new connection from %s:%s" % addr)
    # 向客戶端傳送歡迎訊息
    conn.send(b"Server: Welcome!\n")
    while True:
        conn.send(b"Server: What's your name?")

        data = conn.recv(1024)
        # 如果客戶端傳送 exit 過來請求退出,結束迴圈
        if data == b"exit":
            conn.send(b"Server: Good bye!\n")
            break
        conn.send(b"Server: Hello %s!\n" % data)

    # 關閉連線
    conn.close()
    print("Connection from %s:%s is closed" % addr)

例子中,我們先想客戶端傳送歡迎訊息,然後詢問客戶端名稱,收到名稱後傳送歡迎訊息,直到接收到客戶端的 ‘exit’ 命令,退出迴圈,關閉連線。

例項

伺服器端程式碼:


import socket
import threading
import time

# 處理tcp連線
def tcplink(conn, addr):
    print("Accept new connection from %s:%s" % addr)
    # 向客戶端傳送歡迎訊息
    conn.send(b"Server: Welcome!\n")
    while True:
        conn.send(b"Server: What's your name?")

        data = conn.recv(1024)
        # 如果客戶端傳送 exit 過來請求退出,結束迴圈
        if data == b"exit":
            conn.send(b"Server: Good bye!\n")
            break
        conn.send(b"Server: Hello %s!\n" % data)

    time.sleep(5)
    # 關閉連線
    conn.close()
    print("Connection from %s:%s is closed" % addr)


# 建立 socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 監聽埠
s.bind(("127.0.0.1", 6000))
# 設定等待連線的最大數量為5
s.listen(5)
print("Waiting for connection...")
# 等待接收連線
while True:
    # 接受一個新連線
    conn, addr = s.accept()
    # 建立新執行緒來處理TCP連線
    t = threading.Thread(target=tcplink, args=(conn, addr))
    t.start()

客戶端程式碼:


import socket
import time

# 建立 socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 建立連線
s.connect(("127.0.0.1", 6000))

# 接收伺服器訊息
print(s.recv(1024).decode())

for data in [b'Michael', b'Tracy', b'Sarah']:
    # 傳送資料
    s.send(data)
    time.sleep(2)
    # 列印接收到的資料
    print(s.recv(1024).decode('utf-8'))
    time.sleep(1)

time.sleep(3)
# 請求退出
s.send(b'exit')
time.sleep(2)
print(s.recv(1024).decode('utf-8'))

# 關閉連線
s.close()

注意,在程式碼中,我加入了一些休眠(sleep)操作,主要是為了控制檯能夠順利列印出來,不然程式執行太快,列印順序和內容有可能和預期不一樣。

先執行伺服器端程式碼,然後再執行客戶端程式碼,我們可以看到伺服器端控制檯列印內容如下:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-LYoNJo2w-1604207341192)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午9.43.37.png)]

Python UDP

UDP伺服器的建立可以歸納這幾步:

  • 建立 socket(套接字)
  • 繫結 socket 的 IP 地址和埠號
  • 接收客戶端資料
  • 關閉連線

TCP客戶端的建立可總結為這幾步:

  • 建立 socket(套接字)
  • 向伺服器傳送資料
  • 關閉連線

這裡需要注意的是 UDP 客戶端連線到伺服器的 IP 和埠號必須是 UDP 伺服器的 IP 和監聽的埠號,伺服器伺服器只需要繫結 IP 和埠號,就可以時刻準備接收客戶端傳送的資料,此時伺服器處於阻塞狀態,直到接收到資料為止。

UDP 客戶端

建立 socket,可以這樣做:

# 匯入socket庫
import socket

# 建立一個socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

建立 socket 時,第一個引數 socket.AF_INET 表示指定使用 IPv4 協議,如果要使用 IPv6 協議,就指定為 socket.AF_INET6。SOCK_DGRAM 指定基於 UDP 的資料包式 Socket 通訊。

建立了 socket 之後,我們就可以向目標地址傳送資料包了:

# 傳送資料
s.sendto(b'Hello Server', ('127.0.0.1', 6000))

第一個引數是需要傳送的資料包內容,第二個引數是 IP 地址和埠號的二元組。

如果是接收資料的話,我們可以這樣寫:

# 接收資料
data, addr = s.recv(1024)
# 解碼接收到的資料
data = data.decode('utf-8')

接收資訊的時候,第一個 data 表示接收到的資料, addr 是對方的 IP 地址和埠號的二元組。

想要關閉 socket,直接呼叫 close() 方法即可:

# 關閉 socket
socket.close()

UDP 伺服器

相比於客戶端,伺服器端只是多了一個步驟,在建立 socket 之後,需要繫結一個 IP 地址和埠號,以便接收客戶端隨時可能傳送過來的資料。繫結的方法為:

# 繫結 IP 和埠
s.bind(('127.0.0.1', 6000))

UDP 簡單例項

我們通過一個簡單的例項來體會下 UDP 的客戶端和伺服器的通訊流程。

伺服器程式碼為:

import socket

# 建立 socket
sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 繫結 IP 和埠號
sk.bind(('127.0.0.1', 6000))
while True:
    # 接收資料包
    msg, addr = sk.recvfrom(1024)
    # 列印
    print('來自[%s:%s]的訊息: %s' % (addr[0], addr[1], msg.decode('utf-8')))

    # 等待輸入
    inp = input('>>>')
    # 傳送資料包
    sk.sendto(inp.encode('utf-8'), addr)

# 關閉 socket
sk.close()

這裡,我們先建立 socket,然後繫結本機的6000埠,然後等待接收客戶端傳送的資料包,接收到資料後將資料內容列印在控制檯。然後可以在控制檯輸入回覆內容,傳送給客戶端。

客戶端程式碼:

import socket

# 建立 socket
sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
addr = ('127.0.0.1', 6000)
while True:
    # 等待輸入
    msg = input('>>>')
    # 傳送資料包
    sk.sendto(msg.encode('utf-8'), addr)
    # 接收資料包
    msg_recv, addr = sk.recvfrom(1024)
    # 列印
    print(msg_recv.decode('utf-8'))

# 關閉 socket
sk.close()

在客戶端程式碼中,我們就只是建立 socket,然後在控制檯輸入需要向伺服器傳送的內容,通過 sentto() 方法傳送給伺服器,然後接收伺服器返回的內容,將接收的內容列印到控制檯。

分別執行客戶端和伺服器程式碼,然後我們在客戶端的控制檯輸入 “hello server”,我們可以看到伺服器的控制檯列印了客戶端傳送的內容,然後我們在伺服器控制檯輸入 “hello client”,同樣在客戶端控制檯可以看你到內容。

下面是客戶端的控制檯內容:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-0INi5QT4-1604207341193)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午10.15.38.png)]

下面是伺服器的控制檯內容:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-yQYL4VGm-1604207341194)(/Users/harvey/Library/Application Support/typora-user-images/截圖2020-08-26 上午10.13.56.png)]

這個例項其實就是一個簡單的聊天模型,客戶端和伺服器就像兩個人一樣可以傳送和接收對方的資訊。

那麼多人群聊怎麼實現呢?簡單來說,我們需要設定一臺中心伺服器,我們每個人傳送的內容都先傳送到中心伺服器,然後中心伺服器再轉發到每個群聊的人。

總結

本文為大家介紹了 Java和python的TCP和UDP資料傳輸網路程式設計。

相關文章