Java IO之Socket通信

一、通訊協議TCP、UDP

Java Socket通信是基於TCP協議來完成的。講Socket通信之前有必要先了解這兩種底層協議。

  • TCP協議是面向連接、保證高可靠性(數據無丟失、數據無失序、數據無錯誤、數據無重複到達)傳輸層協議。TCP協議通過三次握手建立連接,四次握手斷開連接,帶重傳功能的肯定確認來保證可靠傳輸。其中HTTP協議就是基於TCP協議來實現的。HTTP1.0默認短連接,HTTP1.1默認使用長連接,通過Keep-Alive來控制長連接時間,在同一個TCP連接中,只有上一個請求得到響應之後下一個請求才會發送。

  • UDP協議是無連接,不保證可靠的傳輸層協議。傳輸數據之前源端和終端不建立連接,當它想傳送時就簡單地去抓取來自應用程序的數據,並儘可能快地把它扔到網絡上。其實“ping”命令的原理就是向對方主機發送UDP數據包,然後對方主機確認收到數據包,如果數據包是否到達的消息及時反饋回來,那麼網絡就是通的。

二、字符流和字節流

java在對文件、圖片、視頻等資源的操作上提供了字符流和字節流的操作方式,但是由於計算機中使用的是二進制來存儲文件,而字符流對文件操作時會經歷編碼和解碼階段,所以字節流的操作要比字符流的操作效率高。但是字符流有很多操作都方便編程人員,對於文件的讀取展現,我們可以使用字符流,免去了我們程序對字節和字符的轉換。除此之外,字符流與字節流還有一個根本的區別。字節流不需要用到緩衝區,而字符流會先將數據放到緩存區再寫入文件。如下例子,我們爲了研究字符流和字節流是否用到緩衝區,不關閉輸出流,因爲在關閉輸出流之前,會將緩衝區的內容全部輸出,類似於調用flush()。

字節流:

package ioTest;

import java.io.FileOutputStream;

public class ByteTest {

    public static void main(String[] arg) {
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("e:/ds2.txt");
            fileOutputStream.write("hello world".getBytes());
            //fileOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

結果:文件ds2.txt顯示【hello world】。

字符流:

package ioTest;

import java.io.FileWriter;
import java.io.IOException;

public class CharTest {

    public static void main(String[] arg){
        try {
            FileWriter fileWriter = new FileWriter("e:/ds.txt");
            fileWriter.write("hello world");
            //fileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

結果:文件ds.txt文件內容爲空。

二進制I/O中常用的流操作類
InputStream:FileInputStream、FilterInputStream、ObjectInputStream。
FilterInputStream:DateInputStream、BufferedInputStream。

  • FileInputStream以文件、文件名爲構造函數創建輸入流,是大部分輸入流的起始點,其他輸入流通過包裝該輸入流來進行操作。
  • DateInputStream通過包裝FileInputStream或其他字節輸入流來構造,提供了多種數據讀取方式,readByte()、readChar()、readInt()、readLine()、readUTF()等等。
  • BufferedInputStream在讀取文件時創建緩衝區,默認是512字節。
    ObjectInputStream包含所有DateInputStream的功能,另外還增加了對對象的操作。

文本I/O中常見的流操作
BufferedReader、InputStreamReader、FileReader

  • FileReader大部分字符流讀取文件的起點。
  • InputStreamReader連通字符流與字節流的樞紐,可以通過字節流來構造該字符流,然後通過其他字符流再包裝該字符流,並且設置編碼方式。如:
new BufferedReader(new InputStreamReader(new
   FileInputStream(fileName),"UTF-8")); 
  • BufferedReader在讀取文件時創建緩衝區。

三、通信實現

前面介紹了相關的基礎知識之後就直接上代碼吧,另外對於多線程不太熟悉的朋友自己去補習一下。本程序包含三個類,具體的運行步驟在代碼中做了詳細的解釋:

  • SocketClient:socket通信客戶端,運行時可以多次啓動來模擬多個客戶端。
  • SocketServer:socket通信服務端,這裏只有一個服務端,對於不同的客戶端請求創建不同的線程來進行處理,通過線程池來進行管理。
  • SocketThread:socket通信基礎線程,供服務端調用,用於與客戶端進行交互。
package socketTest;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

public class SocketClient {

    private DataOutputStream out;//通信輸出流
    private DataInputStream in;//通信輸入流
    private Socket socket;

    public static void main(String[] arg){
        try {
            SocketClient socketClient = new SocketClient();
            socketClient.initParam("127.0.0.1",6644);
            System.out.println("請輸入向服務器發送的內容:");
            socketClient.send();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //初始化參數
    public void initParam(String ipAddress, int port) throws Exception{
        this.socket = new Socket(ipAddress, port);
        this.out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
        this.in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
    }

    //發送內容
    public void send() throws IOException{
        while(true){
            Scanner scanner = new Scanner(System.in);
            String input = scanner.nextLine();
            out.writeUTF(input);
            out.flush();
            if("exit".equals(input)){
                //獲取服務器端斷開連接的信息
                String serverInput = in.readUTF();
                System.out.println(serverInput);
                //客戶端斷開連接
                System.out.println("客戶端關閉連接");
                in.close();
                out.close();
                socket.close();
                break;
            }
        }
    }
}
package socketTest;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SocketServer{

    private ServerSocket serverSocket;
    private ExecutorService threadPool;
    private static final int THREADCOUNT = 5;

    public static void main(String[] arg){
        SocketServer socketServer = new SocketServer();
        try {
            socketServer.initParam(6644);
            socketServer.getRequest();
        } catch (Exception e) {
            socketServer.threadPool.shutdown();
            e.printStackTrace();
        }
    }

    //初始化參數
    public void initParam(int port) throws Exception{
        this.serverSocket = new ServerSocket(port);
        this.threadPool = Executors.newFixedThreadPool(THREADCOUNT);
    }

    //獲取客戶端請求
    public void getRequest() throws IOException{
        while (true) {
            Socket socket = this.serverSocket.accept();
            SocketThread socketThread = new SocketThread(socket);
            threadPool.execute(socketThread);
        }
    }
}
package socketTest;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class SocketThread implements Runnable {

    private Socket socket;
    private DataInputStream in;//通信輸入流
    private DataOutputStream out;//通信輸出流

    public SocketThread(Socket socket){
        try {
            this.socket = socket;
            this.in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
            this.out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            String clientInput;
            while((clientInput = in.readUTF())!=null){
                System.out.println(clientInput);
                if("exit".equals(clientInput)){
                    out.writeUTF("服務器已關閉連接");
                    out.close();
                    in.close();
                    socket.close();
                    System.out.println("服務端關閉連接");
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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