教你怎么用java實現(xiàn)客戶端與服務(wù)器一問一答
運行效果
開啟多個客戶端
服務(wù)端效果:

客戶端效果:

當一個客戶端斷開連接:

代碼
因為代碼中有注釋,我就直接貼上來了
服務(wù)端:
package com.dayrain.server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
public class NioServer {
/**端口**/
private static final int PORT = 8081;
/**buffer大小**/
private static final int DEFAULT_BUFFER_SIZE = 1024;
private final Selector selector;
private final ByteBuffer readBuffer = ByteBuffer.allocate(NioServer.DEFAULT_BUFFER_SIZE);
private final ByteBuffer writeBuffer = ByteBuffer.allocate(NioServer.DEFAULT_BUFFER_SIZE);
private static int count = 0;
public static void main(String[] args) throws IOException {
new NioServer().start();
}
public NioServer() throws IOException {
//創(chuàng)建一個服務(wù)端channel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//設(shè)置為非阻塞
serverSocketChannel.configureBlocking(false);
//獲取服務(wù)器socket
ServerSocket socket = serverSocketChannel.socket();
//綁定ip和端口
socket.bind(new InetSocketAddress(NioServer.PORT));
//創(chuàng)建多路復用選擇器,并保持打開狀態(tài),直到close
selector = Selector.open();
//將服務(wù)器管道注冊到selector上,并監(jiān)聽accept事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("server start on port: " + NioServer.PORT);
start();
}
public void start() throws IOException {
//selector是阻塞的,直到至少有一個客戶端連接。
while (selector.select() > 0) {
//SelectionKey是channel想Selector注冊的令牌,可以通過chancel取消(不是立刻取消,會放進一個cancel list里面,下一次select時才會把它徹底刪除)
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
//當這個key的channel已經(jīng)準備好接收套接字連接
if(selectionKey.isAcceptable()) {
connectHandle(selectionKey);
}
//當這個key的channel已經(jīng)準備好讀取數(shù)據(jù)時
if(selectionKey.isReadable()) {
readHandle(selectionKey);
}
}
}
}
/**
* 處理連接
* @param selectionKey
*/
private void connectHandle(SelectionKey selectionKey) throws IOException {
//注意,服務(wù)端用的是ServerSocketChannel,BIO中是ServerSocket
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
if(socketChannel == null) {
return;
}
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
System.out.println("客戶端連接成功,當前總數(shù):" + (++count));
writeBuffer.clear();
writeBuffer.put("連接成功".getBytes(StandardCharsets.UTF_8));
writeBuffer.flip();
socketChannel.write(writeBuffer);
}
/**
* 讀取數(shù)據(jù)
* @param selectionKey
*/
private void readHandle(SelectionKey selectionKey){
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
try {
readBuffer.clear();
int read = socketChannel.read(readBuffer);
if(read > 0) {
readBuffer.flip();
String receiveData = StandardCharsets.UTF_8.decode(readBuffer).toString();
System.out.println("收到客戶端消息: " + receiveData);
writeBuffer.clear();
writeBuffer.put(receiveData.getBytes(StandardCharsets.UTF_8));
writeBuffer.flip();
socketChannel.write(writeBuffer);
}
}catch (Exception e) {
try {
socketChannel.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
System.out.println("客戶端斷開了連接~~");
count--;
}
}
}
客戶端
package com.dayrain.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
public class NioClient {
private static final int PORT = 8081;
private static final int DEFAULT_BUFFER_SIZE = 1024;
private final Selector selector;
private final ByteBuffer readBuffer = ByteBuffer.allocate(NioClient.DEFAULT_BUFFER_SIZE);
private final ByteBuffer writeBuffer = ByteBuffer.allocate(NioClient.DEFAULT_BUFFER_SIZE);
public static void main(String[] args) throws IOException {
NioClient nioClient = new NioClient();
//終端監(jiān)聽用戶輸入
new Thread(nioClient::terminal).start();
//這個方法是阻塞的,要放在最后
nioClient.start();
}
public NioClient() throws IOException {
selector = Selector.open();
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(InetAddress.getLocalHost(), NioClient.PORT));
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
public void start() throws IOException {
while (selector.select() > 0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
//拿到selectionKey后要刪除,否則會重復處理
iterator.remove();
if(selectionKey.isReadable()) {
handleRead(selectionKey);
}
//只要連接成功,selectionKey.isWritable()一直為true
if(selectionKey.isWritable()) {
handleWrite(selectionKey);
}
}
}
}
/**
* 監(jiān)聽寫操作
*/
private void handleWrite(SelectionKey selectionKey) throws IOException {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// writeBuffer有數(shù)據(jù)就直接寫入,因為另開了線程監(jiān)聽用戶讀取,所以要上鎖
synchronized (writeBuffer) {
writeBuffer.flip();
while (writeBuffer.hasRemaining()) {
socketChannel.write(writeBuffer);
}
writeBuffer.compact();
}
}
/**
* 監(jiān)聽讀操作
*/
private void handleRead(SelectionKey selectionKey) throws IOException {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
readBuffer.clear();
socketChannel.read(readBuffer);
readBuffer.flip();
String res = StandardCharsets.UTF_8.decode(readBuffer).toString();
System.out.println("收到服務(wù)器發(fā)來的消息: " + res);
readBuffer.clear();
}
/**
* 監(jiān)聽終端的輸入
*/
private void terminal() {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
try {
String msg;
while ((msg = bufferedReader.readLine()) != null) {
synchronized (writeBuffer) {
writeBuffer.put((msg + "\r\n").getBytes(StandardCharsets.UTF_8));
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
到此這篇關(guān)于教你怎么用java實現(xiàn)客戶端與服務(wù)器一問一答的文章就介紹到這了,更多相關(guān)java實現(xiàn)客戶端與服務(wù)器一問一答內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringCloudAlibaba Nacos開啟鑒權(quán)解決跳過登錄頁面問題
對于Nacos,如果需要開啟權(quán)限控制,可以在 Nacos 控制臺上進行配置,本文主要介紹了SpringCloudAlibaba Nacos開啟鑒權(quán)解決跳過登錄頁面問題,感興趣的可以了解一下2023-10-10
淺談java Iterator.remove()方法的用法(詳解)
下面小編就為大家?guī)硪黄獪\談java Iterator.remove()方法的用法(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01
MyBatis中執(zhí)行相關(guān)SQL語句的方法
本文主要介紹了MyBatis中執(zhí)行相關(guān)SQL語句的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-08-08
Java求字符串中出現(xiàn)次數(shù)最多的字符串以及出現(xiàn)次數(shù)
這篇文章主要為大家詳細介紹了Java統(tǒng)計字符串中出現(xiàn)次數(shù)最多的字符串以及出現(xiàn)次數(shù),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04
SpringBoot實現(xiàn)Word轉(zhuǎn)PDF和TXT的實踐分享
研發(fā)工作中難免會遇到一些奇奇怪怪的需求,就比如最近,客戶提了個新需求:上傳一個WORD文檔,要求通過系統(tǒng)把該文檔轉(zhuǎn)換成PDF和TXT,所以本文給大家分享了SpringBoot實現(xiàn)Word轉(zhuǎn)PDF和TXT的實踐,感興趣的朋友可以參考下2024-08-08

