<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    神奇好望角 The Magical Cape of Good Hope

    庸人不必自擾,智者何需千慮?
    posts - 26, comments - 50, trackbacks - 0, articles - 11
      BlogJava :: 首頁 ::  :: 聯(lián)系 :: 聚合  :: 管理

    JDK 提供了對 TCP(Transmission Control Protocol,傳輸控制協(xié)議)和 UDP(User Datagram Protocol,用戶數(shù)據(jù)報協(xié)議)這兩個數(shù)據(jù)傳輸協(xié)議的支持。本文開始探討 TCP。

    TCP 基礎知識

    在“服務器-客戶端”這種架構中,服務器和客戶端各自維護一個端點,兩個端點需要通過網(wǎng)絡進行數(shù)據(jù)交換。TCP 為這種需求提供了一種可靠的流式連接,流式的意思是傳出和收到的數(shù)據(jù)都是連續(xù)的字節(jié),沒有對數(shù)據(jù)量進行大小限制。一個端點由 IP 地址和端口構成(專業(yè)術語為“元組 {IP 地址, 端口}”)。這樣,一個連接就可以由元組 {本地地址, 本地端口, 遠程地址, 遠程端口} 來表示。

    連接過程

    在 TCP 編程接口中,端點體現(xiàn)為 TCP 套接字。共有兩種 TCP 套接字:主動和被動,“被動”狀態(tài)也常被稱為“偵聽”狀態(tài)。服務器和客戶端利用套接字進行連接的過程如下:

    1. 服務器創(chuàng)建一個被動套接字,開始循環(huán)偵聽客戶端的連接。
    2. 客戶端創(chuàng)建一個主動套接字,連接服務器。
    3. 服務器接受客戶端的連接,并創(chuàng)建一個代表該連接的主動套接字。
    4. 服務器和客戶端通過步驟 2 和 3 中創(chuàng)建的兩個主動套接字進行數(shù)據(jù)傳輸。

    下面是連接過程的圖解:

    TCP 連接
    TCP 連接

    一個簡單的 TCP 服務器

    JDK 提供了 ServerSocket 類來代表 TCP 服務器的被動套接字。下面的代碼演示了一個簡單的 TCP 服務器(多線程阻塞模式),它不斷偵聽并接受客戶端的連接,然后將客戶端發(fā)送過來的文本按行讀取,全文轉(zhuǎn)換為大寫后返回給客戶端,直到客戶端發(fā)送文本行 bye

    public class TcpServer implements Runnable {
        private ServerSocket serverSocket;
    
        public TcpServer(int port) throws IOException {
            // 創(chuàng)建綁定到某個端口的 TCP 服務器被動套接字。
            serverSocket = new ServerSocket(port);
        }
    
        @Override
        public void run() {
            while (true) {
                try {
                    // 以阻塞的方式接受一個客戶端連接,返回代表該連接的主動套接字。
                    Socket socket = serverSocket.accept();
                    // 在新線程中處理客戶端連接。
                    new Thread(new ClientHandler(socket)).start();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
    
    public class ClientHandler implements Runnable {
        private Socket socket;
    
        public ClientHandler(Socket socket) {
            this.socket = Objects.requireNonNull(socket);
        }
    
        @Override
        public void run() {
            try (Socket s = socket) {  // 減少代碼量的花招……
                // 包裝套接字的輸入流以讀取客戶端發(fā)送的文本行。
                BufferedReader in = new BufferedReader(new InputStreamReader(
                        s.getInputStream(), StandardCharsets.UTF_8));
                // 包裝套接字的輸出流以向客戶端發(fā)送轉(zhuǎn)換結果。
                PrintWriter out = new PrintWriter(new OutputStreamWriter(
                        s.getOutputStream(), StandardCharsets.UTF_8), true);
    
                String line = null;
                while ((line = in.readLine()) != null) {
                    if (line.equals("bye")) {
                        break;
                    }
    
                    // 將轉(zhuǎn)換結果輸出給客戶端。
                    out.println(line.toUpperCase(Locale.ENGLISH));
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
        

    阻塞模式的編程方式簡單,但存在性能問題,因為服務器線程會卡死在接受客戶端的 accept() 方法上,不能有效利用資源。套接字支持非阻塞模式,現(xiàn)在暫時略過。

    一個簡單的 TCP 客戶端

    JDK 提供了 Socket 類來代表 TCP 客戶端的主動套接字。下面的代碼演示了上述服務器的客戶端:

    public class TcpClient implements Runnable {
        private Socket socket;
    
        public TcpClient(String host, int port) throws IOException {
            // 創(chuàng)建連接到服務器的套接字。
            socket = new Socket(host, port);
        }
    
        @Override
        public void run() {
            try (Socket s = socket) {  // 再次減少代碼量……
                // 包裝套接字的輸出流以向服務器發(fā)送文本行。
                PrintWriter out = new PrintWriter(new OutputStreamWriter(
                        s.getOutputStream(), StandardCharsets.UTF_8), true);
                // 包裝套接字的輸入流以讀取服務器返回的文本行。
                BufferedReader in = new BufferedReader(new InputStreamReader(
                        s.getInputStream(), StandardCharsets.UTF_8));
    
                Console console = System.console();
                String line = null;
                while ((line = console.readLine()) != null) {
                    if (line.equals("bye")) {
                        break;
                    }
    
                    // 將文本行發(fā)送給服務器。
                    out.println(line);
                    // 打印服務器返回的文本行。
                    console.writer().println(in.readLine());
                }
    
                // 通知服務器關閉連接。
                out.println("bye");
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
        

    從 JDK 文檔可以看到,ServerSocketSocket 在初始化的時候,可以設定一些參數(shù),還支持延遲綁定。這些東西對性能和行為都有所影響。下一篇文章將詳解這兩個類的初始化。


    評論

    # re: Java 網(wǎng)絡編程從菜鳥到叫獸 2:TCP 和套接字入門  回復  更多評論   

    2012-01-05 17:13 by 何楊
    標題起得不錯。

    # re: Java 網(wǎng)絡編程從菜鳥到叫獸 2:TCP 和套接字入門[未登錄]  回復  更多評論   

    2012-01-05 21:06 by song
    還不夠菜 嘿嘿

    # re: Java 網(wǎng)絡編程從菜鳥到叫獸 2:TCP 和套接字入門  回復  更多評論   

    2012-01-10 14:35 by #
    上面對節(jié)省代碼地方,一直未搞明白!

    # re: Java 網(wǎng)絡編程從菜鳥到叫獸 2:TCP 和套接字入門  回復  更多評論   

    2012-01-10 15:38 by 蜀山兆孨龘
    @#
    建議你先看看 Java SE 7 的新功能“帶資源的 try”,就容易明白了。直接寫 try (socket) 是語法錯誤,因為 try 后面的括號里必須是賦值表達式,所以才引入臨時變量 s 暗度陳倉。

    # re: Java 網(wǎng)絡編程從菜鳥到叫獸 2:TCP 和套接字入門  回復  更多評論   

    2012-01-10 16:09 by 開啟
    學習
    主站蜘蛛池模板: 国产桃色在线成免费视频| 永久免费A∨片在线观看| 成人免费视频试看120秒| 亚洲精品日韩中文字幕久久久| 成人无码WWW免费视频| 亚洲精品夜夜夜妓女网| 成全视成人免费观看在线看| 浮力影院亚洲国产第一页| 久久久久女教师免费一区| 亚洲精品tv久久久久久久久| CAOPORN国产精品免费视频| 亚洲一区二区三区无码中文字幕 | 亚洲高清无码在线观看| 猫咪免费人成网站在线观看入口| 日本中文一区二区三区亚洲| 中文字幕精品亚洲无线码一区应用| 亚洲欧洲免费视频| 野花香高清视频在线观看免费| 又大又硬又粗又黄的视频免费看| 日韩在线免费看网站| 偷自拍亚洲视频在线观看99| 亚洲爽爽一区二区三区| 国产啪精品视频网站免费尤物 | 国产成人免费高清在线观看| 色屁屁在线观看视频免费| 亚洲熟女一区二区三区| 最近免费字幕中文大全视频| 亚洲日本VA中文字幕久久道具| 亚洲高清偷拍一区二区三区| 久久国产精品成人免费| 亚洲一区二区三区在线| 亚洲成年看片在线观看| 国产成人AV免费观看| 亚洲人成电影网站久久| 精品亚洲一区二区三区在线播放| 在线免费观看国产| 老司机午夜在线视频免费观| 日木av无码专区亚洲av毛片| 日韩免费视频观看| 久久一本岛在免费线观看2020| 激情亚洲一区国产精品|