Java NIO聊天室(简单图形界面)

基于NIO实现的聊天室

demo地址:github-chat

流程:

服务端
  • 服务器创建ServerSocket接受所有机器的连接,并设置读事件
  • 当有读事件触发,遍历可读事件,广播到selector中的所有连接输出流中
客户端
  • 新建读线程:注册可读事件
  • 服务器广播到该channel ===>输出数据
说明

服务端代码可以放到远端外网服务器上,修改Client的连接地址就好!

效果

demo

图形界面

demo

服务端代码:


import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class Server {

	private Selector selector;
	private ByteBuffer buffer = ByteBuffer.allocate(2048);
	private ServerSocket serverSocket;
	private Map<String, Object> set = new HashMap<>();
	private Map<String, String> names = new HashMap<>();

	public Server(int port) throws IOException {
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		serverSocketChannel.configureBlocking(false);
		serverSocket = serverSocketChannel.socket();
		serverSocket.bind(new InetSocketAddress(port));
		selector = Selector.open();
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		System.out.println("server start on " + port);
	}

	public void listen() throws IOException {
		while (true) {
			selector.select();
			Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
			while (iterator.hasNext()) {
				SelectionKey key = iterator.next();
				iterator.remove();
				handleKey(key);
			}
			selector.selectedKeys().clear();
		}
	}

	private void handleKey(SelectionKey key) throws IOException {
		ServerSocketChannel server;
		SocketChannel client;
		if (key.isAcceptable()) {
			server = ((ServerSocketChannel) key.channel());
			client = server.accept();
			client.configureBlocking(false);
			client.register(selector, SelectionKey.OP_READ);
			System.out.println("receive connection from " + client.getRemoteAddress());
			boardMsg("当前有" + (names.size() + 1) + "人\n", null);
			write(client, "欢迎来到聊天室,请输入你的昵称!");
			key.interestOps(SelectionKey.OP_ACCEPT);
		} else if (key.isReadable()) {
			client = ((SocketChannel) key.channel());
			try {
				String[] msg = rec(client).split("###");
				if (msg.length > 0) {
					if (msg.length == 1) {
						if (set.containsKey(msg[0])) {
							write(client, " 重复的昵称:" + msg[0] + " ! 请重新输入!");
						} else {
							set.put(msg[0], null);
							names.put(client.getRemoteAddress().toString(),msg[0]);
							write(client, "hello " + msg[0]);
						}
					} else if (msg.length == 2) {
						System.out.println(client.getRemoteAddress() + " named " + msg[0] + " said: " + msg[1]);
						boardMsg(msg[0] + " 说: " + msg[1], client);
					}
				}
				key.interestOps(SelectionKey.OP_READ);
			} catch (Exception e) {
				String address = client.getRemoteAddress().toString();
				System.out.println("receive disconnection from " + address);
				String name = names.get(address);
				set.remove(name);
				names.remove(address);
				client.close();
				boardMsg(name + " 离开了聊天室!" + "还剩" + names.size() + "人!", null);
			}
		}
	}

	private void boardMsg(String msg, SocketChannel except) throws IOException {
		for (SelectionKey k : selector.keys()) {
			Channel target = k.channel();
			if (target.isOpen() && target instanceof SocketChannel && target != except) {
				write((SocketChannel) target, msg);
			}
		}
	}

	private String rec(SocketChannel channel) throws IOException {
		buffer.clear();
		int count = channel.read(buffer);
		return new String(buffer.array(), 0, count, StandardCharsets.UTF_8);
	}

	private void write(SocketChannel channel, String content) throws IOException {
		buffer.clear();
		buffer.put(content.getBytes(StandardCharsets.UTF_8));
		buffer.flip();
		channel.write(buffer);
	}

	public static void main(String[] args) throws IOException {
		Server server = new Server(7777);
		server.listen();
	}
}

客户端代码:

import java.io.IOException;
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;
import java.util.Scanner;
import java.util.Set;

public class Client {

	private static ByteBuffer buffer = ByteBuffer.allocate(1024);
	private final static InetSocketAddress address = new InetSocketAddress("127.0.0.1", 7777);

	static private String rec(SocketChannel channel) throws IOException {
		buffer.clear();
		int count = channel.read(buffer);
		return new String(buffer.array(), 0, count, StandardCharsets.UTF_8);
	}

	static private void write(SocketChannel channel, String content) throws IOException {
		buffer.clear();
		buffer.put(content.getBytes(StandardCharsets.UTF_8));
		buffer.flip();
		channel.write(buffer);
	}

	private static volatile boolean success = false;

	private static volatile String name = "wzy";

	public static void main(String[] args) throws IOException, InterruptedException {
		Selector selector = Selector.open();
		SocketChannel socketChannel = SocketChannel.open(address);
		socketChannel.configureBlocking(false);
		socketChannel.register(selector, SelectionKey.OP_READ);
		new Thread(() -> {
			SocketChannel client = null;
			try {
				while (true) {
					selector.select();
					Set<SelectionKey> set = selector.selectedKeys();
					Iterator<SelectionKey> iterator = set.iterator();
					while (iterator.hasNext()) {
						SelectionKey key = iterator.next();
						iterator.remove();
						if (key.isReadable()) {
							client = ((SocketChannel) key.channel());
							String msg = rec(client);
							if (msg.contains("hello")) {
								name = msg.substring(6);
								success = true;
							}
							System.out.println(msg);
							key.interestOps(SelectionKey.OP_READ);
						}
					}
					set.clear();
				}
			} catch (Exception e) {
				if (client != null) {
					try {
						client.close();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
			}
		}).start();
		Scanner scanner = new Scanner(System.in);
		String tmp = null;
		while (true) {
			tmp = scanner.nextLine();
			if (success)
				break;
			write(socketChannel, tmp);
		}
		write(socketChannel, name + "###" + tmp);
		while (true) {
			String msg = scanner.nextLine();
			if (!msg.trim().equals(""))
				write(socketChannel, name + "###" + msg);
		}
	}
}
小明用基于socket通道做了一个实时聊天,实现了多客户客户端、服务端的实时通信,但是老师要求每个人都有自己的独特功能........于是,小明想,创新是不可能创新的,那现在QQ的工鞥有什么能够移植的呢,,,小明效果了一圈,发现大多功能都是基于文件传输,去找了一下socket的文件传输,果断撂挑子。还有什呢,要不就自己客户端约定好,特殊的字符串表示特殊操作,比如下面小明要举个例子:                                                                                                                                   有两个客户端A、B,客户端都是一模一样的,里面资源实现存放好10张图片,并且约定:发送消息为“@”的时候,这个字符串后边会跟着一个数字,咱们两个同时把数字代表编号的图片输出到聊天框,如                                                                A发送给B:@     那现在,两个客户端就会把3号图片输出到聊天框,造成发送的是图片的假象 说干就干,先实现自己窗口判断输出图片,在获取于是先在获取聊天输入区文本时做判断(我正添加表情按钮,到时候就不用记住暗号了),在输出相应编号图片,欸不对啊,这个聊天信息展示区是个文本框啊,那就给改成面板,使用新建面板类,使用流式布局规定布局宽度(不然他会横着输出消息)然后是面板添加滚动条,然后再聊天区打出来就好了 ----(一个积分,望土豪点一点,我也想恰饭) 你们看到这个代码的时候,我主页应该就有相关博文了,大家可以去参考
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值