java socket群聊_java网络socket编程(七)之java中NIO实现聊天系统的群聊功能

本文介绍如何使用Java的非阻塞I/O(NIO)创建一个聊天室服务器,支持多个客户端连接。服务器端代码通过Selector监听客户端连接,处理客户端的读写请求。客户端代码通过SocketChannel与服务器交互,发送昵称并接收服务器消息。当有新的客户端加入时,服务器会通知所有在线用户。
摘要由CSDN通过智能技术生成

1.服务器端代码

import java.io.IOException;

import java.net.InetSocketAddress;

import java.net.ServerSocket;

import java.nio.ByteBuffer;

import java.nio.channels.Channel;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.ServerSocketChannel;

import java.nio.channels.SocketChannel;

import java.nio.charset.Charset;

import java.util.HashSet;

import java.util.Iterator;

import java.util.Set;

/**

* 聊天室服务器

* 网络多客户端聊天室

* 功能1: 客户端经过Java NIO链接到服务端,支持多客户端的链接

* 功能2:客户端初次链接时,服务端提示输入昵称,若是昵称已经有人使用,提示从新输入,若是昵称惟一,则登陆成功,以后发送消息都须要按照规定格式带着昵称发送消息

* 功能3:客户端登陆后,发送已经设置好的欢迎信息和在线人数给客户端,而且通知其余客户端该客户端上线

* 功能4:服务器收到已登陆客户端输入内容,转发至其余登陆客户端。

* @author 1

* @date Aug 22, 2016 4:02:01 PM

*/

public class ChatRoomServer {

/** 选择器 */

private Selector selector;

/*****端口号*****/

private final static int PORT=9900;

/*******在线统计人名或人数********/

private HashSet online = new HashSet();

/****编码*****/

private Charset charset = Charset.forName("UTF-8");

/****用户存在提示信息*****/

private static String USER_EXIST = "system message: user exist, please change a name";

/****至关于自定义协议格式,与客户端协商好*****/

private static String USER_CONTENT_SPILIT = "#@#";

public static void main(String[] args) {

try {

new ChatRoomServer().init();

} catch (IOException e) {

e.printStackTrace();

}

}

/**

* 初始化服务器

* @author 1

* @throws IOException

*/

public void init() throws IOException{

//打开选择器

this.selector = Selector.open();

// 开启服务器端通道,并指定端口号

ServerSocketChannel server = ServerSocketChannel.open();

ServerSocket serverSocket = server.socket();

InetSocketAddress address = new InetSocketAddress(PORT);

serverSocket.bind(address);

server.configureBlocking(false);

//将选择器注册到服务器通道上

server.register(selector, SelectionKey.OP_ACCEPT);

System.out.println("server is linstening...");

//等待客户端的链接

while(true){

int nums = this.selector.select();

if (nums<=0) {

continue;

}

//存在链接

Set selectionKeys = this.selector.selectedKeys();

Iterator iterator = selectionKeys.iterator();

while (iterator.hasNext()) {

//获得当前的选择键

SelectionKey key = iterator.next();

iterator.remove();

//处理当前的选择键

dealWithSelectionKey(server,key);

}

}

}

/**

*

* @param server

* @param key

* @author 1

* @throws IOException

*/

private void dealWithSelectionKey(ServerSocketChannel server, SelectionKey key) throws IOException {

if (key.isAcceptable()) {

//接收客户端

SocketChannel sChannel = server.accept();

//设置非阻塞

sChannel.configureBlocking(false);

//注册选择器,并设置为读取模式,收到一个链接请求,而后起一个SocketChannel,并注册到selector上,以后这个链接的数据,就由这个socketchannel处理。

sChannel.register(selector, SelectionKey.OP_READ);

//将此对应的channel设置为准备接收其余客户端的请求

key.interestOps(SelectionKey.OP_ACCEPT);

System.out.println("Server is listening from client :" + sChannel.socket().getRemoteSocketAddress());

sChannel.write(charset.encode("Please input your name:"));

}

//处理来自客户端的数据读取请求

else if (key.isReadable()) {

//获得该key对应的channel,其中有数据须要读取

SocketChannel sc = (SocketChannel) key.channel();

StringBuffer content = new StringBuffer();

ByteBuffer buffer = ByteBuffer.allocate(1024);

try {

//获得客户端传过来的消息

while(sc.read(buffer)>0){

buffer.flip();

content.append(charset.decode(buffer));

}

//将此对应的channel设置为准备下一次接受数据

key.interestOps(SelectionKey.OP_READ);

} catch (Exception e) {

e.printStackTrace();

key.cancel();

sc.close();

}

//若是内容不为空

if (content.length()>0) {

//拆分规则

String[] msgArr = content.toString().split(USER_CONTENT_SPILIT);

//注册名字

if (msgArr!=null && msgArr.length==1) {

//用户已经存在,则直接返回

if (online.contains(msgArr[0])) {

sc.write(charset.encode(USER_EXIST));

}else {

String name = msgArr[0];

online.add(name);

int onlineNum = this.onlineTotal();

String msg = "welcome "+name+" to chat room,current online people num is:"+onlineNum;

//通知全部的人

broadCast(selector, null, msg);

}

}

//聊天内容

else if (msgArr!=null && msgArr.length>1) {

String name = msgArr[0];

String message = content.substring(name.length()+USER_CONTENT_SPILIT.length());

message = name + " say " + message;

if(online.contains(name)) {

//不回发给发送此内容的客户端

broadCast(selector, sc, message);

}

}

}

}

}

/**

* 通知全部人

* @param selector 选择器

* @param sc 不通知的客户端

* @param msg 消息

* @author 1

* @throws IOException

*/

private void broadCast(Selector selector, SocketChannel except, String msg) throws IOException {

for(SelectionKey key : selector.keys()){

Channel channel = key.channel();

if (channel instanceof SocketChannel && channel != except) {

SocketChannel socketChannel = (SocketChannel) channel;

socketChannel.write(charset.encode(msg));

}

}

}

/**

* 获得在线总人数

* @return

* @author 1

*/

private int onlineTotal() {

int num=0;

for(SelectionKey key : this.selector.keys()){

Channel targetchannel = key.channel();

if (targetchannel instanceof SocketChannel) {

num++;

}

}

return num;

}

}

2.客户端代码

package com.hq.io.socket;

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.Charset;

import java.util.Iterator;

import java.util.Scanner;

import java.util.Set;

/**

*

* @author 1

* @date Aug 22, 2016 4:02:01 PM

*/

public class ChatRoomClient {

private Selector selector = null;

static final int port = 9900;

private Charset charset = Charset.forName("UTF-8");

private SocketChannel sc = null;

private String name = "";

private static String USER_EXIST = "system message: user exist, please change a name";

private static String USER_CONTENT_SPILIT = "#@#";

public void init() throws IOException {

selector = Selector.open();

// 链接远程主机的IP和端口

sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", port));

sc.configureBlocking(false);

sc.register(selector, SelectionKey.OP_READ);

// 开辟一个新线程来读取从服务器端的数据

new Thread(new ClientThread()).start();

// 在主线程中 从键盘读取数据输入到服务器端

Scanner scan = new Scanner(System.in);

while (scan.hasNextLine()) {

String line = scan.nextLine();

if ("".equals(line))

continue; // 不容许发空消息

if ("".equals(name)) {

name = line;

line = name + USER_CONTENT_SPILIT;

} else {

line = name + USER_CONTENT_SPILIT + line;

}

sc.write(charset.encode(line));// sc既能写也能读,这边是写

}

}

private class ClientThread implements Runnable {

public void run() {

try {

while (true) {

int readyChannels = selector.select();

if (readyChannels == 0)

continue;

Set selectedKeys = selector.selectedKeys(); // 能够经过这个方法,知道可用通道的集合

Iterator keyIterator = selectedKeys.iterator();

while (keyIterator.hasNext()) {

SelectionKey sk = (SelectionKey) keyIterator.next();

keyIterator.remove();

dealWithSelectionKey(sk);

}

}

} catch (IOException io) {

}

}

private void dealWithSelectionKey(SelectionKey sk) throws IOException {

if (sk.isReadable()) {

// 使用 NIO 读取 Channel中的数据,这个和全局变量sc是同样的,由于只注册了一个SocketChannel

// sc既能写也能读,这边是读

SocketChannel sc = (SocketChannel) sk.channel();

ByteBuffer buff = ByteBuffer.allocate(1024);

String content = "";

while (sc.read(buff) > 0) {

buff.flip();

content += charset.decode(buff);

}

// 若系统发送通知名字已经存在,则须要换个昵称

if (USER_EXIST.equals(content)) {

name = "";

}

System.out.println(content);

sk.interestOps(SelectionKey.OP_READ);

}

}

}

public static void main(String[] args) throws IOException {

new ChatRoomClient().init();

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值