/* 学习开发这个聊天系统已经一周总结一下:
* 1、客户端的接受数据功能要new一个线程来处理;服务器端接受并处理来自客户端的程序时也要
* new一个线程来处理。就是说:对于一直要在后台服务的代码段要新建个线程来处理;由于服务器要
* 处理很多个客户端,所以对应每一个客户端都要新建个线程来处理。
*
* 2、JFrame关闭按钮默认响应是隐藏(HIDE_ON_CLOSE),所以在给JFrame的关闭按钮添加监听器时,
* 先设置关闭事件为DO_NOTHING_ON_CLOSE.
*
* 3、响应键盘事件(KeyEvent),当按下Ctrl+Enter时发送数据,Enter有键码,Ctrl没有,
* Ctrl起修饰作用,通过这句:KeyEvent.getModifiersExText(e.getModifiersEx())得到
* 修饰键的字符串(这里是"Ctrl")
*
*
* 技巧:
* 1、给组件注册监听器后,可以调用组件的setActionCommand()方法来设置命令字符串。如:
* 语句:miClose.setActionCommand("退出");把关闭菜单项的命令字符串设置为"退出",因为面板
* 上退出按钮的命令字符串为"退出",于是在监听器类只需处理命令字符串为"退出"的事件,代码如下:
* if(e.getActionCommand.equals("退出")){quit();}
* 2、JOptionPane有几个常用方法:showConfirmDialog 询问一个确认问题,如 yes/no/cancel;
* showInputDialog 提示要求某些输入; showMessageDialog 告知用户某事已发生;
* showOptionDialog 上述三项的大统一 。
*
* 最后:
* 程序是调出来的。
* 注意版本控制,保留以前的版本。
* 不要混用awt组件和swing组件。
* 参看API文档是好习惯。
*
*///客户端:chatClient.java
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
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.ConnectException;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/**
* @author hanwei
*
*/
public class chatClient {
private JFrame frame = new JFrame("Chat");
private JTextArea input = new JTextArea(3,32);
private JTextArea output = new JTextArea(10,37);
private JScrollPane inputsp = new JScrollPane(input);
private JScrollPane outputsp = new JScrollPane(output);
private JButton send = new JButton("发送");
private JButton quit = new JButton("退出");
private JButton conn = new JButton("连接");
private JPanel jp = new JPanel();
private Container container = frame.getContentPane();
private JMenuBar mb = new JMenuBar();
private JMenu mFile = new JMenu("文件");
private JMenu mHelp = new JMenu("帮助");
private JMenuItem miClose = new JMenuItem("关闭");
private JMenuItem miConn = new JMenuItem("连接");
private JMenuItem miAbout = new JMenuItem("关于");
private Socket sk = null;
private DataOutputStream dos = null;
private DataInputStream dis = null;
Thread getMsgThread = null;
private boolean connected = false;
//窗体初始化
public void launchFrame(){
//JFrame关闭按钮默认响应是隐藏(HIDE_ON_CLOSE),
//所以先设置关闭事件为DO_NOTHING_ON_CLOSE.
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
quit();
return;
}
});
send.setToolTipText("快捷键:Ctrl+Enter");
quit.addActionListener(new ActionHandler());
send.addActionListener(new ActionHandler());
conn.addActionListener(new ActionHandler());
jp.setLayout(new BorderLayout());
output.setEditable(false);
input.setToolTipText("发送快捷键:Ctrl+Enter");
input.addKeyListener(new ActionHandler());
jp.add(conn,BorderLayout.SOUTH);
jp.add(send,BorderLayout.CENTER);
jp.add(quit,BorderLayout.NORTH);
container.add(jp,BorderLayout.EAST);
container.add(inputsp,BorderLayout.SOUTH);
container.add(outputsp,BorderLayout.CENTER);
miClose.addActionListener(new ActionHandler());
miClose.setActionCommand("退出");
miAbout.addActionListener(new ActionHandler());
miConn.addActionListener(new ActionHandler());
mFile.add(miConn);
mFile.addSeparator();
mFile.add(miClose);
mHelp.add(miAbout);
mb.add(mFile);
mb.add(mHelp);
frame.setJMenuBar(mb);
frame.setVisible(true);
frame.pack();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame.setLocation((screenSize.width-frame.getWidth())/2
,(screenSize.height-frame.getHeight())/2);
}
//事件监听内部类
class ActionHandler extends KeyAdapter implements ActionListener{
@Override
public void keyPressed(KeyEvent e) {
/*System.out.println("KeyCode"+e.getKeyCode()
+" KeyChar"+e.getKeyChar());
System.out.println(KeyEvent.VK_ENTER);
System.out.println(KeyEvent.getModifiersExText(e.getModifiersEx()));
*/
if(KeyEvent.getModifiersExText(e.getModifiersEx()).equals("Ctrl")
&&e.getKeyCode()==KeyEvent.VK_ENTER){
sendMsg();
return;
}
super.keyTyped(e);
}
public void actionPerformed(ActionEvent e) {
String ActionCommand=e.getActionCommand();
if(ActionCommand.equals("退出")){
quit();
return;
}
if(ActionCommand.equals("发送")){
sendMsg();
return;
}
if(ActionCommand.equals("连接")){
conn();
return;
}
if(ActionCommand.equals("关于")){
about();
return;
}
}
}
//连接到服务器
public void conn(){
try {
if(!connected){
sk = new Socket("127.0.0.1",8888);
dos = new DataOutputStream(sk.getOutputStream());
dis = new DataInputStream(sk.getInputStream());
connected = true;
getMsg();
output.append("连接上服务器:"
+sk.getRemoteSocketAddress());
}else{//connected==true时,即已经连接上服务器时
JOptionPane.showMessageDialog(frame,"已连接上服务器:"
+sk.getRemoteSocketAddress());
}
} catch (ConnectException e) {
JOptionPane.showMessageDialog(frame,"请先启动服务器!");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//发送数据
public void sendMsg() {
if(!connected){
JOptionPane.showMessageDialog(frame,"请先连接到服务器!");
return;
}
String str = input.getText();
if(!str.equals("")&&str!=null){
input.setText("");
try {
dos.writeUTF(str);
dos.flush();
} catch (IOException e1) {
e1.printStackTrace();
// connected = false;
}
}else{
JOptionPane.showMessageDialog(frame,"发送内容不能为空!");
}
}
class getMsg implements Runnable{
public void run() {
try {
while(connected){
String str = dis.readUTF();
output.append("/n"+str);
}
} catch (IOException e) {
try {
if (dis != null) {
dis.close();
dis = null;
}
} catch (IOException e1) {
e1.printStackTrace();
}//这地方偶尔抛出异常java.net.SocketException: Socket closed,
//可我有时狂点测试却没有抛出异常,很奇怪。所以只好把下面这行注释
//e.printStackTrace();
}
}
}
//接收数据
public void getMsg(){
getMsg gm = new getMsg();
getMsgThread = new Thread(gm);
getMsgThread.start();
}
//关闭事件
@SuppressWarnings("deprecation")
public void quit() {
int yes =JOptionPane.showConfirmDialog(frame, "真的要退出吗?"
,"退出",JOptionPane.YES_NO_OPTION);
if(yes==0){
// connected = false;
try {
if(dos!=null){
dos.close();
dos = null;
}
if(dis!=null){
dis.close();
dis = null;
}
if(sk!=null){
sk.close();
sk = null;
}
} catch (Exception e) {
e.printStackTrace();
}
if(getMsgThread!=null){
getMsgThread.stop();
getMsgThread=null;
}
frame.dispose();
System.exit(0);
}
}
//关于菜单事件
public void about() {
String str = "Chat v1.2/n"
+"作者:/n西安邮电学院/n东软2班/n计科0401/n寒微";
JOptionPane.showMessageDialog(frame, str
, "Chat 关于", JOptionPane.INFORMATION_MESSAGE);
}
//main方法在这
public static void main(String[] args) {
chatClient chat = new chatClient();
chat.launchFrame();
}
}
服务器:chatServer.java
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JOptionPane;
/**
* @author hanwei
*
*/
public class chatServer {
private ServerSocket ss = null;
private List<acceptClient> clients = new ArrayList<acceptClient>();
public static void main(String[] args) {
new chatServer().start();
}
public void start() {
boolean serverstarted = false;
try {
ss= new ServerSocket(8888);
serverstarted = true;
System.out.println("服务器正在启动!");
}catch(BindException be){
JOptionPane.showMessageDialog(null, "服务器已经启动!");
System.exit(0);
}catch (IOException e) {
e.printStackTrace();
}
try {
int i=0;//连接到服务器的客户端数
while(serverstarted){
Socket sk = ss.accept();
System.out.println("第"+(++i)+"个客户端连接到服务器!");
acceptClient ac=new acceptClient(sk);//启动一个线程来处理一个客户端
clients.add(ac);
new Thread(ac).start();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(ss!=null) ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//acceptClient内部类,线程类,处理连接到服务器的客户端所发来的数据。
class acceptClient implements Runnable{
private DataInputStream dis = null;
private DataOutputStream dos = null;
private Socket sk = null;
private boolean connected = false;
public acceptClient(Socket s){
sk = s;
try {
dis = new DataInputStream(sk.getInputStream());
dos = new DataOutputStream(sk.getOutputStream());
connected = true;
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try{
while(connected){
String str = dis.readUTF();
System.out.println("客户端("+this.sk.getRemoteSocketAddress()+"):"+str);
for(int i=0;i<clients.size();i++){
acceptClient c = clients.get(i);
// if(!this.equals(c))
c.send(this.sk.getRemoteSocketAddress()+"::"+str);
}
}
} catch (EOFException e) {
System.out.println("客户端("
+this.sk.getRemoteSocketAddress()+")已关闭!");
clients.remove(this);
// if(sk!=null) sk.close();
} catch (IOException e) {
e.printStackTrace();
} finally{
connected = false;
try {
if(dis!=null){
dis.close();
dis = null;
}
if(dos!=null){
dos.close();
dos = null;
}
if(sk!=null){
sk.close();
sk =null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void send(String str){
try {
dos.writeUTF(str);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}