教你怎么用java实现客户端与服务器一问一答

网友投稿 241 2022-10-26


教你怎么用java实现客户端与服务器一问一答

运行效果

开启多个客户端

服务端效果:

客户端效果:

当一个客户端断开连接:

代码

因为代码中有注释,我就直接贴上来了

服务端:

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 {

//创建一个服务端channel

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

//设置为非阻塞

serverSocketChannel.configureBlocking(false);

//获取服务器socket

ServerSocket socket = serverSocketChannel.socket();

//绑定ip和端口

socket.bind(new InetSocketAddress(NioServer.PORT));

//创建多路复用选择器,并保持打开状态,直到close

selector = Selector.open();

//将服务器管道注册到selector上,并监听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 iterator = selector.selectedKeys().iterator();

while (iterator.hasNext()) {

SelectionKey selectionKey = iterator.next();

iterator.remove();

//当这个key的channel已经准备好接收套接字连接

if(selectionKey.isAcceptable()) {

connectHandle(selectionKey);

}

//当这个key的channel已经准备好读取数据时

if(selectionKey.isReadable()) {

readHandle(selectionKey);

}

}

}

}

/**

* 处理连接

* @param selectionKey

*/

private void connectHandle(SelectionKey selectionKey) throws IOException {

//注意,服务端用的是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("客户端连接成功,当前总数:" + (++count));

writeBuffer.clear();

writeBuffer.put("连接成功".getBytes(StandardCharsets.UTF_8));

writeBuffer.flip();

socketChannel.write(writeBuffer);

}

/**

* 读取数据

* @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);

writepksJFkBuffer.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();

//终端监听用户输入

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 iterator = selector.selectedKeys().iterator();

whhttp://ile (iterator.hasNext()) {

SelectionKey selectionKey = iterator.next();

//拿到selectionKey后要删除,否则会重复处理

iterator.remove();

if(selectionKey.isReadable()) {

handleRead(selectionKey);

}

//只要连接成功,selectionKey.isWritable()一直为true

if(selectionKey.isWritable()) {

handleWrite(selectionKey);

}

}

}

}

/**

* 监听写操作

*/

private void handleWrite(SelectionKey selectionKey) throws IOException {

SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

// writeBuffer有数据就直接写入,因为另开了线程监听用户读取,所以要上锁

synchronized (writeBuffer) {

writeBuffer.flip();

while (writeBuffer.hasRemaining()) {

socketChannel.write(writeBuffer);

}

writeBuffer.compact();

}

}

/**

* 监听读操作

*/

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("收到服务器发来的消息: " + res);

readBuffer.clear();

}

/**

* 监听终端的输入

*/

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();

}

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:T-SQL---多值模糊查询的处理
下一篇:Spring MVC__自定义日期类型转换器
相关文章

 发表评论

暂时没有评论,来抢沙发吧~