Java編程Socket實現(xiàn)多個客戶端連接同一個服務(wù)端代碼
Java Socket(套接字)通常也稱作"套接字",用于描述IP地址和端口,是一個通信鏈的句柄。應(yīng)用程序通常通過"套接字"向網(wǎng)絡(luò)發(fā)出請求或者應(yīng)答網(wǎng)絡(luò)請求。
使用Socket實現(xiàn)多個客戶端和同一客戶端通訊;首先客戶端連接服務(wù)端發(fā)送一條消息,服務(wù)端接收到消息后進行處理,完成后再回復(fù)客戶端一條消息。本人通過自己的思維編寫了一份服務(wù)端和客戶端實現(xiàn)的代碼,望能與大家相互學(xué)習(xí),共同進步。
服務(wù)端代碼
/**
* Socket服務(wù)端
* 功能說明:
*
*/
public class Server {
/**
* 入口
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 為了簡單起見,所有的異常信息都往外拋
int port = 8899;
// 定義一個ServiceSocket監(jiān)聽在端口8899上
ServerSocket server = new ServerSocket(port);
System.out.println("等待與客戶端建立連接...");
while (true) {
// server嘗試接收其他Socket的連接請求,server的accept方法是阻塞式的
Socket socket = server.accept();
/**
* 我們的服務(wù)端處理客戶端的連接請求是同步進行的, 每次接收到來自客戶端的連接請求后,
* 都要先跟當(dāng)前的客戶端通信完之后才能再處理下一個連接請求。 這在并發(fā)比較多的情況下會嚴(yán)重影響程序的性能,
* 為此,我們可以把它改為如下這種異步處理與客戶端通信的方式
*/
// 每接收到一個Socket就建立一個新的線程來處理它
new Thread(new Task(socket)).start();
}
// server.close();
}
/**
* 處理Socket請求的線程類
*/
static class Task implements Runnable {
private Socket socket;
/**
* 構(gòu)造函數(shù)
*/
public Task(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
handlerSocket();
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* 跟客戶端Socket進行通信
*
* @throws IOException
*/
private void handlerSocket() throws Exception {
// 跟客戶端建立好連接之后,我們就可以獲取socket的InputStream,并從中讀取客戶端發(fā)過來的信息了
/**
* 在從Socket的InputStream中接收數(shù)據(jù)時,像上面那樣一點點的讀就太復(fù)雜了,
* 有時候我們就會換成使用BufferedReader來一次讀一行
*
* BufferedReader的readLine方法是一次讀一行的,這個方法是阻塞的,直到它讀到了一行數(shù)據(jù)為止程序才會繼續(xù)往下執(zhí)行,
* 那么readLine什么時候才會讀到一行呢?直到程序遇到了換行符或者是對應(yīng)流的結(jié)束符readLine方法才會認(rèn)為讀到了一行,
* 才會結(jié)束其阻塞,讓程序繼續(xù)往下執(zhí)行。
* 所以我們在使用BufferedReader的readLine讀取數(shù)據(jù)的時候一定要記得在對應(yīng)的輸出流里面一定要寫入換行符(
* 流結(jié)束之后會自動標(biāo)記為結(jié)束,readLine可以識別),寫入換行符之后一定記得如果輸出流不是馬上關(guān)閉的情況下記得flush一下,
* 這樣數(shù)據(jù)才會真正的從緩沖區(qū)里面寫入。
*/
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream(), "UTF-8"));
StringBuilder sb = new StringBuilder();
String temp;
int index;
while ((temp = br.readLine()) != null) {
if ((index = temp.indexOf("eof")) != -1) {
// 遇到eof時就結(jié)束接收
sb.append(temp.substring(0, index));
break;
}
sb.append(temp);
}
System.out.println("Form Cliect[port:" + socket.getPort()
+ "] 消息內(nèi)容:" + sb.toString());
// 回應(yīng)一下客戶端
Writer writer = new OutputStreamWriter(socket.getOutputStream(),
"UTF-8");
writer.write(String.format("Hi,%d.天朗氣清,惠風(fēng)和暢!", socket.getPort()));
writer.flush();
writer.close();
System.out.println(
"To Cliect[port:" + socket.getPort() + "] 回復(fù)客戶端的消息發(fā)送成功");
br.close();
socket.close();
}
}
}
客戶端代碼
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.Socket;
/** * Socket客戶端
* 功能說明: * * @author 大智若愚的小懂 * @Date 2016年8月30日 * @version 1.0 */
public class Client {
/** * 入口 * @param args */
public static void main(String[] args) {
// 開啟三個客戶端,一個線程代表一個客戶端
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
@Override public void run() {
try {
TestClient client = TestClientFactory.createClient();
client.send(String.format("Hello,Server!I'm %d.這周末天氣如何。", client.client.getLocalPort()));
client.receive();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
).start();
}
}
/** * 生產(chǎn)測試客戶端的工廠 */
static class TestClientFactory {
public static TestClient createClient() throws Exception {
return new TestClient("127.0.0.1", 8899);
}
}
/** * 測試客戶端 */
static class TestClient {
/** * 構(gòu)造函數(shù) * @param host 要連接的服務(wù)端IP地址 * @param port 要連接的服務(wù)端對應(yīng)的監(jiān)聽端口 * @throws Exception */
public TestClient(String host, int port) throws Exception {
// 與服務(wù)端建立連接
this.client = new Socket(host, port);
System.out.println("Cliect[port:" + client.getLocalPort() + "] 與服務(wù)端建立連接...");
}
private Socket client;
private Writer writer;
/** * 發(fā)送消息 * @param msg * @throws Exception */
public void send(String msg) throws Exception {
// 建立連接后就可以往服務(wù)端寫數(shù)據(jù)了
if(writer == null) {
writer = new OutputStreamWriter(client.getOutputStream(), "UTF-8");
}
writer.write(msg);
writer.write("eof\n");
writer.flush();
// 寫完后要記得flush
System.out.println("Cliect[port:" + client.getLocalPort() + "] 消息發(fā)送成功");
}
/** * 接收消息 * @throws Exception */
public void receive() throws Exception {
// 寫完以后進行讀操作
Reader reader = new InputStreamReader(client.getInputStream(), "UTF-8");
// 設(shè)置接收數(shù)據(jù)超時間為10秒
client.setSoTimeout(10*1000);
char[] chars = new char[64];
int len;
StringBuilder sb = new StringBuilder();
while ((len = reader.read(chars)) != -1) {
sb.append(new String(chars, 0, len));
}
System.out.println("Cliect[port:" + client.getLocalPort() + "] 消息收到了,內(nèi)容:" + sb.toString());
reader.close();
// 關(guān)閉連接
writer.close();
client.close();
}
}
}
接下來模擬一下:
1.首先運行服務(wù)端

2.接著運行客戶端(開三個客戶端請求)
為了演示有所區(qū)分,服務(wù)端我使用的是Eclipse工具,客戶端使用的是IntelliJ IDEA工具。這時可以看到客戶端在控制臺打印出來的消息

一個Port端口號代表一個客戶端,回過來看下服務(wù)端在控制臺打印出來的消息

總結(jié)
以上就是本文關(guān)于Java編程Socket實現(xiàn)多個客戶端連接同一個服務(wù)端代碼的全部內(nèi)容,希望對大家有所幫助。有問題可以留言,小編會及時回復(fù)大家的。
相關(guān)文章
Java guava monitor監(jiān)視器線程的使用詳解
工作中的場景中是否存在類似這樣的場景,需要提交的線程在某個觸發(fā)條件下執(zhí)行。本文主要就是使用guava中的monitor來優(yōu)雅的實現(xiàn)帶監(jiān)視器的線程2021-11-11
java多線程編程之使用Synchronized關(guān)鍵字同步類方法
JAVA中要想解決“臟數(shù)據(jù)”的問題,最簡單的方法就是使用synchronized關(guān)鍵字來使run方法同步,看下面的代碼,只要在void和public之間加上synchronized關(guān)鍵字2014-01-01
java實現(xiàn)微信小程序登錄態(tài)維護的示例代碼
本篇文章主要介紹了java實現(xiàn)微信小程序登錄態(tài)維護的示例代碼,具有一定的參考價值,有興趣的可以了解一下2017-09-09

