以下资料是从别处直接Copy的,为了方便查找使用,放到“毕设手记”里了
一个简单的使用UDP协议实现的联天室,附件中有Eclipse3.4调试的project。
由两个包组成:server 和 client
server包里有如下两个类:
package server; import java.awt.Container; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.Date; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; public class ServerFrame extends JFrame{ private JTextArea allmsg; private JTextField currnum,totalnum,copyright,chatmsg; private JButton send; private JScrollPane js; int num1,num2,port;/* -- num1:当前在线人数 num2:总上线人数 port:服务端口号 --*/ private ServerSocket ss; ArrayList lists;//存放所有在线用户 public ServerFrame() { super("聊天室服务器端"); this.setSize(310,660); this.setLocation(200,50); lists = new ArrayList(); num1 = num2 =0; port = 7777; currnum = new JTextField(" 当前在线人数: "+num1); currnum.setEnabled(false); totalnum = new JTextField(" 上线总人数: "+num2); totalnum.setEnabled(false); copyright = new JTextField(" ----- all copyright @ TOP-king -----"); copyright.setEnabled(false); allmsg = new JTextArea(); allmsg.append(" --------------- 系统消息 --------------\n"); allmsg.setEditable(false); allmsg.setLineWrap(true); //允许自动换行 js = new JScrollPane(allmsg);//为JTextArea添加滚动条 chatmsg = new JTextField("在此输入系统信息"); chatmsg.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { String str = chatmsg.getText().trim(); if(!"".equals(str)) {sendmsg((new Date()).toLocaleString()+" -- 系统消息: "+str);chatmsg.setText("");} else JOptionPane.showMessageDialog(null, "消息不能为空","错误",JOptionPane.OK_OPTION); chatmsg.setText("");/* -- 发送信息后,将输入栏中的信息清空 -- */ } }); send = new JButton("发送"); send.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { String str = chatmsg.getText().trim(); if(!"".equals(str)) {sendmsg((new Date()).toLocaleString()+" -- 系统消息: "+str);chatmsg.setText("");} else JOptionPane.showMessageDialog(null, "消息不能为空","错误",JOptionPane.OK_OPTION); chatmsg.setText("");/* -- 发送信息后,将输入栏中的信息清空 -- */ } }); addcomponettocontainer(); this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent we) { sendmsg("SYSTEM_CLOSED");/* -- 向客户端发送服务器关闭信息 -- */ destory(); } }); start(); /* -- 启动连接服务 -- */ } public void addcomponettocontainer() { Container c = this.getContentPane(); c.setLayout(null); currnum.setBounds(20,15,130,20); totalnum.setBounds(155,15,125,20); js.setBounds(10,50,280,500); chatmsg.setBounds(10,560,180,30); send.setBounds(220,560,70,30); copyright.setBounds(10,600,280,20); c.add(currnum); c.add(totalnum); c.add(js); c.add(chatmsg); c.add(send); c.add(copyright); this.setVisible(true); this.setResizable(false); } /** * start()方法监听客户的连接 * 并且保存客户端的相关信息EG:用户昵称、用户所使用的Socket * 用户连接到服务器成功之后,将其保存到用户列表中,并为该用户启动一个线程用于通信 */ public void start() { boolean isStarted = false;/* -- 用于标记服务器是否已经正常启动 -- */ try { this.ss = new ServerSocket(port); isStarted = true; this.allmsg.append((new Date()).toLocaleString()+" 服务器启动 @ 端口: "+port+"\n"); while(isStarted) { Socket client = this.ss.accept(); /* -- 监听客户端的连接 -- */ DataInputStream in = new DataInputStream(client.getInputStream()); String name = in.readUTF(); user u = new user(); u.name = name; u.socket = client; lists.add(u); //将该用户加到列表中去 num1++; num2++; currnum.setText(" 当前在线人数: "+num1); totalnum.setText(" 上线总人数: "+num2); this.allmsg.append((new Date()).toLocaleString()+" : "+u.name+" 登录 \n"); new Thread(new ClientThread(u)).start();/* -- 为该用户启动一个通信线程 -- */ } } catch (IOException e) { System.out.println("服务器已经启动......"); System.exit(0); } } /** * 通信线程主要功能包括: * 1.监听客户端输入的信息 * 2.将接收到的信息转发给其他用户 */ class ClientThread implements Runnable { user user = null; boolean isConnected = true; DataInputStream dis = null; DataOutputStream dos = null; public ClientThread(user u) { this.user = u; try { this.dis = new DataInputStream(this.user.socket.getInputStream()); this.dos = new DataOutputStream(this.user.socket.getOutputStream()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void run() { readmsg(); } /* -- 读取客户的聊天信息 -- */ public void readmsg() { while(isConnected) { try { String msg = dis.readUTF(); if("quit&logout".equals(msg))//当用户关闭客户端窗口时,发送quit字符串 表示用户已经退出 { num1--; try{ this.dis.close(); this.dos.close(); this.user.socket.close(); this.isConnected = false; }catch(IOException ioe) { ioe.printStackTrace(); }finally{ this.isConnected = false; if(dis!=null) this.dis.close(); if(dos!=null) this.dos.close(); if(this.user.socket!=null) this.user.socket.close(); } lists.remove(this.user);//从列表中删除该用户 currnum.setText(" 当前在线人数: "+num1); allmsg.append((new Date()).toLocaleString()+" : "+this.user.name+" 退出\n"); } else sendmsg(msg); } catch (IOException e) { e.printStackTrace(); } } } } /* -- 将信息进行转发 -- */ public void sendmsg(String msg) { user us = new user(); DataOutputStream os = null; if(lists.size()>0) { for(int i=0;i
package server; import java.net.*; public class user { String name; Socket socket; }
client包里有如下两个类:
1.
package client; import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.io.*; import java.net.*; public class LoginFrame extends JFrame{ private JTextField name; private JTextField ip; private JButton ok,cancle; public Socket socket; public LoginFrame() { super("登录框"); this.setSize(400,80); this.setLocation(100,100); name = new JTextField("昵称"); ip = new JTextField("127.0.0.1"); ok = new JButton("登录"); cancle = new JButton("取消"); ok.addActionListener(new listenEvent()); cancle.addActionListener(new listenEvent()); addcomponettocontainer(); this.setDefaultCloseOperation(EXIT_ON_CLOSE); } public void addcomponettocontainer() { Container c = this.getContentPane(); c.setLayout(null); name.setBounds(10,10,100,30); ip.setBounds(120,10,100,30); ok.setBounds(230,10,70,30); cancle.setBounds(310,10,70,30); c.add(name); c.add(ip); c.add(ok); c.add(cancle); this.setVisible(true); this.setResizable(false); } public class listenEvent implements ActionListener { public void actionPerformed(ActionEvent event) { // TODO Auto-generated method stub if(event.getSource()==ok) { String n = name.getText().trim(); String i = ip.getText().trim(); if("".equals(n)||"".equals(i)) { JOptionPane.showMessageDialog(null,"昵称、IP不能够为空!","错误",JOptionPane.OK_OPTION); } else{login(n,i);} } if(event.getSource()==cancle) { name.setText(""); ip.setText(""); } } } public void login(String name,String ip) { try { socket = new Socket(ip,7777); DataOutputStream out = new DataOutputStream(socket.getOutputStream()); out.writeUTF(name); out.flush();//强制输出缓存中的内容 //out.close(); new ClientFrame(name,socket); destroywindow(); } catch (UnknownHostException e) { JOptionPane.showMessageDialog(null,"找不到主机地址(IP错误/网络故障)!","错误",JOptionPane.OK_OPTION); } catch (IOException e) { } } public void destroywindow() { this.dispose(); } public static void main(String[] args) { new LoginFrame(); } }
package client; import java.awt.Color; import java.awt.Container; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.util.Date; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; public class ClientFrame extends JFrame{ private JTextArea allmsg; private JTextField welcome,copyright,chatmsg; private JButton send; private JScrollPane js; private boolean isConnected = true; public DataOutputStream out; public DataInputStream in; public Socket s = null; String nic; /* -- 保存用户昵称 --*/ /** * 初始化客户端资源 * 1.获取从LoginFrame传递过来的参数 * 2.初始化界面元素 * 3.初始化通信所需要的资源 EG:输入/输出流(DataInputStream/DataOutputStream) * */ public ClientFrame(String name,Socket socket) { this.setSize(310,660); this.setLocation(290,50); this.setTitle("聊天室客户端<"+name+">");/* -- 指定窗口的标题 --*/ this.s = socket;/* -- 接收从LoginFrame中传递过来的Socket --*/ this.nic = name+" 说: "; welcome = new JTextField(" < "+name+" >欢迎您来到聊天室 ",100); welcome.setBackground(Color.blue); welcome.setEnabled(false); copyright = new JTextField(" ----- all copyright @ TOP-king -----"); copyright.setEnabled(false); allmsg = new JTextArea(); allmsg.setEditable(false); allmsg.append(" 系统消息: 欢迎登录在线聊天室 \n"); js = new JScrollPane(allmsg);//为JTextArea添加滚动条 chatmsg = new JTextField("在此输入聊天信息"); chatmsg.addActionListener(new listen()); send = new JButton("发送"); send.addActionListener(new listen());/* -- 添加事件监听器 --*/ try { out = new DataOutputStream(s.getOutputStream()); in = new DataInputStream(s.getInputStream()); } catch (IOException e) {JOptionPane.showMessageDialog(null, "系统异常","错误",JOptionPane.OK_CANCEL_OPTION);} addcomponettocontainer(); /* -- 当用户关闭窗口时进行相关的处理 eg:Socket Data(Input/Output)Stream 的关闭--*/ this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent we) { sendmsg("quit&logout");/* -- 向服务器端发送关闭信息 --*/ isConnected = false; destory();/* -- 销毁窗口资源 --*/ } }); new Thread(new linread()).start();/* -- 启动读取信息线程 --*/ } public void addcomponettocontainer() { Container c = this.getContentPane(); c.setLayout(null); welcome.setBounds(75,10,150,20); js.setBounds(10,50,280,500); chatmsg.setBounds(10,560,180,30); send.setBounds(220,560,70,30); copyright.setBounds(10,600,280,20); c.add(welcome); c.add(js); c.add(chatmsg); c.add(send); c.add(copyright); this.setVisible(true); this.setResizable(false); } class listen implements ActionListener { public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub if(e.getSource()==send||e.getSource()==chatmsg) { String msg = chatmsg.getText().trim(); if("".equals(msg)) { JOptionPane.showMessageDialog(null,"发送信息不能为空!","错误",JOptionPane.OK_OPTION); } else {sendmsg((new Date()).toLocaleString()+"\n"+nic+msg+"\n");chatmsg.setText("");} } } } /* -- 向服务器端发送信息 --*/ public void sendmsg(String m) { if(isConnected)//如果socket的输出流没关闭 { try { out.writeUTF(m); } catch (IOException e) { JOptionPane.showMessageDialog(null,"发送信息失败!(系统异常)","错误",JOptionPane.OK_OPTION); } } else { JOptionPane.showMessageDialog(null,"发送信息失败!(服务器关闭/网络故障)","错误",JOptionPane.OK_OPTION); } } /* -- 读取信息线程 --*/ class linread implements Runnable { public void run() { read(); } public void read() { while(isConnected) { try { String msg = in.readUTF(); if("SYSTEM_CLOSED".equals(msg)) { JOptionPane.showMessageDialog(null,"读取消息失败(服务器关闭/网络故障)!","错误",JOptionPane.OK_OPTION); isConnected = false; } else allmsg.append(msg+"\n"); } catch (IOException e) { } }//end while JOptionPane.showMessageDialog(null,"读取消息失败(服务器关闭/网络故障)!","错误",JOptionPane.OK_OPTION); }//end read() } public void destory() { try { this.out.close(); this.in.close(); this.s.close(); } catch (IOException e) { } this.dispose(); } }
二、运行情况