1. 程式人生 > >Java入門系列-24-實現網路通訊

Java入門系列-24-實現網路通訊

網際網路上那麼多裝置,java 是如何與其他裝置通訊的呢?這次的內容是網路通訊的基礎,有了它咱們才能上網頁、玩遊戲、視訊聊天。

Socket 客戶端套接字

Socket 客戶端套接字,用於連線網際網路提供服務的裝置。

Socket 構造方法

構造方法 說明
Socket() 通過系統預設型別的 SocketImpl 建立未連線套接字
Socket(String host, int port) 建立一個流套接字並將其連線到指定主機上的指定埠號

常用方法

方法名稱 說明
getOutputStream() 返回此套接字的輸出流
getInputStream() 返回此套接字的輸入流

下面示例模擬了一個 HTTP 請求

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class TestSocket {

    public static void main(String[] args) {
        
        //建立套接字
        try(Socket s=new Socket("www.baidu.com", 80);){
            //建立向伺服器傳送資料的輸出流
            OutputStream os=s.getOutputStream();
            StringBuffer sb=new StringBuffer();
            //HTTP協議 請求報文
            sb.append("GET / HTTP/1.1\r\n");
            sb.append("Host: www.baidu.com:80\r\n");
            sb.append("Connection: Keep-Alive\r\n");
            //這裡一定要一個回車換行,表示訊息頭完,不然伺服器會等待
            sb.append("\r\n");
            //傳送
            os.write(sb.toString().getBytes());
            //獲取伺服器相應內容 
            InputStream is=s.getInputStream();
            //通過輸入流建立掃描器,並指定編碼為utf-8防止中文亂碼
            Scanner scanner=new Scanner(is,"utf-8");
            
            while(scanner.hasNextLine()) {
                String line=scanner.nextLine();
                System.out.println(line);
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
            System.out.println("未知主機");
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO異常");
        }
    }
}

ServerSocket

ServerSocket:實現伺服器套接字,伺服器套接字等待請求通過網路傳入。它基於該請求執行某些操作,然後可能向請求者返回結果。

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TestServerSocket {

    public static void main(String[] args) {
        //建立 ServerSocket 監聽1666埠
        try(ServerSocket server=new ServerSocket(1666)){
            //阻塞方法,當有客戶端連入,獲取客戶端Socket
            try(Socket client=server.accept()){
                //獲取客戶端傳送的資料
                InputStream is=client.getInputStream();
                //獲取向客戶端傳送資料的流
                OutputStream os=client.getOutputStream();
                //通過輸入流建立掃描器
                try(Scanner scanner=new Scanner(is)){
                    
                    PrintWriter pw=new PrintWriter(os,true/*自動重新整理*/);
                    //向客戶端傳送訊息
                    pw.println("Hello,enter bye to exit.");
                    boolean done=false;
                    //客戶端有輸入資料並且沒有傳送 bye 
                    while(!done&&scanner.hasNextLine()) {
                        //接收客戶端傳送的資料
                        String line=scanner.nextLine();
                        //將客戶端傳送的資料發回客戶端
                        pw.println("Echo:"+line);
                        //如果客戶端輸入bye 結束通訊
                        if(line.trim().equalsIgnoreCase("bye")) {done=true;}
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

測試方式: 在DOS 中輸入命令:telnet 127.0.0.1 1666

telnet 不是內部或外部命令的讀者,需要在 Windows 功能中啟用 Telnet 客戶端。

上面的程式碼如果有多個客戶端連入就不行了,如果希望服務能被多個客戶端連線,可以使用執行緒。

多執行緒伺服器

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TestMultiServerSocket {

    public static void main(String[] args) {
        //建立 ServerSocket 監聽 1666埠
        try(ServerSocket server=new ServerSocket(1666)){
            while(true) {
                //accept() 是一個阻塞方法
                Socket client=server.accept();
                InputStream is=client.getInputStream();
                OutputStream os=client.getOutputStream();
                //開啟新的執行緒處理,傳入當前客戶端
                Thread t=new Thread(new ThreadEchoHandler(client));
                t.start();              
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
class ThreadEchoHandler implements Runnable{
    private Socket socket=null;

    public ThreadEchoHandler(Socket socket) {
        this.socket=socket;
    }
    @Override
    public void run() {
        try {
            InputStream is=socket.getInputStream();
            OutputStream os=socket.getOutputStream();
            try(Scanner scanner=new Scanner(is)){
                PrintWriter pw=new PrintWriter(os,true);
                pw.println("Hello,enter bye to exit.");
                boolean done=false;
                while(!done&&scanner.hasNextLine()) {
                    String line=scanner.nextLine();
                    pw.println("Echo:"+line);
                    if(line.trim().equalsIgnoreCase("bye")) {done=true;}
                }
            }
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

URLConnection

抽象類 URLConnection 是所有類的超類,它代表應用程式和 URL 之間的通訊連結。此類的例項可用於讀取和寫入此 URL 引用的資源。

Socket 可以預設任意型別的網路通訊,URLConnection 更適合 HTTP 請求,使用 URLConnection 進行HTTP操作更方便,模擬請求報文,獲取響應報文和內容。

URLConnection 常用方法

方法 說明
connect() 開啟到此 URL 引用的資源的通訊連結(如果尚未建立這樣的連線)
getContentEncoding() 返回 content-encoding 頭欄位的值
getContentType() 返回 content-type 頭欄位的值
getHeaderFields() 返回頭欄位的不可修改的 Map
getInputStream() 返回從此開啟的連線讀取的輸入流
setRequestProperty(String key, String value) 設定一般請求屬性

獲取 URLConnection 需要先建立 URL 物件: URL url=new URL(host);

使用 URLConnection 獲取網頁的內容

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;

public class TestURLConnection {
    public static void main(String[] args) {
        
        try {
            //建立URL物件
            URL url=new URL("http://www.baidu.com");
            //建立 URLConnection物件
            URLConnection connection=url.openConnection();
            //設定請求屬性
            //connection.setRequestProperty("", "");
            //連線
            connection.connect();
            //獲取輸入流
            InputStream is=connection.getInputStream();
            //通過輸入流構建一個掃描器
            Scanner scanner=new Scanner(is,"utf-8");
            while(scanner.hasNextLine()) {
                String line=scanner.nextLine();
                System.out.println(line);
            }
            System.out.println("===響應頭===");
            Map<String,List<String>> headers=connection.getHeaderFields();
            for (Entry<String, List<String>> entry: headers.entrySet()) {
                String key=entry.getKey();
                System.out.print(key+":");
                for (String string : entry.getValue()) {
                    System.out.print(string);
                }
                System.out.println();
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}