Java 网络 socket 编程
使用套接字實現基于 TCP 協議的服務器和客戶機程序
依據 TCP 協議,在 C/S 架構的通訊過程中,客戶端和服務器的 Socket 動作如下:
客戶端:
1.用服務器的 IP 地址和端口號實例化 Socket 對象。
2.調用 connect 方法,連接到服務器上。
3.將發送到服務器的 IO 流填充到 IO 對象里,比如 BufferedReader/PrintWriter。
4.利用 Socket 提供的 getInputStream 和 getOutputStream 方法,通過 IO 流對象,向服務器發送數據流。
5. 通訊完成后,關閉打開的 IO 對象和 Socket。
服務器:
1. 在服務器,用一個端口來實例化一個 ServerSocket 對象。此時,服務器就可以這個端口時刻監聽從客戶端發來的連接請求。
2.調用 ServerSocket 的 accept 方法,開始監聽連接從端口上發來的連接請求。
3.利用 accept 方法返回的客戶端的 Socket 對象,進行讀寫 IO 的操作通訊完成后,關閉打開的流和 Socket 對象。
?
下面是一個簡單的客戶端與服務器端的例子:
客戶端:
package my.socket.tcp;import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; /** 上述客戶端代碼的主要業務邏輯是: 1. 同樣定義了通訊端口號,這里給出的端口號必須要和服務器端的一致。 2. 在 main 函數里,根據地址信息“localhost”,創建一個 InetAddress 類型的對象addr。這里,因為我們把客戶端和服務器端的代碼都放在本機運行,所以同樣可以用“127.0.0.1”字符串,來創建 InetAddress 對象。 3. 根據 addr 和端口號信息,創建一個 Socket 類型對象,該對象用來同服務器端的ServerSocket 類型對象交互,共同完成 C/S 通訊流程。 4. 同樣地創建 in 和 out 兩類 IO 句柄,用來向服務器端發送和接收數據流。 5. 通過 out 對象,向服務器端發送"Hello Server,I am …"的字符串。發送后,同樣可以用 in 句柄,接收從服務器端的消息。 6. 利用 out 對象,發送”byebye”字符串,用以告之服務器端,本次通訊結束。 7. 在 finally 從句里,關閉 Socket 對象,斷開同服務器端的連接。* @author asus**/ public class ClientCode {static String clientName = "Mike";// 端口號public static int portNo = 3333;public static void main(String[] args) throws IOException {// 設置連接地址類,連接本地InetAddress addr = InetAddress.getByName("localhost");// 要對應服務器端的 3333 端口號Socket socket = new Socket(addr, portNo);try {System.out.println("socket = " + socket);// 設置 IO 句柄BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);out.println("Hello Server,I am " + clientName);String str = in.readLine();System.out.println(str);out.println("byebye");} finally {System.out.println("close the Client socket and the io.");socket.close();}} }服務器端:
package my.socket.tcp;import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; /*** 編寫服務器端的主體代碼:這段代碼的主要業務邏輯是: 1. 在上述代碼里的 main 函數前,我們設置了通訊所用到的端口號,為 3333。 2. 在 main 函數里,根據給定 3333 端口號,初始化一個 ServerSocket 對象 s,該對象用來承擔服務器端監聽連接和提供通訊服務的功能。 3. 調用 ServerSocket 對象的 accept 方法,監聽從客戶端的連接請求。當完成調用accept 方法后,整段服務器端代碼將回阻塞在這里,直到客戶端發來 connect 請求。 4. 當客戶端發來 connect 請求,或是通過構造函數直接把客戶端的 Socket 對象連接到服務器端后,阻塞于此的代碼將會繼續運行。此時服務器端將會根據 accept 方法的執行結果,用一個 Socket 對象來描述客戶端的連接句柄。 5. 創建兩個名為 in 和 out 的對象,用來傳輸和接收通訊時的數據流。 6. 創建一個 while(true)的死循環,在這個循環里,通過 in.readLine()方法,讀取從客戶端發送來的 IO 流(字符串),并打印出來。如果讀到的字符串是“byebye”,那么退出while 循環。 7. 在 try…catch…finally 語句段里,不論在 try 語句段里是否發生異常,并且不論這些異常的種類,finally 從句都將會被執行到。在 finally 從句里,將關閉描述客戶端的連接句柄 socket 對象和 ServerSocket 類型的 s 對象。* @author asus**/ public class ServerCode {// 設置端口號public static int portNo = 3333;public static void main(String[] args) throws IOException {ServerSocket s = new ServerSocket(portNo);System.out.println("The Server is start: " + s);// 阻塞,直到有客戶端連接Socket socket = s.accept();try {System.out.println("Accept the Client: " + socket);// 設置 IO 句柄BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);while (true) {String str = in.readLine();if (str.equals("byebye")) {break;}System.out.println("In Server reveived the info: " + str);out.println(str);}} finally {System.out.println("close the Server socket and the io.");socket.close();s.close();}} }先運行服務器端,再運行客戶端之后,可以看到服務器端接收到來自客戶端發送的信息。
通常網絡編程都是用多線程來實現,將大大地提高服務器端的利用效率,并能使服務器端能具備完善的
服務功能。
首先運行服務器端,再運行客戶端,可以清楚的看到服務器端多線程接收到來自客戶端的消息。
下面是同時開啟服務器端和客戶端,兩者進行不間斷的通信。
package my.socket.udp;import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException;public class ClientBean {// 描述 UDP 通訊的 DatagramSocket 對象private DatagramSocket ds;// 用來封裝通訊字符串private byte buffer[];// 客戶端的端口號private int clientport;// 服務器端的端口號private int serverport;// 通訊內容private String content;// 描述通訊地址private InetAddress ia;public ClientBean() throws SocketException, UnknownHostException {buffer = new byte[1024];clientport = 1985;serverport = 1986;content = "";ds = new DatagramSocket(clientport);ia = InetAddress.getByName("localhost");}public void sendToServer() throws IOException {buffer = content.getBytes();ds.send(new DatagramPacket(buffer, content.length(), ia, serverport));}// 以下是各屬性的 Get 和 Set 類型方法public byte[] getBuffer() {return buffer;}public void setBuffer(byte[] buffer) {this.buffer = buffer;}public int getClientport() {return clientport;}public void setClientport(int clientport) {this.clientport = clientport;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public DatagramSocket getDs() {return ds;}public void setDs(DatagramSocket ds) {this.ds = ds;}public InetAddress getIa() {return ia;}public void setIa(InetAddress ia) {this.ia = ia;}public int getServerport() {return serverport;}public void setServerport(int serverport) {this.serverport = serverport;} } package my.socket.udp;import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException;public class ServerBean {// 描述 UDP 通訊的 DatagramSocket 對象private DatagramSocket ds;// 用來封裝通訊字符串private byte buffer[];// 客戶端的端口號private int clientport;// 服務器端的端口號private int serverport;// 通訊內容private String content;// 描述通訊地址private InetAddress ia;public ServerBean() throws SocketException, UnknownHostException {buffer = new byte[1024];clientport = 1985;serverport = 1986;content = "";ds = new DatagramSocket(serverport);ia = InetAddress.getByName("localhost");}public void listenClient() throws IOException {// 在循環體里接收消息while (true) {// 初始化 DatagramPacket 類型的變量DatagramPacket dp = new DatagramPacket(buffer, buffer.length);// 接收消息,并把消息通過 dp 參數返回ds.receive(dp);content = new String(dp.getData(), 0, dp.getLength());// 打印消息print();}}public void print() {System.out.println(content);}public DatagramSocket getDs() {return ds;}public void setDs(DatagramSocket ds) {this.ds = ds;}public byte[] getBuffer() {return buffer;}public void setBuffer(byte[] buffer) {this.buffer = buffer;}public int getClientport() {return clientport;}public void setClientport(int clientport) {this.clientport = clientport;}public int getServerport() {return serverport;}public void setServerport(int serverport) {this.serverport = serverport;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public InetAddress getIa() {return ia;}public void setIa(InetAddress ia) {this.ia = ia;}} package my.socket.udp;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;public class UDPClient implements Runnable {public static String content;public static ClientBean client;public void run() {try {client.setContent(content);client.sendToServer();} catch (Exception ex) {System.err.println(ex.getMessage());}}// end of run// main 方法// …public static void main(String args[]) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));client = new ClientBean();System.out.println("客戶端啟動...");while (true) {// 接收用戶輸入content = br.readLine();// 如果是 end 或空,退出循環if (content == null || content.equalsIgnoreCase("end") || content.equalsIgnoreCase("")) {break;}// 開啟新線程,發送消息new Thread(new UDPClient()).start();}} } package my.socket.udp;import java.io.IOException;public class UDPServer {public static void main(String args[]) throws IOException {System.out.println("服務器端啟動...");// 初始化 ServerBean 對象ServerBean server = new ServerBean();// 開啟監聽程序server.listenClient();} }先運行服務器端,再運行客戶端。
在客戶端輸入想要發送的字符,在服務器端可以接收到。
總結
以上是生活随笔為你收集整理的Java 网络 socket 编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Eclipse(javaweb)刚换工作
- 下一篇: 简单谈谈linux的文件权限问题