[size=large][color=darkblue]package com.itany.chat;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import com.itany.chat.communication.MessageHandler;
import com.itany.chat.communication.Messenger;
import com.itany.chat.communication.UDPMessager;
import com.itany.chat.utils.ChatUtils;
public class ChatFrame implements MessageHandler {
private JFrame frame;
private JTextArea recvMsg;//接收内容的对话框
private JTextArea enterMsg;//发送内容的对话框
private JList userList;//存放好友的列表
private JButton addUserBtn;//添加好友的按钮
private JButton sendBtn;//发送消息的按钮
private int port;
private String name; //用户名
private Messenger messenger;
private static final String ECHO_STRING = "echo";
private DefaultListModel userListModel;
public ChatFrame() {
buildGUI();
init();
}
private void buildGUI() {
frame = new JFrame();
frame.setSize(600, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
recvMsg = new JTextArea();
recvMsg.setRows(1);
recvMsg.setColumns(10);
JScrollPane scrollRecv = new JScrollPane(recvMsg);
recvMsg.setEditable(false);//设置接收信息的文本框不可编辑
enterMsg = new JTextArea();
JScrollPane scrollEnter = new JScrollPane(enterMsg);
scrollEnter.setMinimumSize(new Dimension(100, 80));
scrollEnter.setPreferredSize(new Dimension(100, 80));
userListModel = new DefaultListModel();
userList = new JList(userListModel);
addUserBtn = new JButton("添加好友");
sendBtn = new JButton("发送消息");
JPanel sendPanel = new JPanel();
sendPanel.setLayout(new BorderLayout(7, 0));
sendPanel.add(scrollEnter, BorderLayout.CENTER);
sendPanel.add(sendBtn, BorderLayout.EAST);
JPanel userListPanel = new JPanel();
JScrollPane scrollUserList = new JScrollPane(userList);
scrollUserList.setMinimumSize(new Dimension(80, 80));
scrollUserList.setPreferredSize(new Dimension(80, 80));
userListPanel.setLayout(new BorderLayout(0, 3));
userListPanel.add(scrollUserList, BorderLayout.CENTER);
userListPanel.add(addUserBtn, BorderLayout.SOUTH);
Container container = frame.getContentPane();
container.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.BOTH;
gbc.insets = new Insets(7, 7, 7, 7);
container.add(scrollRecv, gbc);
gbc.gridx = 1;
gbc.gridy = 0;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.BOTH;
gbc.insets = new Insets(7, 0, 7, 7);
container.add(userListPanel, gbc);
gbc.gridx = 0;
gbc.gridy = 1;
gbc.gridwidth = 2;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.insets = new Insets(0, 7, 7, 7);
gbc.fill = GridBagConstraints.BOTH;
container.add(sendPanel, gbc);
}
private void init() {
while (true) {
String[][] initValue = new String[][] { { "用户名:", "陆晓锋" },
{ "端口:", "9999" } };// 默认的初始的用户名与端口号
InputMesssageDialog initDialog = new InputMesssageDialog(frame,
"请输入用户名和端口", true, initValue);// true用来表示对话框的模态,frame是对话框所在的窗体
initDialog.setVisible(true);// 设置对话框可见
String[] nameAndPort = initDialog.getValue();
if (nameAndPort == null) {
continue;
}
name=nameAndPort[0];//用户名
try {
port = Integer.valueOf(nameAndPort[1]);// 得到端口号
if (port <= 0 || port > 65535) {
String errMsg = "错误的端口号" + port + ",端口号必须在0至65536之间";
JOptionPane.showMessageDialog(frame, errMsg, "错误的端口号",
JOptionPane.ERROR_MESSAGE);// 弹出一个对话框来显示错误信息
continue;
}
} catch (NumberFormatException e) {
//JOptionPane 有助于方便地弹出要求用户提供值或向其发出通知的标准对话框
JOptionPane.showMessageDialog(frame, "输入的端口号不是数字", "错误的端口号",
JOptionPane.ERROR_MESSAGE);// 弹出一个对话框来显示错误信息
}
break;
}
try {
messenger = new UDPMessager(port);// 父类Messager引用指向子类对象
messenger.setMessageHandler(this);// 设置消息处理器(是类本身的一个对象)
messenger.startMessenger();// 开启两个线程
} catch (BindException e) {
// 弹出一个对话框(用来显示错误信息)
JOptionPane.showMessageDialog(frame, "9999端口已经被占用,程序将退出",
"端口号已经被占用", JOptionPane.ERROR_MESSAGE);
System.exit(1);// 退出系统
} catch (Exception e) {
e.printStackTrace();
}
addUserBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addUser();//调用添加好友信息的方法addUser()
}
} );
// 利用匿名内部类实现按钮监听器事件
sendBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
/* String content = enterMsg.getText();// 得到输入对话框中的内容
SocketAddress adder = new InetSocketAddress("192.168.2.42",
9999);//对方的Ip地址和端口号
sendMessage(content, adder);
enterMsg.setText("");//设置输入内容发后之后清空
*/
// messenger.sendData(content.getBytes(), adder);// 把消息保存到list列表中去,调用了 sendMessage方法
sendChatMessage();
}
});
userList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
userList.addListSelectionListener(new ListSelectionListener(){
public void valueChanged(ListSelectionEvent e) {
Object selection=userList.getSelectedValue();
if (selection == null || !(selection instanceof UserModel)) {
return;
}
UserModel user = (UserModel) selection;
recvMsg.setText(user.getMessageHistory().toString());
updateTitle();
}
});
updateTitle();
}
private void sendMessage(String content, SocketAddress addr) {
byte[] data=ChatUtils.buildMessage(name, content);
messenger.sendData(data, addr);
}
//在聊天窗体上显示聊天人名字与端口号
private void updateTitle() {
String title = name + "的聊天窗口,通讯端口为" + port;
Object selection = userList.getSelectedValue();
if (selection != null && (selection instanceof UserModel)) {
title = title + ",对方为" + ((UserModel) selection).getName();
}
this.frame.setTitle(title);
}
private void sendChatMessage() {
Object selection = userList.getSelectedValue();
if (selection == null || !(selection instanceof UserModel)) {
JOptionPane.showMessageDialog(frame,"请选择好友,再发送消息","请选择好友",
JOptionPane.ERROR_MESSAGE);
return;
}
UserModel user = (UserModel) selection;
String content = enterMsg.getText();
sendMessage(content,user.getAddr());
if(content.length()==0){
return ;
}
enterMsg.setText("");
user.getMessageHistory().append(name+"说:\r\n"+content+"\r\n");
recvMsg.setText(user.getMessageHistory().toString());
}
/*private void sendChatMessage() {
Object selection = userList.getSelectedValue();
if (selection == null || !(selection instanceof UserModel)) {
JOptionPane.showMessageDialog(frame, "请先选择好友,再发送消息", "请选择好友",
JOptionPane.ERROR_MESSAGE);
return;
}
UserModel user = (UserModel) selection;
String content = enterMsg.getText();
sendMessage(content, user.getAddr());
if (content.length() == 0) {
return;
}
enterMsg.setText("");
user.getMessageHistory().append(name + "说:\r\n " + content + "\r\n");
recvMsg.setText(user.getMessageHistory().toString());
}
*/
// 窗体可见
public void showFrame() {
this.frame.setVisible(true);
}
//添加好友的方法
private void addUser() {
String[][] defaultUser = new String[][] { { "IP地址:", "192.168.2.43" },{ "端口号:", "7777" } };
InputMesssageDialog userProp = new InputMesssageDialog(frame, "请输入好友信息",
true, defaultUser);
userProp.setVisible(true);
String [] value=userProp.getValue();
if(value==null){
return ;
}
InetSocketAddress socketAddr = ChatUtils.createSocketAddrFromStr(
value[0], value[1]);
if (socketAddr == null) {
JOptionPane.showMessageDialog(frame, "输入的信息错误,无法识别的IP地址和端口号",
"信息错误", JOptionPane.ERROR_MESSAGE);
return;
}
sendMessage(ECHO_STRING, socketAddr);//实际添加好友发送的信息是发送name,content,与ECHO_STRING
}
private void updateChatHistory(String history) {
recvMsg.setText(history);
int lastLineStart = history.lastIndexOf('\n');
if (lastLineStart == -1) {
return;
}
recvMsg.setCaretPosition(lastLineStart);
}
//查找某对象是否存在
private UserModel findUser(UserModel model){
int n=userListModel.getSize();
for(int i=0;i<n;i++){
UserModel user= (UserModel)userListModel.get(i);
if(user.equals(model)){
return user;
}
}
return null;
}
// 因为类实现了处理消息借口 MessageHandler,所有要重写它的方法
public void handleMessage(byte[] data, SocketAddress addr) {
String[] content = ChatUtils.parseMessage(data);//对字节数组进行解析
String userName = content[0];//第一个元素是发给我信息人的名字
String message = content[1];//发送信息的内容
recvMsg.setText(userName+"说"+message);
UserModel newUser=new UserModel(userName,addr);
UserModel user=findUser(newUser);
if(user==null){
userListModel.addElement(newUser);
user=newUser;
}
if(message.equals(ECHO_STRING)){
this.sendMessage("",addr);
return ;
}
if(message.length()>0){
user.getMessageHistory().append(userName+"说:\r\n"+message+"\r\n");
}
userList.setSelectedValue(user, true);
updateChatHistory(user.getMessageHistory().toString());
}
}
package com.itany.chat;
import java.awt.Container;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import com.itany.chat.utils.ChatUtils;
public class InputMesssageDialog extends JDialog {
private String[][] items;
private JTextField[] values;
private String[] retValues;
public InputMesssageDialog(Frame owner, String title, boolean modal,
String[][] items) {
super(owner, title, modal);//modal指对话框的模式,模式的对话框只能操作对话框,其它的都不可以
this.items = items;
init();
}
private void init() {
Container container = this.getContentPane();
container.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
int count = items.length;
gbc.gridx = 0;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.fill = GridBagConstraints.NONE;
gbc.insets = new Insets(3, 3, 3, 3);
for (int i = 0; i < count; i++) {
gbc.gridy = i;
container.add(new JLabel(items[i][0]), gbc);
}
values = new JTextField[count];
gbc.gridx = 1;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
for (int i = 0; i < count; i++) {
gbc.gridy = i;
values[i] = new JTextField(items[i][1]);
container.add(values[i], gbc);
}
gbc.gridx = 0;
gbc.gridy = count;
gbc.gridwidth = 2;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.SOUTH;
container.add(new JSeparator(), gbc);
gbc.gridy = count + 1;
gbc.weightx = 1.0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.SOUTHEAST;
gbc.insets = new Insets(7, 7, 7, 7);
JButton btn = new JButton("确定");
container.add(btn, gbc);
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
retValues = new String[items.length];
for (int i = 0; i < retValues.length; i++) {
retValues[i] = values[i].getText();
}
InputMesssageDialog.this.dispose();
}
});
this.setSize(300, count * 30 + 70);
ChatUtils.locateDialogCenter(this);
}
public String[] getValue() {
return retValues;
}
}
package com.itany.chat;
import java.net.SocketAddress;
public class UserModel {
private String name;
private SocketAddress addr;
private StringBuffer messageHistory;
public UserModel(String name, SocketAddress addr) {
this.name = name;
this.addr = addr;
this.messageHistory =new StringBuffer() ;
}
public SocketAddress getAddr() {
return addr;
}
public void setAddr(SocketAddress addr) {
this.addr = addr;
}
public StringBuffer getMessageHistory() {
return messageHistory;
}
public void setMessageHistory(StringBuffer messageHistory) {
this.messageHistory = messageHistory;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int hashCode(){
int result;
result=(addr==null)?0:addr.hashCode();
return result;
}
public boolean equals(Object obj){
if(this==obj){
return true;
}
if(obj==null){
return false;
}
if(getClass()!=obj.getClass()){
return false;
}
UserModel u=(UserModel)obj;
if(!addr.equals(u.addr)){
return false;
}
return true;
}
public String toString() {
return name;
}
}
package com.itany.chat;
public class ChatFrameMain {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ChatFrame chat=new ChatFrame();
chat.showFrame();
}
}
package com.itany.chat.utils;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.net.InetSocketAddress;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JFrame;
import java.net.*;
public class ChatUtils {
private static JFrame frame;
public static final String SEPARATOR="#";
public static void locateDialogCenter(JDialog dialog) {
int frameWidth = dialog.getWidth();// 对话框的宽度
int frameHeight = dialog.getHeight();
Toolkit toolkit = Toolkit.getDefaultToolkit();// 获取默认工具包
Dimension screen = toolkit.getScreenSize();// 获取屏幕的大小
int screenwidth = screen.width;// 得到屏幕的宽度
int screenheight = screen.height;
// 设置对话框的位置
dialog.setLocation((screenwidth - frameWidth) / 2,
(screenheight - frameHeight) / 2);
}
public static InetSocketAddress createSocketAddrFromStr(String ipstr,
String port) {
int Port;
Port = Integer.valueOf(port);// 得到端口号
try {
if (Port <= 0 || Port > 65535) {
return null;
}
} catch (NumberFormatException e) {
return null;
}
String ip[] = ipstr.split("\\.");
try {
if (ip.length != 4) {
return null;
}
for (int i = 0; i < ip.length; i++) {
if (Integer.valueOf(ip[i]) > 255 || Integer.valueOf(ip[i]) < 0) {
return null;
}
}
} catch (NumberFormatException e) {
return null;
}
/*
* try { Port = Integer.valueOf(port);// 得到端口号 if (Port <= 0 || Port >
* 65535) { String errMsg = "错误的端口号" + port + ",端口号必须在0至65536之间";
* JOptionPane.showMessageDialog(frame, errMsg, "错误的端口号",
* JOptionPane.ERROR_MESSAGE);// 弹出一个对话框来显示错误信息 }
* } catch (NumberFormatException e) {
* JOptionPane.showMessageDialog(frame, "输入的端口号不是数字", "错误的端口号",
* JOptionPane.ERROR_MESSAGE);}
*
*
* String ip[]= ipstr.split("\\.");
*
* try{ if(ip.length!=4){ String errMsg = "错误的ip地址" + port +
* ",ip地址必须是四位"; JOptionPane.showMessageDialog(frame, errMsg,
* "错误的ip地址",JOptionPane.ERROR_MESSAGE); } for(int i=0;i<ip.length;i++){
* if(Integer.valueOf(ip[i])>255||Integer.valueOf(ip[i])<0){ String
* errMsg = "错误的ip地址" + port + ",ip地址必须在0到255之间";
* JOptionPane.showMessageDialog(frame, errMsg,
* "错误的ip地址",JOptionPane.ERROR_MESSAGE); } } } catch
* (NumberFormatException e) { JOptionPane.showMessageDialog(frame,
* "输入的ip地址不是数字", "错误的ip地址", JOptionPane.ERROR_MESSAGE);} return null;
*/
return new InetSocketAddress(ipstr,Port);
}
public static byte[] buildMessage(String name,String content){
String message=name+SEPARATOR+content;
return message.getBytes();
}
public static String[]parseMessage(byte[] data){
String message=new String(data);
int pos=message.indexOf(SEPARATOR);
if(pos==-1){
System.out.println("收到不符合格式的消息"+message);
return null;
}
String [] msg=new String[2];
msg[0]=message.substring(0,pos);
msg[1]=message.substring(pos+1);
return msg;
}
}
communication包:
package com.itany.chat.communication;
import java.net.SocketAddress;
//代表消息处理模块的接口
public interface MessageHandler {
//处理消息的方法,这个方法用来处理UDP消息收发模块接收到的消息
/**
* data 接收到的数据
* addr 消息来自哪个Socket地址
* */
public void handleMessage(byte[] data,SocketAddress addr);
}
package com.itany.chat.communication;
import java.net.SocketAddress;
//代表消息收发模块的接口
public interface Messenger {
//发送数据,消息处理模块和图形界面模块使用这个方法来发送UDP消息,所有的消息
//都由这个消息发送方法统一负责发送
public void sendData(byte[] data,SocketAddress addr);
//为UDP消息收发模块设置一个处理消息的类,UDP消息收发模块不知道如何
//处理消息,每次UDP消息收发模块收到一个消息后,就由消息处理器处理消息
public void setMessageHandler(MessageHandler hanler);
/*
*启动消息发送模块。应该启动两个线程,分别负责发送和接收UDP消息
* */
public void startMessenger();
}
package com.itany.chat.communication;
import java.net.SocketAddress;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.io.IOException;
public class UDPMessager implements Messenger {
public static int DEFAULT_PORT = 9999;
private DatagramSocket UDPWorker;
// 消息列表,每次发送消息的时候,其实将消息
// 封装成一个DatagramPacke对象
private List<DatagramPacket> messageList;
private MessageHandler handler;
public UDPMessager() throws SocketException {
this(DEFAULT_PORT);
}
public UDPMessager(int port) throws SocketException {
UDPWorker = new DatagramSocket(port);
messageList = new ArrayList<DatagramPacket>();
}
/*
* 把消息封装在DatagramPacket对象中,然后把它放入到messageList中
* 唤醒等待在messageList上的线程,等待在messageList上的线程就是 消息发送线程。
*/
public void sendData(byte[] data, SocketAddress addr) {
synchronized (messageList) {
DatagramPacket msg = null;
msg = new DatagramPacket(data, data.length);
msg.setSocketAddress(addr);
messageList.add(msg);
messageList.notifyAll();
}
}
public void setMessageHandler(MessageHandler hanler) {
this.handler = hanler;
}
public void startMessenger() {
Thread recvThread = new Thread(new MessageReciever());
recvThread.start();
Thread sendThread = new Thread(new MessageSender());
sendThread.start();
}
// 接收线程,内部类
class MessageReciever implements Runnable {
public void run() {
byte[] data = new byte[1024];
while (!Thread.currentThread().isInterrupted()) {
DatagramPacket msg = new DatagramPacket(data, data.length);
try {
UDPWorker.receive(msg);
} catch (Exception e) {
e.printStackTrace();
}
byte[] recvData = msg.getData();
byte[] realData = new byte[msg.getLength()];
System.arraycopy(recvData, 0, realData, 0, msg.getLength());
handler.handleMessage(realData, msg.getSocketAddress());
}
}
}
// 发送内部类,实现Runnable接口
class MessageSender implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
synchronized (messageList) {
while (messageList.size() == 0) { // 如果列表为空
try {
messageList.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
DatagramPacket date = messageList
.get(messageList.size() - 1);
messageList.remove(messageList.size() - 1);
try {
UDPWorker.send(date);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}[/color][/size]
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import com.itany.chat.communication.MessageHandler;
import com.itany.chat.communication.Messenger;
import com.itany.chat.communication.UDPMessager;
import com.itany.chat.utils.ChatUtils;
public class ChatFrame implements MessageHandler {
private JFrame frame;
private JTextArea recvMsg;//接收内容的对话框
private JTextArea enterMsg;//发送内容的对话框
private JList userList;//存放好友的列表
private JButton addUserBtn;//添加好友的按钮
private JButton sendBtn;//发送消息的按钮
private int port;
private String name; //用户名
private Messenger messenger;
private static final String ECHO_STRING = "echo";
private DefaultListModel userListModel;
public ChatFrame() {
buildGUI();
init();
}
private void buildGUI() {
frame = new JFrame();
frame.setSize(600, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
recvMsg = new JTextArea();
recvMsg.setRows(1);
recvMsg.setColumns(10);
JScrollPane scrollRecv = new JScrollPane(recvMsg);
recvMsg.setEditable(false);//设置接收信息的文本框不可编辑
enterMsg = new JTextArea();
JScrollPane scrollEnter = new JScrollPane(enterMsg);
scrollEnter.setMinimumSize(new Dimension(100, 80));
scrollEnter.setPreferredSize(new Dimension(100, 80));
userListModel = new DefaultListModel();
userList = new JList(userListModel);
addUserBtn = new JButton("添加好友");
sendBtn = new JButton("发送消息");
JPanel sendPanel = new JPanel();
sendPanel.setLayout(new BorderLayout(7, 0));
sendPanel.add(scrollEnter, BorderLayout.CENTER);
sendPanel.add(sendBtn, BorderLayout.EAST);
JPanel userListPanel = new JPanel();
JScrollPane scrollUserList = new JScrollPane(userList);
scrollUserList.setMinimumSize(new Dimension(80, 80));
scrollUserList.setPreferredSize(new Dimension(80, 80));
userListPanel.setLayout(new BorderLayout(0, 3));
userListPanel.add(scrollUserList, BorderLayout.CENTER);
userListPanel.add(addUserBtn, BorderLayout.SOUTH);
Container container = frame.getContentPane();
container.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.BOTH;
gbc.insets = new Insets(7, 7, 7, 7);
container.add(scrollRecv, gbc);
gbc.gridx = 1;
gbc.gridy = 0;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.BOTH;
gbc.insets = new Insets(7, 0, 7, 7);
container.add(userListPanel, gbc);
gbc.gridx = 0;
gbc.gridy = 1;
gbc.gridwidth = 2;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.insets = new Insets(0, 7, 7, 7);
gbc.fill = GridBagConstraints.BOTH;
container.add(sendPanel, gbc);
}
private void init() {
while (true) {
String[][] initValue = new String[][] { { "用户名:", "陆晓锋" },
{ "端口:", "9999" } };// 默认的初始的用户名与端口号
InputMesssageDialog initDialog = new InputMesssageDialog(frame,
"请输入用户名和端口", true, initValue);// true用来表示对话框的模态,frame是对话框所在的窗体
initDialog.setVisible(true);// 设置对话框可见
String[] nameAndPort = initDialog.getValue();
if (nameAndPort == null) {
continue;
}
name=nameAndPort[0];//用户名
try {
port = Integer.valueOf(nameAndPort[1]);// 得到端口号
if (port <= 0 || port > 65535) {
String errMsg = "错误的端口号" + port + ",端口号必须在0至65536之间";
JOptionPane.showMessageDialog(frame, errMsg, "错误的端口号",
JOptionPane.ERROR_MESSAGE);// 弹出一个对话框来显示错误信息
continue;
}
} catch (NumberFormatException e) {
//JOptionPane 有助于方便地弹出要求用户提供值或向其发出通知的标准对话框
JOptionPane.showMessageDialog(frame, "输入的端口号不是数字", "错误的端口号",
JOptionPane.ERROR_MESSAGE);// 弹出一个对话框来显示错误信息
}
break;
}
try {
messenger = new UDPMessager(port);// 父类Messager引用指向子类对象
messenger.setMessageHandler(this);// 设置消息处理器(是类本身的一个对象)
messenger.startMessenger();// 开启两个线程
} catch (BindException e) {
// 弹出一个对话框(用来显示错误信息)
JOptionPane.showMessageDialog(frame, "9999端口已经被占用,程序将退出",
"端口号已经被占用", JOptionPane.ERROR_MESSAGE);
System.exit(1);// 退出系统
} catch (Exception e) {
e.printStackTrace();
}
addUserBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addUser();//调用添加好友信息的方法addUser()
}
} );
// 利用匿名内部类实现按钮监听器事件
sendBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
/* String content = enterMsg.getText();// 得到输入对话框中的内容
SocketAddress adder = new InetSocketAddress("192.168.2.42",
9999);//对方的Ip地址和端口号
sendMessage(content, adder);
enterMsg.setText("");//设置输入内容发后之后清空
*/
// messenger.sendData(content.getBytes(), adder);// 把消息保存到list列表中去,调用了 sendMessage方法
sendChatMessage();
}
});
userList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
userList.addListSelectionListener(new ListSelectionListener(){
public void valueChanged(ListSelectionEvent e) {
Object selection=userList.getSelectedValue();
if (selection == null || !(selection instanceof UserModel)) {
return;
}
UserModel user = (UserModel) selection;
recvMsg.setText(user.getMessageHistory().toString());
updateTitle();
}
});
updateTitle();
}
private void sendMessage(String content, SocketAddress addr) {
byte[] data=ChatUtils.buildMessage(name, content);
messenger.sendData(data, addr);
}
//在聊天窗体上显示聊天人名字与端口号
private void updateTitle() {
String title = name + "的聊天窗口,通讯端口为" + port;
Object selection = userList.getSelectedValue();
if (selection != null && (selection instanceof UserModel)) {
title = title + ",对方为" + ((UserModel) selection).getName();
}
this.frame.setTitle(title);
}
private void sendChatMessage() {
Object selection = userList.getSelectedValue();
if (selection == null || !(selection instanceof UserModel)) {
JOptionPane.showMessageDialog(frame,"请选择好友,再发送消息","请选择好友",
JOptionPane.ERROR_MESSAGE);
return;
}
UserModel user = (UserModel) selection;
String content = enterMsg.getText();
sendMessage(content,user.getAddr());
if(content.length()==0){
return ;
}
enterMsg.setText("");
user.getMessageHistory().append(name+"说:\r\n"+content+"\r\n");
recvMsg.setText(user.getMessageHistory().toString());
}
/*private void sendChatMessage() {
Object selection = userList.getSelectedValue();
if (selection == null || !(selection instanceof UserModel)) {
JOptionPane.showMessageDialog(frame, "请先选择好友,再发送消息", "请选择好友",
JOptionPane.ERROR_MESSAGE);
return;
}
UserModel user = (UserModel) selection;
String content = enterMsg.getText();
sendMessage(content, user.getAddr());
if (content.length() == 0) {
return;
}
enterMsg.setText("");
user.getMessageHistory().append(name + "说:\r\n " + content + "\r\n");
recvMsg.setText(user.getMessageHistory().toString());
}
*/
// 窗体可见
public void showFrame() {
this.frame.setVisible(true);
}
//添加好友的方法
private void addUser() {
String[][] defaultUser = new String[][] { { "IP地址:", "192.168.2.43" },{ "端口号:", "7777" } };
InputMesssageDialog userProp = new InputMesssageDialog(frame, "请输入好友信息",
true, defaultUser);
userProp.setVisible(true);
String [] value=userProp.getValue();
if(value==null){
return ;
}
InetSocketAddress socketAddr = ChatUtils.createSocketAddrFromStr(
value[0], value[1]);
if (socketAddr == null) {
JOptionPane.showMessageDialog(frame, "输入的信息错误,无法识别的IP地址和端口号",
"信息错误", JOptionPane.ERROR_MESSAGE);
return;
}
sendMessage(ECHO_STRING, socketAddr);//实际添加好友发送的信息是发送name,content,与ECHO_STRING
}
private void updateChatHistory(String history) {
recvMsg.setText(history);
int lastLineStart = history.lastIndexOf('\n');
if (lastLineStart == -1) {
return;
}
recvMsg.setCaretPosition(lastLineStart);
}
//查找某对象是否存在
private UserModel findUser(UserModel model){
int n=userListModel.getSize();
for(int i=0;i<n;i++){
UserModel user= (UserModel)userListModel.get(i);
if(user.equals(model)){
return user;
}
}
return null;
}
// 因为类实现了处理消息借口 MessageHandler,所有要重写它的方法
public void handleMessage(byte[] data, SocketAddress addr) {
String[] content = ChatUtils.parseMessage(data);//对字节数组进行解析
String userName = content[0];//第一个元素是发给我信息人的名字
String message = content[1];//发送信息的内容
recvMsg.setText(userName+"说"+message);
UserModel newUser=new UserModel(userName,addr);
UserModel user=findUser(newUser);
if(user==null){
userListModel.addElement(newUser);
user=newUser;
}
if(message.equals(ECHO_STRING)){
this.sendMessage("",addr);
return ;
}
if(message.length()>0){
user.getMessageHistory().append(userName+"说:\r\n"+message+"\r\n");
}
userList.setSelectedValue(user, true);
updateChatHistory(user.getMessageHistory().toString());
}
}
package com.itany.chat;
import java.awt.Container;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import com.itany.chat.utils.ChatUtils;
public class InputMesssageDialog extends JDialog {
private String[][] items;
private JTextField[] values;
private String[] retValues;
public InputMesssageDialog(Frame owner, String title, boolean modal,
String[][] items) {
super(owner, title, modal);//modal指对话框的模式,模式的对话框只能操作对话框,其它的都不可以
this.items = items;
init();
}
private void init() {
Container container = this.getContentPane();
container.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
int count = items.length;
gbc.gridx = 0;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.fill = GridBagConstraints.NONE;
gbc.insets = new Insets(3, 3, 3, 3);
for (int i = 0; i < count; i++) {
gbc.gridy = i;
container.add(new JLabel(items[i][0]), gbc);
}
values = new JTextField[count];
gbc.gridx = 1;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
for (int i = 0; i < count; i++) {
gbc.gridy = i;
values[i] = new JTextField(items[i][1]);
container.add(values[i], gbc);
}
gbc.gridx = 0;
gbc.gridy = count;
gbc.gridwidth = 2;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.SOUTH;
container.add(new JSeparator(), gbc);
gbc.gridy = count + 1;
gbc.weightx = 1.0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.SOUTHEAST;
gbc.insets = new Insets(7, 7, 7, 7);
JButton btn = new JButton("确定");
container.add(btn, gbc);
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
retValues = new String[items.length];
for (int i = 0; i < retValues.length; i++) {
retValues[i] = values[i].getText();
}
InputMesssageDialog.this.dispose();
}
});
this.setSize(300, count * 30 + 70);
ChatUtils.locateDialogCenter(this);
}
public String[] getValue() {
return retValues;
}
}
package com.itany.chat;
import java.net.SocketAddress;
public class UserModel {
private String name;
private SocketAddress addr;
private StringBuffer messageHistory;
public UserModel(String name, SocketAddress addr) {
this.name = name;
this.addr = addr;
this.messageHistory =new StringBuffer() ;
}
public SocketAddress getAddr() {
return addr;
}
public void setAddr(SocketAddress addr) {
this.addr = addr;
}
public StringBuffer getMessageHistory() {
return messageHistory;
}
public void setMessageHistory(StringBuffer messageHistory) {
this.messageHistory = messageHistory;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int hashCode(){
int result;
result=(addr==null)?0:addr.hashCode();
return result;
}
public boolean equals(Object obj){
if(this==obj){
return true;
}
if(obj==null){
return false;
}
if(getClass()!=obj.getClass()){
return false;
}
UserModel u=(UserModel)obj;
if(!addr.equals(u.addr)){
return false;
}
return true;
}
public String toString() {
return name;
}
}
package com.itany.chat;
public class ChatFrameMain {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ChatFrame chat=new ChatFrame();
chat.showFrame();
}
}
package com.itany.chat.utils;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.net.InetSocketAddress;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JFrame;
import java.net.*;
public class ChatUtils {
private static JFrame frame;
public static final String SEPARATOR="#";
public static void locateDialogCenter(JDialog dialog) {
int frameWidth = dialog.getWidth();// 对话框的宽度
int frameHeight = dialog.getHeight();
Toolkit toolkit = Toolkit.getDefaultToolkit();// 获取默认工具包
Dimension screen = toolkit.getScreenSize();// 获取屏幕的大小
int screenwidth = screen.width;// 得到屏幕的宽度
int screenheight = screen.height;
// 设置对话框的位置
dialog.setLocation((screenwidth - frameWidth) / 2,
(screenheight - frameHeight) / 2);
}
public static InetSocketAddress createSocketAddrFromStr(String ipstr,
String port) {
int Port;
Port = Integer.valueOf(port);// 得到端口号
try {
if (Port <= 0 || Port > 65535) {
return null;
}
} catch (NumberFormatException e) {
return null;
}
String ip[] = ipstr.split("\\.");
try {
if (ip.length != 4) {
return null;
}
for (int i = 0; i < ip.length; i++) {
if (Integer.valueOf(ip[i]) > 255 || Integer.valueOf(ip[i]) < 0) {
return null;
}
}
} catch (NumberFormatException e) {
return null;
}
/*
* try { Port = Integer.valueOf(port);// 得到端口号 if (Port <= 0 || Port >
* 65535) { String errMsg = "错误的端口号" + port + ",端口号必须在0至65536之间";
* JOptionPane.showMessageDialog(frame, errMsg, "错误的端口号",
* JOptionPane.ERROR_MESSAGE);// 弹出一个对话框来显示错误信息 }
* } catch (NumberFormatException e) {
* JOptionPane.showMessageDialog(frame, "输入的端口号不是数字", "错误的端口号",
* JOptionPane.ERROR_MESSAGE);}
*
*
* String ip[]= ipstr.split("\\.");
*
* try{ if(ip.length!=4){ String errMsg = "错误的ip地址" + port +
* ",ip地址必须是四位"; JOptionPane.showMessageDialog(frame, errMsg,
* "错误的ip地址",JOptionPane.ERROR_MESSAGE); } for(int i=0;i<ip.length;i++){
* if(Integer.valueOf(ip[i])>255||Integer.valueOf(ip[i])<0){ String
* errMsg = "错误的ip地址" + port + ",ip地址必须在0到255之间";
* JOptionPane.showMessageDialog(frame, errMsg,
* "错误的ip地址",JOptionPane.ERROR_MESSAGE); } } } catch
* (NumberFormatException e) { JOptionPane.showMessageDialog(frame,
* "输入的ip地址不是数字", "错误的ip地址", JOptionPane.ERROR_MESSAGE);} return null;
*/
return new InetSocketAddress(ipstr,Port);
}
public static byte[] buildMessage(String name,String content){
String message=name+SEPARATOR+content;
return message.getBytes();
}
public static String[]parseMessage(byte[] data){
String message=new String(data);
int pos=message.indexOf(SEPARATOR);
if(pos==-1){
System.out.println("收到不符合格式的消息"+message);
return null;
}
String [] msg=new String[2];
msg[0]=message.substring(0,pos);
msg[1]=message.substring(pos+1);
return msg;
}
}
communication包:
package com.itany.chat.communication;
import java.net.SocketAddress;
//代表消息处理模块的接口
public interface MessageHandler {
//处理消息的方法,这个方法用来处理UDP消息收发模块接收到的消息
/**
* data 接收到的数据
* addr 消息来自哪个Socket地址
* */
public void handleMessage(byte[] data,SocketAddress addr);
}
package com.itany.chat.communication;
import java.net.SocketAddress;
//代表消息收发模块的接口
public interface Messenger {
//发送数据,消息处理模块和图形界面模块使用这个方法来发送UDP消息,所有的消息
//都由这个消息发送方法统一负责发送
public void sendData(byte[] data,SocketAddress addr);
//为UDP消息收发模块设置一个处理消息的类,UDP消息收发模块不知道如何
//处理消息,每次UDP消息收发模块收到一个消息后,就由消息处理器处理消息
public void setMessageHandler(MessageHandler hanler);
/*
*启动消息发送模块。应该启动两个线程,分别负责发送和接收UDP消息
* */
public void startMessenger();
}
package com.itany.chat.communication;
import java.net.SocketAddress;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.io.IOException;
public class UDPMessager implements Messenger {
public static int DEFAULT_PORT = 9999;
private DatagramSocket UDPWorker;
// 消息列表,每次发送消息的时候,其实将消息
// 封装成一个DatagramPacke对象
private List<DatagramPacket> messageList;
private MessageHandler handler;
public UDPMessager() throws SocketException {
this(DEFAULT_PORT);
}
public UDPMessager(int port) throws SocketException {
UDPWorker = new DatagramSocket(port);
messageList = new ArrayList<DatagramPacket>();
}
/*
* 把消息封装在DatagramPacket对象中,然后把它放入到messageList中
* 唤醒等待在messageList上的线程,等待在messageList上的线程就是 消息发送线程。
*/
public void sendData(byte[] data, SocketAddress addr) {
synchronized (messageList) {
DatagramPacket msg = null;
msg = new DatagramPacket(data, data.length);
msg.setSocketAddress(addr);
messageList.add(msg);
messageList.notifyAll();
}
}
public void setMessageHandler(MessageHandler hanler) {
this.handler = hanler;
}
public void startMessenger() {
Thread recvThread = new Thread(new MessageReciever());
recvThread.start();
Thread sendThread = new Thread(new MessageSender());
sendThread.start();
}
// 接收线程,内部类
class MessageReciever implements Runnable {
public void run() {
byte[] data = new byte[1024];
while (!Thread.currentThread().isInterrupted()) {
DatagramPacket msg = new DatagramPacket(data, data.length);
try {
UDPWorker.receive(msg);
} catch (Exception e) {
e.printStackTrace();
}
byte[] recvData = msg.getData();
byte[] realData = new byte[msg.getLength()];
System.arraycopy(recvData, 0, realData, 0, msg.getLength());
handler.handleMessage(realData, msg.getSocketAddress());
}
}
}
// 发送内部类,实现Runnable接口
class MessageSender implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
synchronized (messageList) {
while (messageList.size() == 0) { // 如果列表为空
try {
messageList.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
DatagramPacket date = messageList
.get(messageList.size() - 1);
messageList.remove(messageList.size() - 1);
try {
UDPWorker.send(date);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}[/color][/size]
本文介绍了一个基于Java的聊天应用程序的设计与实现过程,包括图形界面构建、消息处理机制及网络通信细节。该程序支持用户间的即时消息交换,并具备添加好友等功能。
251

被折叠的 条评论
为什么被折叠?



