要想詳細了解socket,大家請自行百度,我這里只簡單介紹。
在網絡中,我們可以利用ip地址+協議+端口號唯一標示網絡中的一個進程。而socket編程就是為了完成兩個唯一進程之間的通信(一個是客戶端,一個是服務器端),其中用到的協議是TCP/UDP協議,它們都屬於傳輸層的協議。
TCP是基於連接的協議,在收發數據前,需要建立可靠的連接,也就是所謂的三次握手。使用TCP協議時,數據會准確到達,但是效率較低。
UDP是面向非連接的協議,它不與對方建立連接,而是直接就把數據包發送過去。使用UDP協議時,傳輸效率高,但是不能保證數據准確到達,視頻聊天,語音聊天時就用的UDP協議。
以使用TCP協議通訊的socket為例,其交互流程大概是這樣子的:
服務器端 客戶端
創建服務器端的socket 創建客戶端的socket
綁定端口號 連接服務器端的端口
監聽端口 向服務器端發送數據
接收客戶端的連接請求 關閉socket
讀取客戶端發送數據
關閉socket
下面貼上代碼:
服務器端:
package SocketStudy;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketServer {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(10068);//創建綁定到特定端口的服務器Socket。
Socket socket = null;//需要接收的客戶端Socket
int count = 0;//記錄客戶端數量
System.out.println("服務器啟動");
//定義一個死循環,不停的接收客戶端連接
while (true) {
socket = serverSocket.accept();//偵聽並接受到此套接字的連接
InetAddress inetAddress=socket.getInetAddress();//獲取客戶端的連接
ServerThread thread=new ServerThread(socket,inetAddress);//自己創建的線程類
thread.start();//啟動線程
count++;//如果正確建立連接
System.out.println("客戶端數量:" + count);//打印客戶端數量
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
自定義線程類:
package SocketStudy;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class ServerThread extends Thread {
Socket socket = null;
InetAddress inetAddress=null;//接收客戶端的連接
public ServerThread(Socket socket,InetAddress inetAddress) {
this.socket = socket;
this.inetAddress=inetAddress;
}
@Override
public void run() {
InputStream inputStream = null;//字節輸入流
InputStreamReader inputStreamReader = null;//將一個字節流中的字節解碼成字符
BufferedReader bufferedReader = null;//為輸入流添加緩沖
OutputStream outputStream = null;//字節輸出流
OutputStreamWriter writer = null;//將寫入的字符編碼成字節后寫入一個字節流
try {
inputStream = socket.getInputStream();
inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
bufferedReader = new BufferedReader(inputStreamReader);
String info = null;//臨時
//循環讀取客戶端信息
while ((info = bufferedReader.readLine()) != null) {
//獲取客戶端的ip地址及發送數據
System.out.println("服務器端接收:"+"{'from_client':'"+socket.getInetAddress().getHostAddress()+"','data':'"+info+"'}");
}
socket.shutdownInput();//關閉輸入流
//響應客戶端請求
outputStream = socket.getOutputStream();
writer = new OutputStreamWriter(outputStream, "UTF-8");
writer.write("{'to_client':'"+inetAddress.getHostAddress()+"','data':'我是服務器數據'}");
writer.flush();//清空緩沖區數據
} catch (IOException e) {
e.printStackTrace();
} finally {
//關閉資源
try {
if (writer != null) {
writer.close();
}
if (outputStream != null) {
outputStream.close();
}
if (bufferedReader != null) {
bufferedReader.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
if (inputStream != null) {
inputStream.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客戶端:
package SocketStudy;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class SocketClient {
public static void main(String[] args) {
try {
Socket socket = new Socket("服務器的ip", 10068);
OutputStream outputStream = socket.getOutputStream();//得到一個輸出流,用於向服務器發送數據
OutputStreamWriter writer=new OutputStreamWriter(outputStream,"UTF-8");//將寫入的字符編碼成字節后寫入一個字節流
System.out.println("請輸入數據:");
Scanner sc = new Scanner(System.in);
String data = sc.nextLine();
writer.write(data);
writer.flush();//刷新緩沖
socket.shutdownOutput();//只關閉輸出流而不關閉連接
//獲取服務器端的響應數據
InputStream inputStream = socket.getInputStream();//得到一個輸入流,用於接收服務器響應的數據
InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"UTF-8");//將一個字節流中的字節解碼成字符
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);//為輸入流添加緩沖
String info = null;
System.out.println("客戶端IP地址:"+socket.getInetAddress().getHostAddress());
//輸出服務器端響應數據
while ((info = bufferedReader.readLine()) != null) {
System.out.println("客戶端接收:" + info);
}
//關閉資源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
writer.close();
outputStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
運行結果(先運行服務器端,再運行客戶端):
服務器:
客戶端:
回車之后
客戶端:
服務器端:
在cmd下運行(先編譯再運行生成的.class文件,如果有中文,需要加encoding參數,當類中有導入自己創建的類時,需要切換到能包含該類的文件夾下執行命令,否則會報錯)
此時的服務器端: