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();
}
}
結果博主就不發了,自己在兩個端口自問自答感覺有點怪。