Java學習總結:52(Java網絡編程)

Java網絡編程

開發第一個網絡程序

java.net包提供了網絡編程有關的開發工具類,在此包中有一下兩個主要的核心操作類。

  • ServerSocket類:是一個封裝支持的TCP協議的操作類,主要工作在服務器端,用於接收客戶端請求;
  • Socket類:也是一個封裝了TCP協議的操作類,每一個Socket對象都表示一個客戶端。

ServerSocket類的常用方法

No. 方法名稱 類型 描述
1 public ServerSocket(int port) throws IOException 構造 開闢一個指定的端口監聽,一般使用5000以上的端口
2 public Socket accept() throws IOException 普通 服務器端接收客戶端請求,通過Socket返回
3 public void close() throws IOException 普通 關閉服務器端

Socket類的常用方法

No. 方法名稱 類型 描述
1 public Socket(String host, int port) throws UnknownHostException,IOException 構造 指定要連接的主機(IP地址)和接口,host代表主機的IP地址,如果是本機直接訪問,則使用localhost(127.0.0.1)代替IP
2 public OutputStream getOutputStream() throws IOException 普通 取得指定客戶端的輸出對象,使用PrintStream操作
3 public InputStream getInputStream() throws IOException 普通 從指定的客戶端讀取數據,使用Scanner操作

在客戶端,程序可以通過Socket類的getInputStream()方法,取得服務器的輸出信息,在服務器端可以通過getOutputStream()方法取得客戶端的輸出信息。

例:定義服務器端

package Project.Study.ServerSocket;

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

public class Test1 {
    public static void main(String[]args)throws Exception{
        ServerSocket server = new ServerSocket(9999);   //所有的服務器必須有接口
        System.out.println("等待客戶端連接...");
        Socket socket = server.accept();                      //等待客戶端連接
        //OutputStream並不方便進行內容的輸出,所以利用打印流完成輸出
        PrintStream out = new PrintStream(socket.getOutputStream());
        out.println("Hello World!!!");                          //輸出數據
        out.close();
        socket.close();
        server.close();
    }
}
//結果:
//等待客戶端連接...(此處講出現堵塞情況,一直到客戶端連接後纔會繼續執行)

本程序在本機的9999端口上設置了一個服務器的監聽操作(accept()方法表示打開服務器監聽),這樣當有客戶端通過TCP連接方式連接到服務器端後,服務器端將利用PrintStream輸出數據,當數據輸出完畢後該服務器就將關閉,所以本次定義的服務器只能處理一次客戶端的請求。

例:編寫客戶端

package Project.Study.Socket;

import java.net.Socket;
import java.util.Scanner;

public class Test1 {
    public static void main(String[]args)throws Exception{
        Socket socket = new Socket("localhost",9999);//連接服務器
        //取得客戶端的輸入數據流對象,表示接收服務器端的輸出信息
        Scanner scanner = new Scanner(socket.getInputStream());
        scanner.useDelimiter("\n");     //設置分隔符
        if (scanner.hasNext()){         //判斷是否有數據
            System.out.println("迴應數據:"+scanner.next());//取出數據
        }
        scanner.close();
        socket.close();
    }
}
//結果:
//迴應數據:Hello World!!!

在TCP程序中,每一個Socket對象都表示一個客戶端的信息,所以客戶端程序要連接也必須依靠Socket對象操作。在實例化Socket類對象時必須設置要連接的主機名稱(本機爲localhost,或者填寫IP地址)以及連接端口號,當連接成功後就可以利用Scanner進行輸入流數據的讀取,接收服務器端的迴應信息。

網絡開發的經典模型——Echo程序

例:實現服務器端

package Project.Study.ServerSocket;

import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class Test2 {
    public static void main(String[]args)throws Exception{
        ServerSocket serverSocket = new ServerSocket(9999);     //定義連接端口
        Socket socket = serverSocket.accept();                          //等待客戶連接
        //得到客戶端輸入的數據以及向客戶端輸出數據的對象,利用掃描流接收,打印流輸出
        Scanner scanner = new Scanner(socket.getInputStream());
        PrintStream out = new PrintStream(socket.getOutputStream());
        boolean flag = true;                                            //設置循環標記
        while (flag){
            if (scanner.hasNext()){                                     //是否有內容輸入
                String str = scanner.next().trim();                     //得到客戶端發送的內容,並刪除空格
                if (str.equalsIgnoreCase("####")){          //程序結束標記
                    out.println("再見!");                                 //輸出結束信息
                    flag = false;                                           //退出循環
                }else{                                                  //迴應輸入信息
                    out.println("ECHO:"+str);                           //加上前綴返回
                }
            }
        }
        scanner.close();
        out.close();
        socket.close();
        serverSocket.close();
    }
}

該服務器端存在一個缺陷,它是單線程的網絡應用,所有的操作都是在主線程上進行的開發,所以只能連接一個客戶端,不能連接其他客戶端。

例:修改服務器端(使其可以同時處理多個客戶端的連接操作)

package Project.Study.ServerSocket;

import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

class EchoThread implements Runnable{   //建立線程類
    private Socket socket;              //每個線程處理一個客戶端
    public EchoThread(Socket socket){   //創建線程對象時傳遞Socket
        this.socket=socket;
    }
    @Override
    public void run(){
        try {                           //每個線程對象取得各自Socket的輸入流與輸出流
            Scanner scanner = new Scanner(socket.getInputStream());
            PrintStream out = new PrintStream(socket.getOutputStream());
            boolean flag = true;                            //控制多次接收操作
            while (flag){
                if (scanner.hasNext()){                     //判斷是否有內容
                    String str = scanner.next().trim();     //得到客戶端發送的內容
                    if (str.equalsIgnoreCase("###")){//程序結束
                        out.println("再見!");
                        flag = false;                       //退出循環
                    }else{                                  //應該回應輸入信息
                        out.println("ECHO:"+str);           //迴應信息
                    }
                }
            }
            scanner.close();
            out.close();
            socket.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
public class Test3 {
    public static void main(String[]args)throws Exception{
        ServerSocket serverSocket = new ServerSocket(9999);//在9999端口上監聽
        boolean flag = true;                                    //循環標記
        while (flag){                                           //接收多個客戶端請求
            Socket socket = serverSocket.accept();              //客戶端連接
            new Thread(new EchoThread(socket)).start();         //創建並啓動新線程
        }
        serverSocket.close();
    }
}

例:定義客戶端

package Project.Study.Socket;

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

public class Test2 {
    public static void main(String[]args)throws Exception{
        Socket socket = new Socket("localhost",9999);   //服務器地址和端口
        Scanner input = new Scanner(System.in);                     //鍵盤輸入數據
        //利用Scanner包裝客戶端輸入數據(服務器端輸出),PrintStream包裝客戶端輸出數據
        Scanner scanner = new Scanner(socket.getInputStream());
        PrintStream out = new PrintStream(socket.getOutputStream());
        input.useDelimiter("\n");                                   //設置鍵盤輸入分隔符
        scanner.useDelimiter("\n");                                 //設置迴應數據分隔符
        boolean flag = true;                                        //循環標誌
        while (flag){
            System.out.println("請輸入要發送的數據:");
            if (input.hasNext()){                                   //判斷鍵盤是否輸入數據
                String str = input.next().trim();                   //取得鍵盤輸入的數據
                out.println(str);                                   //發送數據到服務器端
                if (str.equalsIgnoreCase("###")){       //結束標記
                    flag = false;                                       //結束循環
                }
                if (scanner.hasNext()){                             //服務器有迴應
                    System.out.println(scanner.next());             //輸出迴應數據
                }
            }
        }
        input.close();
        scanner.close();
        out.close();
        socket.close();
    }
}

結果博主就不發了,自己在兩個端口自問自答感覺有點怪。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章