Java的網絡編程主要涉及到的內容是Socket編程,那么什么是Socket呢?簡單地說,Socket,套接字,就是兩臺主機之間邏輯連接的端點。TPC/IP協議是傳輸層協議,主要解決數據如何在網絡中傳輸,而HTTP是應用層協議,主要解決如何包裝數據。Socket,本質上就是一組接口,是對TCP/IP協議的封裝和應用(程序員層面上)。
套接字使用TCP提供了兩臺計算機之間的通信機制。 客戶端程序創建一個套接字,并嘗試連接服務器的套接字。
當連接建立時,服務器會創建一個 Socket 對象。客戶端和服務器現在可以通過對 Socket 對象的寫入和讀取來進行通信。
java.net.Socket 類代表一個套接字,并且 java.net.ServerSocket 類為服務器程序提供了一種來監聽客戶端,并與他們建立連接的機制。
以下步驟在兩臺計算機之間使用套接字建立TCP連接時會出現:
服務器實例化一個 ServerSocket 對象,表示通過服務器上的端口通信。
服務器調用 ServerSocket 類的 accept() 方法,該方法將一直等待,直到客戶端連接到服務器上給定的端口。
服務器正在等待時,一個客戶端實例化一個 Socket 對象,指定服務器名稱和端口號來請求連接。
Socket 類的構造函數試圖將客戶端連接到指定的服務器和端口號。如果通信被建立,則在客戶端創建一個 Socket 對象能夠與服務器進行通信。
在服務器端,accept() 方法返回服務器上一個新的 socket 引用,該 socket 連接到客戶端的 socket。
連接建立后,通過使用 I/O 流在進行通信,每一個socket都有一個輸出流和一個輸入流,客戶端的輸出流連接到服務器端的輸入流,而客戶端的輸入流連接到服務器端的輸出流。
TCP 是一個雙向的通信協議,因此數據可以通過兩個數據流在同一時間發送.以下是一些類提供的一套完整的有用的方法來實現 socket。
整體流程
Socket編程主要涉及到客戶端和服務器端兩個方面,首先是在服務器端創建一個服務器套接字(ServerSocket),并把它附加到一個端口上,服務器從這個端口監聽連接。端口號的范圍是0到65536,但是0到1024是為特權服務保留的端口號,我們可以選擇任意一個當前沒有被其他進程使用的端口。
客戶端請求與服務器進行連接的時候,根據服務器的域名或者IP地址,加上端口號,打開一個套接字。當服務器接受連接后,服務器和客戶端之間的通信就像輸入輸出流一樣進行操作。

實例
下面是一個客戶端和服務器端進行數據交互的簡單例子,客戶端輸入正方形的邊長,服務器端接收到后計算面積并返回給客戶端,通過這個例子可以初步對Socket編程有個把握。
服務器端
public class SocketServer { public static void main(String[] args) throws IOException { // 端口號 int port = 7000; // 在端口上創建一個服務器套接字 ServerSocket serverSocket = new ServerSocket(port); // 監聽來自客戶端的連接 Socket socket = serverSocket.accept(); DataInputStream dis = new DataInputStream( new BufferedInputStream(socket.getInputStream())); DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(socket.getOutputStream())); do { double length = dis.readDouble(); System.out.println("服務器端收到的邊長數據為:" + length); double result = length * length; dos.writeDouble(result); dos.flush(); } while (dis.readInt() != 0); socket.close(); serverSocket.close(); }}
客戶端
public class SocketClient { public static void main(String[] args) throws UnknownHostException, IOException { int port = 7000; String host = "localhost"; // 創建一個套接字并將其連接到指定端口號 Socket socket = new Socket(host, port); DataInputStream dis = new DataInputStream( new BufferedInputStream(socket.getInputStream())); DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(socket.getOutputStream())); Scanner sc = new Scanner(System.in); boolean flag = false; while (!flag) { System.out.println("請輸入正方形的邊長:"); double length = sc.nextDouble(); dos.writeDouble(length); dos.flush(); double area = dis.readDouble(); System.out.println("服務器返回的計算面積為:" + area); while (true) { System.out.println("繼續計算?(Y/N)"); String str = sc.next(); if (str.equalsIgnoreCase("N")) { dos.writeInt(0); dos.flush(); flag = true; break; } else if (str.equalsIgnoreCase("Y")) { dos.writeInt(1); dos.flush(); break; } } } socket.close(); }}
實例二
可以看到上面的服務器端程序和客戶端程序是一對一的關系,為了能讓一個服務器端程序能同時為多個客戶提供服務,可以使用多線程機制,每個客戶端的請求都由一個獨立的線程進行處理。下面是改寫后的服務器端程序。
public class SocketServerM { public static void main(String[] args) throws IOException { int port = 7000; int clientNo = 1; ServerSocket serverSocket = new ServerSocket(port); // 創建線程池 ExecutorService exec = Executors.newCachedThreadPool(); try { while (true) { Socket socket = serverSocket.accept(); exec.execute(new SingleServer(socket, clientNo)); clientNo++; } } finally { serverSocket.close(); } }}class SingleServer implements Runnable { private Socket socket; private int clientNo; public SingleServer(Socket socket, int clientNo) { this.socket = socket; this.clientNo = clientNo; } @Override public void run() { try { DataInputStream dis = new DataInputStream( new BufferedInputStream(socket.getInputStream())); DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(socket.getOutputStream())); do { double length = dis.readDouble(); System.out.println("從客戶端" + clientNo + "接收到的邊長數據為:" + length); double result = length * length; dos.writeDouble(result); dos.flush(); } while (dis.readInt() != 0); } catch (IOException e) { e.printStackTrace(); } finally { System.out.println("與客戶端" + clientNo + "通信結束"); try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } }}
上面改進后的服務器端代碼可以支持不斷地并發響應網絡中的客戶請求。關鍵的地方在于多線程機制的運用,同時利用線程池可以改善服務器程序的性能。