/*应用通讯协议
* 发送在线名单:00+所有人&姓名1&姓名2......
* 上线:01+姓名
* 下线:02+姓名
* 向所有人发送消息:03+发送者+消息
* 客户端向某个人发送消息:04+发送对象+发送者+消息 服务端再变成04+发送者+消息 发送给发送对象
*
*/
* 发送在线名单:00+所有人&姓名1&姓名2......
* 上线:01+姓名
* 下线:02+姓名
* 向所有人发送消息:03+发送者+消息
* 客户端向某个人发送消息:04+发送对象+发送者+消息 服务端再变成04+发送者+消息 发送给发送对象
*
*/
由于原定的登录界面(每个用户有唯一的账户)没写故使用了用户名做客户的标志
所以在使用时客户端的姓名不能重复,不然会出错~
发送的字符长度不能超过1024字节
若存在其他bug欢迎提出好改正~谢谢~
服务端代码:
package Server;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.util.*;
import java.util.jar.Attributes.Name;
import javax.swing.text.AbstractDocument.BranchElement;
public class ser
{
//创建一个Selector对象
private Selector selector = null;
//创建一个ServerSocketChannel对象
private ServerSocketChannel serverSocketChannel = null;
//设置端口号
private int port = 8885;
//编码和解码需要的
private Charset charset = Charset.forName("GBK");
//散列表用来存放对应的每个人的姓名和通讯通道
private static Hashtable<String, SocketChannel> hast = new Hashtable<String, SocketChannel>();
//存放在线名单
private static ArrayList<String> allName=new ArrayList<String>();
//构造函数
public ser() throws IOException
{
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
//使得在同一个主机上关闭了服务器程序,紧接着再启动该服务器程序时,可以顺利绑定到相同的端口
serverSocketChannel.socket().setReuseAddress(true);
//使ServerSocketChannel工作于非阻塞模式
serverSocketChannel.configureBlocking(false);
//把服务器进程与一个本地端口绑定
serverSocketChannel.socket().bind(new InetSocketAddress(port));
System.out.println("服务器启动");
}
public void service() throws IOException
{
//添加一个接收连接的事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//当触发事件时
while( selector.select() > 0 )
{
//获得Selector的selected-keys集合
Set<?> readyKeys = selector.selectedKeys();
Iterator<?> it = readyKeys.iterator();
//循环事件的集合
while( it.hasNext() )
{ //
SelectionKey key = null;
try
{ //取出一个SelectionKey
key = (SelectionKey)it.next();
it.remove(); //
if( key.isAcceptable() )
{
//处理接收连接就绪事件;
//获得与SelectionKey关联的ServerSocketChannel
ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
//获得与客户连接的SocketChannel
SocketChannel socketChannel = (SocketChannel)ssc.accept();
System.out.println("接收到客户连接,来自:" + socketChannel.socket().getInetAddress()
+ ":" + socketChannel.socket().getPort());
//把SocketChannel设置为非阻塞模式
socketChannel.configureBlocking(false);
//SocketChannel向Selector注册读就绪事件, 关联了一个buffer附件
socketChannel.register(selector, SelectionKey.OP_READ );
}
if( key.isReadable() )
{
//处理读就绪事件;
receive(key);
}
}
catch( IOException e )
{
//某个和客户端的通讯发送错误就关闭该通道,继续其他通讯
e.printStackTrace();
try
{
if( key != null )
{
key.cancel();
key.channel().close();
}
}
catch( Exception ex )
{
e.printStackTrace();
}
}
}
}
}
//发送消息
public void send(SocketChannel socketChannel, String msg) throws IOException
{
//发送编码后的消息
socketChannel.write(encode(msg));
}
//向某个客户端发送消息
public void sendOne(String name,String msg)throws IOException
{
//获取该用户的通道
SocketChannel soc=hast.get(name);
//向该用户发送消息
send(soc, msg);
}
//向所有人发送消息
public void sendAll(String msg)throws IOException
{
//获取所有的连接通道
Enumeration<SocketChannel> em = hast.elements();
while(em.hasMoreElements())
{//循环发送
SocketChannel soc=(SocketChannel)em.nextElement();
send(soc, msg);
}
}
// 读取数据
public void receive(SelectionKey key) throws IOException
{
SocketChannel socketChannel = (SocketChannel)key.channel();
//创建一个用于存放用户发送来的数据的缓冲区
ByteBuffer readBuff = ByteBuffer.allocate(1024);
if (socketChannel.read(readBuff) >= 0) {
readBuff.flip();
//解码
String msg = decode(readBuff);
readBuff.clear();//清除缓冲区
if(msg.length()>2)
{
//读取前2位的应用通讯协议。这个程序员自己定义
String mark=msg.substring(0,2);
//由于java的switch跟C#不一样不支持string类型所以转换成int类型,
Integer markerInteger=new Integer(mark);
switch(markerInteger)
{
case 1://上线处理
{
//获取上线者姓名
String name =msg.substring(2);
sendAll(msg);
//添加上线用户和通讯通道
hast.put(name, socketChannel);
String getAllName="所有人";
//生成用户名单字符串
for(int i=0;i<allName.size();i++)
{
getAllName+="&";
getAllName+=allName.get(i);
}
//发送给刚上线的用户在线名单
send(socketChannel, "00"+getAllName);
//添加刚上线用户的姓名
allName.add(name);
break;
}
case 2://下线处理
{
String name =msg.substring(2);
//删除该用户的通讯
hast.remove(name);
//从名单去除
allName.remove(name);
//发送消息通知所有用户有人下线
sendAll(msg);
break;
}
case 3://向所有人发送消息
{
sendAll(msg);
break;
}
case 4://向某个人发送消息
{
//获取发送的对象
String sendName=msg.substring(2,12).trim();
msg="04"+msg.substring(12);
sendOne(sendName, msg);
break;
}
}
}
//后台输出。验证使用可以删除
System.out.println(msg);
}
}
public String decode(ByteBuffer buffer)
{ //解码
CharBuffer charBuffer = charset.decode(buffer);
return charBuffer.toString();
}
public ByteBuffer encode(String str)
{ //编码
return charset.encode(str);
}
//主函数入口
public static void main(String args[]) throws Exception
{
ser server = new ser();
server.service();
}
}
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.util.*;
import java.util.jar.Attributes.Name;
import javax.swing.text.AbstractDocument.BranchElement;
public class ser
{
//创建一个Selector对象
private Selector selector = null;
//创建一个ServerSocketChannel对象
private ServerSocketChannel serverSocketChannel = null;
//设置端口号
private int port = 8885;
//编码和解码需要的
private Charset charset = Charset.forName("GBK");
//散列表用来存放对应的每个人的姓名和通讯通道
private static Hashtable<String, SocketChannel> hast = new Hashtable<String, SocketChannel>();
//存放在线名单
private static ArrayList<String> allName=new ArrayList<String>();
//构造函数
public ser() throws IOException
{
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
//使得在同一个主机上关闭了服务器程序,紧接着再启动该服务器程序时,可以顺利绑定到相同的端口
serverSocketChannel.socket().setReuseAddress(true);
//使ServerSocketChannel工作于非阻塞模式
serverSocketChannel.configureBlocking(false);
//把服务器进程与一个本地端口绑定
serverSocketChannel.socket().bind(new InetSocketAddress(port));
System.out.println("服务器启动");
}
public void service() throws IOException
{
//添加一个接收连接的事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//当触发事件时
while( selector.select() > 0 )
{
//获得Selector的selected-keys集合
Set<?> readyKeys = selector.selectedKeys();
Iterator<?> it = readyKeys.iterator();
//循环事件的集合
while( it.hasNext() )
{ //
SelectionKey key = null;
try
{ //取出一个SelectionKey
key = (SelectionKey)it.next();
it.remove(); //
if( key.isAcceptable() )
{
//处理接收连接就绪事件;
//获得与SelectionKey关联的ServerSocketChannel
ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
//获得与客户连接的SocketChannel
SocketChannel socketChannel = (SocketChannel)ssc.accept();
System.out.println("接收到客户连接,来自:" + socketChannel.socket().getInetAddress()
+ ":" + socketChannel.socket().getPort());
//把SocketChannel设置为非阻塞模式
socketChannel.configureBlocking(false);
//SocketChannel向Selector注册读就绪事件, 关联了一个buffer附件
socketChannel.register(selector, SelectionKey.OP_READ );
}
if( key.isReadable() )
{
//处理读就绪事件;
receive(key);
}
}
catch( IOException e )
{
//某个和客户端的通讯发送错误就关闭该通道,继续其他通讯
e.printStackTrace();
try
{
if( key != null )
{
key.cancel();
key.channel().close();
}
}
catch( Exception ex )
{
e.printStackTrace();
}
}
}
}
}
//发送消息
public void send(SocketChannel socketChannel, String msg) throws IOException
{
//发送编码后的消息
socketChannel.write(encode(msg));
}
//向某个客户端发送消息
public void sendOne(String name,String msg)throws IOException
{
//获取该用户的通道
SocketChannel soc=hast.get(name);
//向该用户发送消息
send(soc, msg);
}
//向所有人发送消息
public void sendAll(String msg)throws IOException
{
//获取所有的连接通道
Enumeration<SocketChannel> em = hast.elements();
while(em.hasMoreElements())
{//循环发送
SocketChannel soc=(SocketChannel)em.nextElement();
send(soc, msg);
}
}
// 读取数据
public void receive(SelectionKey key) throws IOException
{
SocketChannel socketChannel = (SocketChannel)key.channel();
//创建一个用于存放用户发送来的数据的缓冲区
ByteBuffer readBuff = ByteBuffer.allocate(1024);
if (socketChannel.read(readBuff) >= 0) {
readBuff.flip();
//解码
String msg = decode(readBuff);
readBuff.clear();//清除缓冲区
if(msg.length()>2)
{
//读取前2位的应用通讯协议。这个程序员自己定义
String mark=msg.substring(0,2);
//由于java的switch跟C#不一样不支持string类型所以转换成int类型,
Integer markerInteger=new Integer(mark);
switch(markerInteger)
{
case 1://上线处理
{
//获取上线者姓名
String name =msg.substring(2);
sendAll(msg);
//添加上线用户和通讯通道
hast.put(name, socketChannel);
String getAllName="所有人";
//生成用户名单字符串
for(int i=0;i<allName.size();i++)
{
getAllName+="&";
getAllName+=allName.get(i);
}
//发送给刚上线的用户在线名单
send(socketChannel, "00"+getAllName);
//添加刚上线用户的姓名
allName.add(name);
break;
}
case 2://下线处理
{
String name =msg.substring(2);
//删除该用户的通讯
hast.remove(name);
//从名单去除
allName.remove(name);
//发送消息通知所有用户有人下线
sendAll(msg);
break;
}
case 3://向所有人发送消息
{
sendAll(msg);
break;
}
case 4://向某个人发送消息
{
//获取发送的对象
String sendName=msg.substring(2,12).trim();
msg="04"+msg.substring(12);
sendOne(sendName, msg);
break;
}
}
}
//后台输出。验证使用可以删除
System.out.println(msg);
}
}
public String decode(ByteBuffer buffer)
{ //解码
CharBuffer charBuffer = charset.decode(buffer);
return charBuffer.toString();
}
public ByteBuffer encode(String str)
{ //编码
return charset.encode(str);
}
//主函数入口
public static void main(String args[]) throws Exception
{
ser server = new ser();
server.service();
}
}
客户端代码:
package Client;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
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.Date;
import java.util.Iterator;
import java.util.Set;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
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.Date;
import java.util.Iterator;
import java.util.Set;
public class ChatClient extends JFrame implements ActionListener,Runnable{
JTextArea jtamsg;
JTextField jtasname,jtaport,jtfsendmsg,jtfname;
JLabel jlsname,jlsport,jlmsg,jlname;
JButton jbcon,jbsend;
JScrollPane jsp;
JComboBox combox;
BufferedReader br;
String ip;
int port;
String name;
String message;
String strcombox;
String time;
Thread thread;
private SocketChannel socketChannel = null;
private Charset charset = Charset.forName("GBK");
private Selector selector;
/**
* 构造方法是实现窗口的界面布局
*/
public ChatClient()
{
jlsname = new JLabel("服务器名");
jlsname.setBounds(new Rectangle(40,25,80,30));
jtasname = new JTextField("192.168.0.20");
jtasname.setBounds(new Rectangle(110,25,80,30));
jlsport = new JLabel("端口");
jlsport.setBounds(new Rectangle(210,25,80,30));
jtaport = new JTextField("8885");
jtaport.setBounds(new Rectangle(240,25,50,30));
jlname = new JLabel("姓名");
jlname.setBounds(300,25,50,30);
jtfname = new JTextField();
jtfname.setBounds(340,25,80,30);
jbcon = new JButton("连接");
jbcon.setBounds(new Rectangle(440,25,80,30));
jlmsg = new JLabel("输入信息");
jlmsg.setBounds(new Rectangle(40,65,80,30));
jtfsendmsg = new JTextField();
jtfsendmsg.setBounds(new Rectangle(110,65,210,30));
combox = new JComboBox();
combox.setBounds(new Rectangle(340,65,80,30));
combox.addItem("所有人");
jbsend = new JButton("发送");
jbsend.setBounds(new Rectangle(440,65,80,30));
jtamsg = new JTextArea();
jtamsg.setEditable(false);
jsp = new JScrollPane(jtamsg);
jsp.setBounds(new Rectangle(40,105,480,240));
jbcon.addActionListener(this);
jbsend.addActionListener(this);
this.setLayout(null);
this.add(jlsname);
this.add(jtasname);
this.add(jlsport);
this.add(jtaport);
this.add(jbcon);
this.add(jlname);
this.add(jtfname);
this.add(jlmsg);
this.add(jtfsendmsg);
this.add(combox);
this.add(jbsend);
this.add(jsp);
this.setSize(550, 400);
this.setResizable(false);
this.setVisible(true);
//添加关闭窗口事件处理,发送下线消息
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
if(socketChannel == null)
System.exit(0);
else
{
try {
//下线
down();
}
catch (Exception e2) {
// TODO: handle exception
}
finally
{
//关闭窗口
System.exit(0);
}
}
}
});
}
/**
* 事件处理函数
* 有连接和发送两个事件
* 连接:判断基本信息是否填写好;
* 如果填写好,则调用connect()函数建立连接
* 接着禁用连接按钮及相关编辑框
* 发送:判断是对个人说还是对所有人说
* 根据对象的不同对message进行加工,
* 最后都是通过send()函数发送消息
*/
public void actionPerformed(ActionEvent e)
{
//连接事件处理
if(e.getSource() == jbcon)
{
name = jtfname.getText().trim();
ip = jtasname.getText().trim();
this.setTitle("客户端:"+name);
port = Integer.parseInt(jtaport.getText().trim());
if(!name.equals("")&&!ip.equals("")&&!jtaport.getText().trim().equals(""))
{
//姓名限制在10个字符内
if(name.length()>10)
{
JOptionPane.showMessageDialog(null, "名字在10个字符以内" +
"请谅解!","提示",JOptionPane.INFORMATION_MESSAGE);
}
else {
try {
//连接服务器
connect(ip,port);
jtasname.setEditable(false);
jtaport.setEditable(false);
jtfname.setEditable(false);
jbcon.setEnabled(false);
} catch (Exception e1) {
}
}
}
else JOptionPane.showMessageDialog(null, "请填写基本信息","提示",JOptionPane.INFORMATION_MESSAGE);
JTextField jtasname,jtaport,jtfsendmsg,jtfname;
JLabel jlsname,jlsport,jlmsg,jlname;
JButton jbcon,jbsend;
JScrollPane jsp;
JComboBox combox;
BufferedReader br;
String ip;
int port;
String name;
String message;
String strcombox;
String time;
Thread thread;
private SocketChannel socketChannel = null;
private Charset charset = Charset.forName("GBK");
private Selector selector;
/**
* 构造方法是实现窗口的界面布局
*/
public ChatClient()
{
jlsname = new JLabel("服务器名");
jlsname.setBounds(new Rectangle(40,25,80,30));
jtasname = new JTextField("192.168.0.20");
jtasname.setBounds(new Rectangle(110,25,80,30));
jlsport = new JLabel("端口");
jlsport.setBounds(new Rectangle(210,25,80,30));
jtaport = new JTextField("8885");
jtaport.setBounds(new Rectangle(240,25,50,30));
jlname = new JLabel("姓名");
jlname.setBounds(300,25,50,30);
jtfname = new JTextField();
jtfname.setBounds(340,25,80,30);
jbcon = new JButton("连接");
jbcon.setBounds(new Rectangle(440,25,80,30));
jlmsg = new JLabel("输入信息");
jlmsg.setBounds(new Rectangle(40,65,80,30));
jtfsendmsg = new JTextField();
jtfsendmsg.setBounds(new Rectangle(110,65,210,30));
combox = new JComboBox();
combox.setBounds(new Rectangle(340,65,80,30));
combox.addItem("所有人");
jbsend = new JButton("发送");
jbsend.setBounds(new Rectangle(440,65,80,30));
jtamsg = new JTextArea();
jtamsg.setEditable(false);
jsp = new JScrollPane(jtamsg);
jsp.setBounds(new Rectangle(40,105,480,240));
jbcon.addActionListener(this);
jbsend.addActionListener(this);
this.setLayout(null);
this.add(jlsname);
this.add(jtasname);
this.add(jlsport);
this.add(jtaport);
this.add(jbcon);
this.add(jlname);
this.add(jtfname);
this.add(jlmsg);
this.add(jtfsendmsg);
this.add(combox);
this.add(jbsend);
this.add(jsp);
this.setSize(550, 400);
this.setResizable(false);
this.setVisible(true);
//添加关闭窗口事件处理,发送下线消息
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
if(socketChannel == null)
System.exit(0);
else
{
try {
//下线
down();
}
catch (Exception e2) {
// TODO: handle exception
}
finally
{
//关闭窗口
System.exit(0);
}
}
}
});
}
/**
* 事件处理函数
* 有连接和发送两个事件
* 连接:判断基本信息是否填写好;
* 如果填写好,则调用connect()函数建立连接
* 接着禁用连接按钮及相关编辑框
* 发送:判断是对个人说还是对所有人说
* 根据对象的不同对message进行加工,
* 最后都是通过send()函数发送消息
*/
public void actionPerformed(ActionEvent e)
{
//连接事件处理
if(e.getSource() == jbcon)
{
name = jtfname.getText().trim();
ip = jtasname.getText().trim();
this.setTitle("客户端:"+name);
port = Integer.parseInt(jtaport.getText().trim());
if(!name.equals("")&&!ip.equals("")&&!jtaport.getText().trim().equals(""))
{
//姓名限制在10个字符内
if(name.length()>10)
{
JOptionPane.showMessageDialog(null, "名字在10个字符以内" +
"请谅解!","提示",JOptionPane.INFORMATION_MESSAGE);
}
else {
try {
//连接服务器
connect(ip,port);
jtasname.setEditable(false);
jtaport.setEditable(false);
jtfname.setEditable(false);
jbcon.setEnabled(false);
} catch (Exception e1) {
}
}
}
else JOptionPane.showMessageDialog(null, "请填写基本信息","提示",JOptionPane.INFORMATION_MESSAGE);
}
//发送事件处理
if(e.getSource() == jbsend)
{
try
{
if(!jtfsendmsg.getText().trim().equals(""))
{
message = jtfsendmsg.getText();
strcombox = (String) combox.getSelectedItem();
if(strcombox.equals("所有人"))
{
send("03"+makeName(name)+message);
//jtamsg.append("你 对 所有人 说:\n"+jtfsendmsg.getText()+"\n");
}
else
{
showTime();
jtamsg.append("你 对 "+strcombox+" 说:\n"+message+"\n");
message = "04"+makeName(strcombox)+ makeName(name)+message;
send(message);
}
jtfsendmsg.setText("");
}
else
JOptionPane.showMessageDialog(null, "发送内容不能为空,请重新发送","提示",JOptionPane.WARNING_MESSAGE);
}
catch(IOException ex)
{}
}
}
private String makeName(String name)
{//将姓名变成长度为10的字符串
String setName=name;
while(setName.length()<10)
setName+=" ";
return setName;
}
/**
* 函数实现了连接指定服务器的功能
* @param ip 服务器IP
* @param port 服务器端口号
* @throws Exception 异常处理
*/
public void connect(String ip,int port) throws Exception
{
socketChannel = SocketChannel.open();
InetSocketAddress isa = new InetSocketAddress(ip, port);
socketChannel.connect(isa); //采用阻塞模式连接服务器
socketChannel.configureBlocking(false); //设置为非阻塞模式
send(jtfname.getText());
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
jtamsg.append("连接成功"+"\n");
send("01"+name);
thread = new Thread(this);
//发送事件处理
if(e.getSource() == jbsend)
{
try
{
if(!jtfsendmsg.getText().trim().equals(""))
{
message = jtfsendmsg.getText();
strcombox = (String) combox.getSelectedItem();
if(strcombox.equals("所有人"))
{
send("03"+makeName(name)+message);
//jtamsg.append("你 对 所有人 说:\n"+jtfsendmsg.getText()+"\n");
}
else
{
showTime();
jtamsg.append("你 对 "+strcombox+" 说:\n"+message+"\n");
message = "04"+makeName(strcombox)+ makeName(name)+message;
send(message);
}
jtfsendmsg.setText("");
}
else
JOptionPane.showMessageDialog(null, "发送内容不能为空,请重新发送","提示",JOptionPane.WARNING_MESSAGE);
}
catch(IOException ex)
{}
}
}
private String makeName(String name)
{//将姓名变成长度为10的字符串
String setName=name;
while(setName.length()<10)
setName+=" ";
return setName;
}
/**
* 函数实现了连接指定服务器的功能
* @param ip 服务器IP
* @param port 服务器端口号
* @throws Exception 异常处理
*/
public void connect(String ip,int port) throws Exception
{
socketChannel = SocketChannel.open();
InetSocketAddress isa = new InetSocketAddress(ip, port);
socketChannel.connect(isa); //采用阻塞模式连接服务器
socketChannel.configureBlocking(false); //设置为非阻塞模式
send(jtfname.getText());
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
jtamsg.append("连接成功"+"\n");
send("01"+name);
thread = new Thread(this);
//启动另外一个线程监视通信事件
thread.start();
}
/**
* 下线通知函数
*/
public void down()throws IOException
{
send("02"+name);
socketChannel.close();
}
thread.start();
}
/**
* 下线通知函数
*/
public void down()throws IOException
{
send("02"+name);
socketChannel.close();
}
/**
* 发送消息函数
* 将消息写入数据输出流中输出
* @param message 要发送的消息
* @throws IOException
*/
public void send(String msg) throws IOException
{
socketChannel.write(charset.encode(msg));
}
/**
* 函数实现了消息的接受和显示
* 首先创建也Socket绑定的输入流
* 其次建立相应的数据输入流,并读取消息
* 消息分为3类,一类是用户在线情况通知,以“&”区分
* 一类是用户下线通知,以“下线”区分
* 最后一类是聊天的消息
* 将着三类消息进行处理
*/
public void run()
{
try {
while (true)
{
selector.select();
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while (iter.hasNext())
{
SelectionKey key = iter.next();
iter.remove();
if (key.isReadable())
{
receive(key);
}
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
public void sendd(SelectionKey key) throws IOException
{
ByteBuffer buffer = (ByteBuffer)key.attachment();
SocketChannel socketChannel = (SocketChannel)key.channel();
buffer.flip(); //把极限设为位置,把位置设为0
socketChannel.write(buffer);
// System.out.print(data);
}
public void receive(SelectionKey key) throws IOException
{
SocketChannel socketChannel2 = (SocketChannel)key.channel();
ByteBuffer readBuff = ByteBuffer.allocate(1024);
if (socketChannel2.read(readBuff) >= 0) {
readBuff.flip();
{
SocketChannel socketChannel2 = (SocketChannel)key.channel();
ByteBuffer readBuff = ByteBuffer.allocate(1024);
if (socketChannel2.read(readBuff) >= 0) {
readBuff.flip();
String msg = decode(readBuff);
readBuff.clear();
String mark=msg.substring(0,2);
Integer intmark=new Integer(mark);
switch(intmark)
{
case 0:
{
//接收所有在线名单
String allNameString=msg.substring(2);
String []allName=allNameString.split("&");
combox.removeAllItems();
for(int i=0;i<allName.length;i++)
{
combox.addItem(allName[i]);
}
break;
}
case 1:
{
String theName =msg.substring(2).trim();
combox.addItem(theName);
showTime();
jtamsg.append(theName+" 上线了\n");
break;
}
case 2:
{
String theName =msg.substring(2).trim();
combox.removeItem(theName);
showTime();
jtamsg.append(theName+" 下线了\n");
break;
}
case 3:
{
//接收群发消息
String sender=msg.substring(2,12).trim();
showTime();
jtamsg.append(sender+" 对所有人说:\n");
jtamsg.append(msg.substring(12));
break;
}
case 4:
{
//接收某人消息
String theSendName =msg.substring(2,12).trim();
String theMsg=msg.substring(12);
showTime();
jtamsg.append(theSendName+" 对你说:\n");
jtamsg.append(theMsg);
break;
}
}
}
}
public String decode(ByteBuffer buffer)
{ //解码
CharBuffer charBuffer = charset.decode(buffer);
return charBuffer.toString();
}
public ByteBuffer encode(String str)
{ //编码
return charset.encode(str);
}
/**
* 显示时间函数,目的是压缩代码
*/
public void showTime()
{
Date date = new Date();
time = date.toLocaleString();
jtamsg.append(time+"\n");
}
public static void main(String arg[])
{
new ChatClient();
}
}
下面是简单的流程图