一、通訊協議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();
}
}
}