鄙人以MyEclipse为平台实现简单的聊天程序
1、创建一个Server类模块:创建ServerSocket,在指定端口监听;当有用户登录, 接受套接字输入流,显示其登录信息,调用ServerHander线程进行管理。
代码实现:
public class Server {
private ServerSocket server;
public Server(int port) throws IOException{
server = new ServerSocket(port); // new一个实例对象端口
while(true){
Socket conn=server.accept(); //建立客户端套接字
DataInputStream in =new DataInputStream(conn.getInputStream());
String who=in.readUTF();
System.out.print("Client" +"(ip:"+conn.getInetAddress()+")"+who+" 进入!"+"\n");
ServerHander cn=new ServerHander(who,conn);
cn.start();
}
}
public static void main(String []args)throws IOException{
new Server(9595);
}
}//Server类模块结束
2、创建ServerHander模块:这个模块是整个服务器实现与客户端交互的核心代码,互相添加或者解密关键字,实现群聊、私聊上下线提醒的功能,创建Vector 对象,可以方便用户管理,Vector对象是可变数组,可以实现线程同步。用户发送消息通过关键字@进行解析 ,@前面的字符串为发送的消息,后面的字符串为发送对象。
2.1创建线程
Socket socket;
DataInputStream in;
DataOutputStream out;
String who;
protected static Vectoruser=new Vector();//把用户线程放入可变对象数组
public ServerHander(String name,Socket socket) throws IOException{
this.who = name;
this.socket = socket;
in =new DataInputStream(new BufferedInputStream(socket.getInputStream()));
out =new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
}
public void run(){
try{
user.addElement(this); //Vector中增加新线程
sendallclient(who+"上线");
senduser();//为每一个客户端更新在线用户
while(true){
String news=in.readUTF();
int index = news.lastIndexOf("@");//对客户 端发过来的信息做相应的处理
String str = news.substring(index+1,news.length());//取得发送对象和发送的消息
news=news.substring(0,index);
if(str.equals("所有人")==false){
sendallclient(who+"对"+str+"说:"+news+"@"); //不是发给所有人则增添标记@
}
else
sendallclient(who+"对所有人说: "+news);
}
}
catch(IOException e){
System.out.println("error");
}
finally{
user.removeElement(this);
System.out.print("Client" +"(ip:"+socket.getInetAddress()+")"+who+" 退出!欢迎下次登录"+"\n");
sendallclient(who+"下线");
try{
socket.close();
}
catch(IOException e){
System.out.println("closing");
}
}
}
2.2、创建send all client类模块: 把news消息发送给每一个用户。其中AllClients是 ServerHander的枚举类型,可以指向每一个用户。
protected static void sendallclient(String news){
synchronized (user){
Enumeration AllClients=user.elements();
while(AllClients.hasMoreElements()){
ServerHander serh=(ServerHander)AllClients.nextElement();
try{
serh.out.writeUTF(news);
serh.out.flush();
}catch(IOException e){
serh.interrupt();
}
}
}
} //Send all client类模块结束
2.3、创建Send user类模块:更新用户列表 ;实现方法是在名字信息后面加上“$”关键字,达到实现新用户获得除自己外的所有用户名单,而以前的所有用户都在聊天名单中加入这个新用户。
public static synchronized void senduser(){
for(int j=0;j
3、创建Client类:实现每个客户登录操作
public class Client {
public ServiceFrame sf;
private Socket csocket;
private DataInputStream in;
private DataOutputStream out;
public static void main(String []args){
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入登录用户名: ");
String who=" ";
try{
who=in.readLine().trim();//控制台获得名字
}
catch(IOException e){
e.printStackTrace();
}
new Client(who,"127.0.0.1",9595);
}
public Client(String who,String server,int port){
sf=new ServiceFrame(who+" 的客户端");
String str1 = null;
sf.but.addActionListener(new ActListener(this,sf));
sf.addWindowListener(new ExitListener(this));
try{ csocket =new Socket(server,port);
in=new DataInputStream(new BufferedInputStream(csocket.getInputStream()));
out=new DataOutputStream(new BufferedOutputStream(csocket.getOutputStream()));
out.writeUTF(who);
out.flush();//名字发给服务器
while(true){
str1 = in.readUTF();//重点!!!,服务器端发来的消息
if(str1.endsWith("$"))//若以$结尾,则增添用户
{int index = str1.lastIndexOf("$");
String str2 = str1.substring(0,index);
sf.friendlist.addItem(str2.trim());
}
else if(str1.endsWith("@"))//以@结尾为私聊信息
{str1=str1.substring(0,str1.length()-1);
if(str1.contains(who))
sf.showAT.append("-- "+str1+"\n");
}else{
if(str1.endsWith("下线"))//用户下线,删除此用户
{ int index = str1.lastIndexOf("下线");
String str2 = str1.substring(0,index);
sf.friendlist.removeItem(str2.trim());}
sf.showAT.append("-- "+str1+"\n");
}sf.showCount.setText("在线人 数:"+(sf.friendlist.getItemCount()-1)); //动态更新在线人数
}
}catch(Exception e){
System.out.println("Server Error");
this.close(); System.exit(0);
}
}
protected void send(String msg){//发送消息给服务器的方法
try{
out.writeUTF(msg);
out.flush();
}catch(Exception e){ }
}
protected void close(){
try{
sf.dispose();
out.close();
in.close();
csocket.close();
}catch(IOException ex){}
}
}
4、创建服务器的ServiceFrame类
class ServiceFrame extends Frame{
private static final long serialVersionUID = 1L;
JTextArea showAT;
JTextField sendFD;
JComboBox friendlist;
JButton but;
JLabel showCount;
JScrollPane textAreaScrollPane;
JPanel textFieldPanel = new JPanel();
@SuppressWarnings("deprecation")
public ServiceFrame (String winname){
super(winname);//继承父类的名字
setSize(500,400);
textFieldPanel.setLayout(new FlowLayout(0,10,10));
showAT = new JTextArea(400,400);
showAT.setEditable(false);
textAreaScrollPane = new JScrollPane(showAT);
add(textAreaScrollPane, BorderLayout.CENTER);
add(textFieldPanel, BorderLayout.SOUTH);
friendlist = new JComboBox();
friendlist.addItem("所有人");
textFieldPanel.add(friendlist);
sendFD = new JTextField(20);
textFieldPanel.add(sendFD);
but = new JButton("发送");
but.setMnemonic(KeyEvent.VK_ENTER);
textFieldPanel.add(but);
showCount = new JLabel("在线人数:1");
textFieldPanel.add(showCount);
show();
}
}
5、设置客户端信息发送监听器 ActListener。
class ActListener implements ActionListener {
Client client;
ServiceFrame sframe;
public ActListener(Client c,ServiceFrame sf){
client=c; sframe=sf;
}
public void actionPerformed(ActionEvent e){//发送信息,并以@作为私聊标记
client.send(sframe.sendFD.getText().trim()+"@"+sframe.friendlist.getSelectedItem());
sframe.sendFD.setText(" ");
}
}
6、设置客户端用户退出监听器 ExitListener。
class ExitListener extends WindowAdapter{
Client client;
public ExitListener(Client c){
client=c;
}
public void windowClosing(WindowEvent e){
client.close();
System.exit(0);
}
}
开始聊天: