1.服务器使用nio的方式
1.1定义成员变量
private ServerSocketChannel serverSocketChannel;
private ServerSocket serverSocket;
private Selector selector;
1.2定义构造函数,进行配置初始化
public GroupChatServer() throws IOException {
// 开启一个serverSocketChannel
serverSocketChannel = ServerSocketChannel.open();
// 设置通道为非阻塞模式
serverSocketChannel.configureBlocking(false);
// 创建serverSocket,绑定端口
serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress("127.0.0.1", 6666));
// 初始化selector
selector = Selector.open();
// 将通道注册到selector并进行连接事件的监听
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
1.3监听事件,事件派发
private void listen() throws IOException {
while (true) {
if (selector.select(3000) <= 0) {
System.out.println("未感触到信息");
} else {
System.out.println("感触到信息");
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isAcceptable()) {
SocketChannel socketChannel = serverSocketChannel.accept();
// 通道设置为非阻塞,没数据读,不用阻塞
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
SocketAddress address = socketChannel.getRemoteAddress();
System.out.println("主机为" + address + "的主机已经连接进来了");
} else if (key.isReadable()) {
SocketChannel channel = (SocketChannel) key.channel();
channel.configureBlocking(false);
//读取数据
readData(channel);
}
// 删除key,防止重复
iterator.remove();
}
}
}
}
1.4读取和转发的具体实现
private void readData(SocketChannel channel) throws IOException {
ByteBuffer byteBuffer = ByteBuffer.allocate(64);
int read = channel.read(byteBuffer);
if (read > 0) {
String msg = new String(byteBuffer.array());
System.out.println("服务接受到了来自主机:"+channel.getRemoteAddress()+"的消息:"+msg);
// 进行消息转发
sendInfoToOthers(msg, channel);
}
}
private void sendInfoToOthers(String msg, SocketChannel channel) throws IOException {
Set<SelectionKey> keys = selector.keys();
for (SelectionKey key : keys) {
Channel targetChannel = key.channel();
//key.
//排除自己
if (targetChannel instanceof SocketChannel &&targetChannel != channel) {
SocketChannel dest = (SocketChannel)targetChannel;
//将msg 存储到buffer
ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
//将buffer 的数据写入 通道
dest.write(buffer);
}
}
}
1.5主函数
public static void main(String[] args) throws IOException {
GroupChatServer server = new GroupChatServer();
server.listen();
}
2.客户端写法,客户端用传统bio
2.1成员变量
Socket socket = null;
final static CountDownLatch cdl = new CountDownLatch(2);//参数为线程个数
2.2构造函数
public GroupChatClient() throws IOException {
socket = new Socket();
socket.connect(new InetSocketAddress("127.0.0.1", 6666));
}
2.3客户端处理逻辑主体
public void chat() {
if (socket.isConnected()) {
new Thread(() -> {
try {
while (true) {
readData();
}
} catch (Exception e) {
e.printStackTrace();
}
cdl.countDown();
}).start();//用于读
new Thread(() -> {
try {
while (true) {
Scanner in = new Scanner(System.in);
String msg = in.next();
writeData(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
cdl.countDown();
}).start();//用于写
}
}
2.4读写
public void readData() throws IOException {
InputStream inputStream = socket.getInputStream();
byte[] b = new byte[1024];
int read = inputStream.read(b);
if (read > 0) {
System.out.println(new String(b));
}
}
public void writeData(String msg) throws IOException {
OutputStream outputStream = socket.getOutputStream();
byte[] bytes = msg.getBytes();
outputStream.write(bytes);
System.out.println("成功写入");
}
2.5主函数
public static void main(String[] args) throws IOException, InterruptedException {
GroupChatClient groupChatClient = new GroupChatClient();
groupChatClient.chat();
cdl.await();
}
3.效果
3.1两客户端连接服务器成功

3.2转发效果
客户端1进行消息发送

服务端接受效果

客户端2接受效果

本文探讨了使用Java NIO实现的服务器端群聊服务器,同时对比了传统Bio客户端的连接方式。服务器采用非阻塞I/O处理多路连接,并展示了消息接收、转发和客户端连接的详细实现。客户端则通过同步阻塞IO进行操作。

被折叠的 条评论
为什么被折叠?



