主要利用Java NIO中TCP编程实现服务端和客户端通信,服务端接受客户端消息广播给其它客户端,客户端接受消息使用多线程实现。
服务端
服务端通过一个选择器来实现一个线程处理多个通道,实现IO多路复用。服务端代码:
public class Server {
public static void main(String[] args) throws IOException {
//启动服务端
Server server = new Server();
server.start();
}
//启动服务端
public void start() throws IOException {
//创建一个选择器
Selector selector = Selector.open();
//创建一个服务端通道ServerSocketChannel
ServerSocketChannel ssc = ServerSocketChannel.open();
//给服务端绑定地址和端口
ssc.socket().bind(new InetSocketAddress("127.0.0.1",8080));
//设置为非阻塞
ssc.configureBlocking(false);
System.out.println("服务端启动成功");
//将通道注册到选择器中,并设置选择器感兴趣的事情是连接到达
ssc.register(selector, SelectionKey.OP_ACCEPT);
//死循环,一直遍历
while(true){
int num = selector.select();//获得当前选择器中有没有达到要求的通道
if(num == 0){
continue;
}
//当有多个或者一个通过有感兴趣的时候,使用迭代器遍历,获取key
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
//连接可通
if(selectionKey.isAcceptable()){
operatorAccept(ssc,selector);
}else if(selectionKey.isReadable()){//连接可读
operatorRead(selectionKey,selector);
}
iterator.remove();//移出当前遍历
}
}
}
//当连接成功后,返回给客户端消息
private void operatorAccept(ServerSocketChannel ssc,Selector selector) throws IOException {
SocketChannel socketChannel = ssc.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
//回复消息
socketChannel.write(Charset.forName("UTF-8").encode("您已进入聊天室,请注意隐式"));
}
//当一个客户端发送消息以后,将其消息广播给其它客户端
private void operatorRead(SelectionKey selectionKey,Selector selector) throws IOException {
SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);//开辟一个1024字节大小的缓冲区用于读写消息
StringBuilder stringBuilder = new StringBuilder();//使用StringBulder拼接消息
int len;
while((len = socketChannel.read(buffer)) != -1){
if(len == 0){//当没有任何消息的时候就退出循环
break;
}
buffer.flip();//读写模式切换
stringBuilder.append(Charset.forName("UTF-8").decode(buffer));
}
String message = stringBuilder.toString();
//Channel再次注册
socketChannel.register(selector,SelectionKey.OP_READ);
if(message.length() > 0){
//输出消息
System.out.println(message);
//广播给其它客户端
castOtherClient(message,selector,socketChannel);
}
}
//将消息广播给其它客户端
private void castOtherClient(String message,Selector selector,SocketChannel socketChannel) throws IOException {
Set<SelectionKey> keys = selector.keys();
for(SelectionKey key : keys){
Channel channel = key.channel();
//排除自己,给其它所有客户端发送消息
if(channel instanceof SocketChannel && channel != socketChannel){
((SocketChannel)channel).write(
Charset.forName("UTF-8").encode(message));
}
}
}
}
客户端
public class Client {
private String name;//给每个客户端标记名字
public Client(String name) {
this.name = name;
}
public void start() throws IOException {
//开启一个到服务端的客户端通道
SocketChannel socketChannel = SocketChannel.open(
new InetSocketAddress("127.0.0.1",8080));
//接受服务端响应数据
Selector selector = Selector.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
//创建线程进行处理
new Thread(new ClientThread(selector)).start();
//发送消息
Scanner scanner = new Scanner(System.in);
while(scanner.hasNextLine()){
String msg = scanner.nextLine();
if(msg.length() > 0){
//发送消息到服务端
socketChannel.write(Charset.forName("Utf-8").encode(name + " : "+msg));
}
}
}
}
//使用实现Runable来实现多线程
public class ClientThread implements Runnable{
private Selector selector;
public ClientThread(Selector selector) {
this.selector = selector;
}
@Override
public void run() {
while(true){
int num = 0;
try {
num = selector.select();
} catch (IOException e) {
e.printStackTrace();
}
if(num == 0){
continue;
}
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
if(selectionKey.isReadable()){
try {
operatorRead(selectionKey,selector);
} catch (IOException e) {
e.printStackTrace();
}
}
iterator.remove();
}
}
}
//连接可读
private void operatorRead(SelectionKey selectionKey,Selector selector) throws IOException {
SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
StringBuilder stringBuilder = new StringBuilder();
int len;
while((len = socketChannel.read(buffer)) != -1){
if(len == 0){
break;
}
buffer.flip();
stringBuilder.append(Charset.forName("UTF-8").decode(buffer));
}
String message = stringBuilder.toString();
//Channel再次注册
socketChannel.register(selector,SelectionKey.OP_READ);
if(message.length() > 0){
System.out.println(message);
}
}
}
测试
public class ClientA {
public static void main(String[] args) throws IOException {
new Client("无涯子").start();
}
}
public class ClientB {
public static void main(String[] args) throws IOException {
new Client("jack").start();
}
}