1. 程式人生 > >Java使用多執行緒實現Socket多客戶端的通訊

Java使用多執行緒實現Socket多客戶端的通訊

 要想詳細瞭解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引數,當類中有匯入自己建立的類時,需要切換到能包含該類的資料夾下執行命令,否則會報錯)

此時的伺服器端: