在学习socket的时候,通过查找资料,和自己的学习,花了一个星期写出了这个模型,尽管功能和不太强大,还有点bug,但至少体现了socket的基本思想。部分代码省略了
客户端口,可以在client.ini 中配置
功能:
1. 用socket实现client/server的通信,服务端口8888;(配置文件)
2. 提供聊天的GUI,用Java Application实现,包括一个主窗口以及聊天窗口;(Swing)
3. 可实现一对多的聊天,每一个连接打开一个新的聊天窗口;(Thread)
4. 服务器端显示连接到客户端的IP地址,连接人数,以及在线客户的聊天记录
5. 客户端显示服务器IP地址,本机IP地址,和连接服务器的人数,以及当前客户的聊天记录
6. 关闭服务器,客户端连接失败,信息发送不出去;
7. 关闭客户端,服务器显示离开的客户端的IP地址;
8. 通过文件配置读server的端口,和客户端的IP,Port;
9. 具有发送文本约束性。
1.服务器端
public class ChatServer extends JFrame implements Runnable, ActionListener {
private JTextArea taOther;
private JScrollPane pnOther;
private JMenuItem version;
private JMenuItem quit;
private JMenu exitmn;
private JMenu helpmn;
private JMenuItem newmn;
private JMenu filemn;
private JMenuBar menuBar;
private static final long serialVersionUID = 1L;
private JTextArea taMessage;
private JScrollPane pnMessage;
private ServerSocket server; // 定义服务器端套接字
private BManager bMan = new BManager();
/**
* @param title
*/
public ChatServer(String title) {
super(title);
initialize();
}
/**
* 启动服务器
*
*/
public void startServer() {
try {
// XXX 读取服务器端配置文件
// 格式化代码
BufferedReader br = new BufferedReader(new FileReader("server.ini"));
// 产生读属性文件的对象
Properties properties = new Properties();
// 加载读文件的数据
properties.load(br);
// 从属性对象中取出对应的值
String port = properties.getProperty("port");
server = new ServerSocket(Integer.parseInt(port));
taOther.append("服务器套接字创建完毕.\n");
taOther.append("主机IP地址:"
+ InetAddress.getLocalHost().getHostAddress() + "\n");
taOther.append("等待客户机连接……" + "\n");
while (true) {
Socket socket = server.accept();
taOther.append("客户IP地址:" + socket.getInetAddress() + "\n");
// 创建并运行与客户机通信的线程
new Chat_Thread(socket).start();
// 向消息广播者列表添加socket
bMan.add(socket);
// 消息广播者向所有客户机传送当前连接人数
bMan.sendClientInfo();
}
} catch (Exception e) {
taOther.append(("服务器已经被启用,不能再被启用"));
}
}
public static void main(String[] args) {
ChatServer server = new ChatServer("聊天服务器");
server.setVisible(true);
server.startServer();
}
/**
* 与客户机通信的线程类
*
*/
class Chat_Thread extends Thread {
Socket socket;
private BufferedReader reader;
private PrintWriter writer;
public Chat_Thread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
// 从输出流获取消息
reader = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
writer = new PrintWriter(socket.getOutputStream(), true);
writer.flush();
String msg;
while ((msg = reader.readLine()) != null) {
taMessage.append(msg.trim() + "\n");
// 向所有客户机传送信息
bMan.sendToAll(msg);
}
} catch (Exception e) {
} finally {
try {
// 从消息广播者列表删除socket
bMan.remove(socket);
taOther.append("客户机" + socket.getInetAddress() + "离开"
+ "\n");
if (reader != null)
reader.close();
if (writer != null)
writer.close();
if (socket != null)
socket.close();
reader = null;
writer = null;
socket = null;
bMan.sendClientInfo();
} catch (Exception e) {
}
}
}
}
/**
* 消息广播者类,继承Vector类
*
*
*/
class BManager extends Vector {
public BManager() {
}
/**
* 添加套接字
*
* @param sock
*
*/
public void add(Socket sock) {
super.add(sock);
}
/**
* 删除套接字
*
* @param sock
*/
public void remove(Socket sock) {
super.remove(sock);
}
/**
* 向所有客户机传送内容
*
* @param msg
*/
public synchronized void sendToAll(String msg) {
PrintWriter writer = null;
Socket sock;
for (int i = 0; i < size(); i++) {
// 获取第i套接字
sock = (Socket) elementAt(i);
try {
// 输出流刷空
writer = new PrintWriter(sock.getOutputStream(), true);
} catch (IOException ie) {
}
// 使用第i个套接字输出流,输出msg
if (writer != null)
writer.println(msg);
}
}
/**
* 服务器和客户端同时显示当前聊天人数
*
*/
public synchronized void sendClientInfo() {
String info = "当前聊天人数:" + size();
taOther.append(info + "\n");
sendToAll(info);
}
}
private void initialize() {
setJMenuBar(getMainJMenuBar());
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setName("f");
setSize(new Dimension(700, 500));
getContentPane().add(getPnMessage(), BorderLayout.CENTER);
getContentPane().add(getPnOther(), BorderLayout.EAST);
}
/**
* @return
*/
protected JScrollPane getPnMessage() {
if (pnMessage == null) {
pnMessage = new JScrollPane();
pnMessage.setViewportView(getTaMessage());
}
return pnMessage;
}
/**
* @return
*/
protected JTextArea getTaMessage() {
if (taMessage == null) {
taMessage = new JTextArea();
taMessage.setBackground(new Color(220, 220, 220));
taMessage.setEditable(false);
}
return taMessage;
}
/**
* @return
*/
/**
* @return
*/
@Override
public void run() {
// TODO Auto-generated method stub
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if (e.getSource() == getQuit()) {
System.exit(0);
}
}
/**
* @return
*/
protected JMenuBar getMainJMenuBar() {
if (menuBar == null) {
menuBar = new JMenuBar();
menuBar.add(getFilemn());
menuBar.add(getHelpmn());
menuBar.add(getExitmn());
}
return menuBar;
}
/**
* @return
*/
protected JMenu getFilemn() {
if (filemn == null) {
filemn = new JMenu();
filemn.setText("文件");
filemn.add(getNewmn());
}
return filemn;
}
/**
* @return
*/
protected JMenuItem getNewmn() {
if (newmn == null) {
newmn = new JMenuItem();
newmn.setText("新建");
}
return newmn;
}
/**
* @return
*/
protected JMenu getHelpmn() {
if (helpmn == null) {
helpmn = new JMenu();
helpmn.setText("帮助");
helpmn.add(getVersion());
}
return helpmn;
}
/**
* @return
*/
protected JMenu getExitmn() {
if (exitmn == null) {
exitmn = new JMenu();
exitmn.setText("退出");
exitmn.add(getQuit());
}
return exitmn;
}
/**
* @return
*/
protected JMenuItem getQuit() {
if (quit == null) {
quit = new JMenuItem();
quit.addActionListener(this);
quit.setText("关闭");
}
return quit;
}
/**
* @return
*/
protected JMenuItem getVersion() {
if (version == null) {
version = new JMenuItem();
version.setText("版本");
}
return version;
}
/**
* @return
*/
protected JScrollPane getPnOther() {
if (pnOther == null) {
pnOther = new JScrollPane();
pnOther.setViewportView(getTaOther());
}
return pnOther;
}
/**
* @return
*/
protected JTextArea getTaOther() {
if (taOther == null) {
taOther = new JTextArea();
taOther.setEditable(false);
}
return taOther;
}
}
2.客户端
public class ChatClient extends JFrame implements Runnable, ActionListener {
private JTextArea tauser;
private JScrollPane userpn;
private JScrollPane viewpn;
private JMenuItem versionmn;
private JMenuItem shutupmn;
private JTextArea ta;
private JMenuItem connectmn;
private JTextField nametf;
private JTextField sendtf;
private JButton sendbtn;
private JPanel panel;
private JMenu helpmn;
private JMenu quitmn;
private JMenu newmn;
private JMenuBar menuBar;
private Socket socket;
private BufferedReader reader;
private PrintWriter writer;
private boolean runnable = true;
public boolean getRunnable() {
return runnable;
}
public void stop() {
runnable = false;
}
/**
* 客户端主程序
*
* @param args
*/
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ChatClient client = new ChatClient();
client.setVisible(true);
client.connect();// 连接服务器
} catch (Exception e) {
// e.printStackTrace();
}
}
});
}
/**
* 添加动作事件监听器
*
*/
public void actionPerformed(ActionEvent e) {
// 监听发送按钮和输入信息文本框的事件
if (e.getSource() == getSendtf() || e.getSource() == getSendbtn()) {
// 如果输入昵称为空,弹出警告框
if (nametf.getText().equals("")) {
JOptionPane.showMessageDialog(null, "请输入昵称!", "警告",
JOptionPane.ERROR_MESSAGE);
}
// 如果输入的内容为空,弹出警告框
else if (sendtf.getText().equals("")) {
JOptionPane.showMessageDialog(null, "输入内容不能为空!", "警告",
JOptionPane.ERROR_MESSAGE);
}
// 否则向文本框发送输入的内容
else {
writer.println(nametf.getText().trim() + " : "
+ sendtf.getText().trim());
sendtf.setText("");// 清除sendBox 内容
}
}
// 关闭客户端事件
if (e.getSource() == getShutupmn()) {
try {
this.stop();
socket.close();
} catch (Exception e1) {
// e1.printStackTrace();
System.exit(0);
}
System.exit(0);
}
// 版本事件
if (e.getSource() == getVersionmn()) {
Version v = new Version();
v.show();
}
if (e.getSource() == getNewmn()) {
ChatClient client = null;
// return;
client.connect();
}
}
/**
* 客户端连接服务器
*
*/
private void connect() {
try {
// 读文件
// 格式化代码
BufferedReader rd = new BufferedReader(new FileReader("client.ini"));
// 产生读属性文件的对象
Properties properties = new Properties();
// 加载读文件的数据
properties.load(rd);
// 从属性对象中取出key对应的值
String ip = (String) properties.get("IP");
String port = (String) properties.get("port");
// 客户端ip,port
socket = new Socket(ip, Integer.parseInt(port));
reader = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
writer = new PrintWriter(socket.getOutputStream(), true);
writer.flush();
new Thread(this).start();
tauser.append("连接服务器成功!" + "\n");
tauser.append("本机地址:" + InetAddress.getLocalHost().getHostAddress()
+ "\n");
tauser.append("服务器地址:" + socket.getInetAddress() + "\n");
} catch (Exception e) {
// e.printStackTrace();
tauser.append("服务器未启动,请先启动服务器!");
}
}
/**
* 创建客户端界面
*
*/
public ChatClient() {
super();
setBounds(100, 100, 500, 375);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initialize();
}
private void initialize() {
setTitle("客户机");
setJMenuBar(getMainJMenuBar());
getContentPane().add(getPanel(), BorderLayout.SOUTH);
getContentPane().add(getNametf(), BorderLayout.NORTH);
getContentPane().add(getViewpn());
getContentPane().add(getUserpn(), BorderLayout.EAST);
}
/**
* @return 菜单栏
*/
protected JMenuBar getMainJMenuBar() {
if (menuBar == null) {
menuBar = new JMenuBar();
menuBar.add(getNewmn());
menuBar.add(getHelpmn());
menuBar.add(getQuitmn());
}
return menuBar;
}
/**
* @return 新建
*/
protected JMenu getNewmn() {
if (newmn == null) {
newmn = new JMenu();
newmn.setText("新建");
newmn.add(getConnectmn());
}
return newmn;
}
/**
* @return 退出
*/
protected JMenu getQuitmn() {
if (quitmn == null) {
quitmn = new JMenu();
quitmn.addActionListener(this);
quitmn.setText("退出");
quitmn.add(getShutupmn());
}
return quitmn;
}
/**
* @return 帮助
*/
protected JMenu getHelpmn() {
if (helpmn == null) {
helpmn = new JMenu();
helpmn.setText("帮助");
helpmn.add(getVersionmn());
}
return helpmn;
}
/**
* @return 面板
*/
protected JPanel getPanel() {
if (panel == null) {
panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(getSendbtn(), BorderLayout.EAST);
panel.add(getSendtf());
}
return panel;
}
/**
* @return 发送
*/
protected JButton getSendbtn() {
if (sendbtn == null) {
sendbtn = new JButton();
sendbtn.addActionListener(this);
sendbtn.setText("发送");
}
return sendbtn;
}
/**
* @return 发送文本框
*/
protected JTextField getSendtf() {
if (sendtf == null) {
sendtf = new JTextField();
}
return sendtf;
}
@Override
public void run() {
// TODO Auto-generated method stub
while (runnable) {
try {
// 从输入流读取数据,然后加至msgView
ta.append(reader.readLine().trim() + "\n");
} catch (IOException ie) {
// tauser.append("服务器已关闭,请重新连接!" + "\n");
// tauser.append("");
}
}
tauser.append("服务器已关闭,请重新连接!" + "\n");
}
/**
* @return 姓名文本框
*/
protected JTextField getNametf() {
if (nametf == null) {
nametf = new JTextField();
}
return nametf;
}
/**
* @return 菜单新建连接
*/
protected JMenuItem getConnectmn() {
if (connectmn == null) {
connectmn = new JMenuItem();
connectmn.addActionListener(this);
connectmn.setText("新建连接");
}
return connectmn;
}
/**
* @return 信息输入文本框
*/
protected JTextArea getTa() {
if (ta == null) {
ta = new JTextArea();
ta.setBackground(new Color(250, 240, 230));
ta.setEditable(false);
}
return ta;
}
/**
* @return 菜单关闭
*/
protected JMenuItem getShutupmn() {
if (shutupmn == null) {
shutupmn = new JMenuItem();
shutupmn.addActionListener(this);
shutupmn.setText("关闭");
}
return shutupmn;
}
/**
* @return 菜单版本
*/
protected JMenuItem getVersionmn() {
if (versionmn == null) {
versionmn = new JMenuItem();
versionmn.addActionListener(this);
versionmn.setText("版本");
}
return versionmn;
}
/**
* @return 消息面板
*/
protected JScrollPane getViewpn() {
if (viewpn == null) {
viewpn = new JScrollPane();
viewpn.setViewportView(getTa());
}
return viewpn;
}
/**
* @return 信息面板
*/
protected JScrollPane getUserpn() {
if (userpn == null) {
userpn = new JScrollPane();
userpn.setViewportView(getTauser());
}
return userpn;
}
/**
* @return 文本用户
*/
protected JTextArea getTauser() {
if (tauser == null) {
tauser = new JTextArea();
tauser.setEditable(false);
}
return tauser;
}
}
3.版本
public class Version extends JFrame implements ActionListener {
private JLabel version10betaLabel;
/**
* 版本框架
*
* @param args
*/
public static void main(String args[]) {
Version frame = new Version();
frame.setVisible(true);
}
/**
* Create the frame
*/
public Version() {
super();
setBounds(60, 60, 210, 198);
initialize();
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
private void initialize() {
getContentPane().add(getVersion10betaLabel(), BorderLayout.CENTER);
}
/**
* @return
*/
protected JLabel getVersion10betaLabel() {
if (version10betaLabel == null) {
version10betaLabel = new JLabel();
version10betaLabel.setText("Version:1.0Beta 设计者:mhm");
}
return version10betaLabel;
}
}