前言

  大家看我CSFramework博文哗啦啦一蹴而就的写下来,但并不是那么顺利写下来的,我们知道越是底层的的东西就一定要进行单元测试,测试这部分功能是否符合要求,其实在写CSFramework的时候我就拿聊天室作为单元测试测试每一部分的功能性是否可行。应该说CSFramework框架是和聊天室一起磕磕绊绊,修修补补写出来的,边测试聊天室边修改框架内容,相辅相成才出来的CSFramework框架的。
  这篇博文旨在熟练掌握我们的CSFramework框架。

聊天室Project

首先来看聊天室工程整体做下来其内容是怎么分配的吧,主要是看包之间的名字和对应的关系。其中用到了MVC模式(高内聚,低耦合)和以前写好的工具类。

聊天室服务器工程大致

java cs应用框架选择 java做cs客户端框架_java cs应用框架选择


聊天室客户端工程大致

java cs应用框架选择 java做cs客户端框架_java_02

聊天室服务器view模块

服务器的界面是一个很简单的控制台。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;

import com.mec.csframework.core.IListener;
import com.mec.csframework.core.Server;
import com.mec.util.FrameIsNull;
import com.mec.util.IMecView;
import com.mec.util.ViewTool;

public class ServerView implements IMecView, IListener {

	private Server server;
	private JFrame jfrmMain;
	private Container container;
	
	private JTextField jtxfCommand;
	private JTextArea jtxaMessage;
	
	public ServerView() {
		server = new Server();
		server.addListener(this);
		initView();
	}
	
	public void setServerConfig(String path) {
		server.initConfig(path);
	}
	
	public void setServerConfigAbsolutePath(String path) {
		server.initConfigAbsolutePath(path);
	}
	
	@Override
	public void init() {

		jfrmMain = new JFrame("服务器测试");
		jfrmMain.setSize(800, 700);
		jfrmMain.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		jfrmMain.setLocationRelativeTo(null);
		container = jfrmMain.getContentPane();
		container.setLayout(new BorderLayout());
		
		JLabel jlblTopic = new JLabel("一个用于测试的服务器监控界面", JLabel.CENTER);
		jlblTopic.setFont(topicFont);
		jlblTopic.setForeground(topicColor);
		container.add(jlblTopic, BorderLayout.NORTH);
		
		JPanel jpnlFooter = new JPanel(new FlowLayout());
		container.add(jpnlFooter, BorderLayout.SOUTH);
		
		JLabel jlblCommand = new JLabel("命令");
		jlblCommand.setFont(normalFont);
		jpnlFooter.add(jlblCommand);
		
		jtxfCommand = new JTextField(40);
		jtxfCommand.setFont(normalFont);
		jpnlFooter.add(jtxfCommand);
		
		JPanel jpnlLeft = new JPanel();
		JLabel jlblLeft = new JLabel("    ");
		jlblLeft.setFont(normalFont);
		jpnlLeft.add(jlblLeft);
		container.add(jpnlLeft, BorderLayout.WEST);
		
		JPanel jpnlRight = new JPanel();
		JLabel jlblRight = new JLabel("    ");
		jlblRight.setFont(normalFont);
		jpnlRight.add(jlblRight);
		container.add(jpnlRight, BorderLayout.EAST);
		
		jtxaMessage = new JTextArea();
		jtxaMessage.setEditable(false);
		jtxaMessage.setFocusable(false);
		jtxaMessage.setFont(normalFont);
		JScrollPane jscpMessage = new JScrollPane(jtxaMessage);
		TitledBorder ttbdMessage = new TitledBorder("系统消息");
		ttbdMessage.setTitleFont(normalFont);
		ttbdMessage.setTitleColor(Color.red);
		ttbdMessage.setTitlePosition(TitledBorder.ABOVE_TOP);
		ttbdMessage.setTitleJustification(TitledBorder.CENTER);
		jscpMessage.setBorder(ttbdMessage);
		container.add(jscpMessage, BorderLayout.CENTER);
		
	}

	@Override
	public void reinit() {

	}

	@Override
	public void dealEvent() {

		jfrmMain.addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				closeView();
			}
			
		});
		
		jtxfCommand.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				String command = jtxfCommand.getText().trim();
				if (command.length() <= 0) {
					return;
				}
				dealCommand(command);
				jtxfCommand.setText("");
			}
		});
	}

	@Override
	public JFrame getJFrame() {
		return jfrmMain;
	}

	@Override
	public void processMessage(String message) {
		jtxaMessage.append(message);
		jtxaMessage.append("\n");
		jtxaMessage.setCaretPosition(jtxaMessage.getText().length());
	}
	
	private void closeView() {
		if (server.isStartUp()) {
			ViewTool.showWarnning(jfrmMain, "服务器尚未宕机,请先宕机再关闭");
			return;
		}
		try {
			exitView();
		} catch (FrameIsNull e) {
			e.printStackTrace();
		}
	}
	
	private void dealCommand(String command) {
		if (command.equalsIgnoreCase("st")) {
			try {
				server.startUp();
			} catch (IOException e) {
				ViewTool.showError(jfrmMain, e.getMessage());
			}
		} else if (command.equalsIgnoreCase("sd")) {
			server.shutDown();
		} else if (command.equalsIgnoreCase("fd")) {
			server.forceDown();
		} else if (command.equalsIgnoreCase("x")) {
			closeView();
		}
		
	}
	
}

聊天室服务器model模块

就是聊天室用户的model,描述一个用户的所有信息

public class UserInfo {

	private String userId;
	private String password;
	private String nick;
	private String netId;
	
	public UserInfo() {
		
	}

	public String getUserId() {
		return userId;
	}

	public UserInfo setUserId(String userId) {
		this.userId = userId;
		return this;
	}

	public String getPassword() {
		return password;
	}

	public UserInfo setPassword(String password) {
		this.password = password;
		return this;
	}

	public String getNick() {
		return nick;
	}

	public UserInfo setNick(String nick) {
		this.nick = nick;
		return this;
	}

	public String getNetId() {
		return netId;
	}

	public UserInfo setNetId(String netId) {
		this.netId = netId;
		return this;
	}

	@Override
	public String toString() {
		return nick;
	}
		
}

聊天室服务器controller模块

DAO模块:对于数据bai库中的数据做增删改查等操作的代码

import com.mec.chatroom.server.model.UserInfo;
import com.mec.orm.core.MecDataBase;

public class UserDao {

	public UserDao() {
	}
	
	public UserInfo getUserById(String userId) {
		
		MecDataBase mecDataBase = MecDataBase.newInstance();
		UserInfo user = mecDataBase.getRecordByKey(UserInfo.class, userId);
		
		return user;
	}
}

service模块:服务层,将几种操作封装起来,提供不同的数据库的服务。

import com.mec.chatroom.server.model.UserInfo;
import com.mec.chatroom.server.userDao.UserDao;

public class UserService {

	private UserDao userDao;
	
	public UserService() {
		userDao = new UserDao();
	}
	
	public UserInfo checkUser(String userId, String password) {
		UserInfo user = new UserInfo();		
		if (userId.equals("") || password.equals("")) {
			return null;
		}

		user = userDao.getUserById(userId);
		if (user == null) {
			return null;
		}
		if (user.getPassword() == null) {
			return null;
		}
		String hPassword = String.valueOf(user.getPassword().hashCode());
		if (!hPassword.equals(password)) {
			return null;
		}
		return user;
	}
	
}

对与用户的操作响应类

import com.mec.chatroom.server.model.UserInfo;
import com.mec.chatroom.server.userService.UserService;

public class UserAction {

	private UserService userService;
	
	public UserAction() {
		userService = new UserService();
	}
	
	public UserInfo userLogin(String userId, String password) {
		UserInfo user = userService.checkUser(userId, password);

		return user;
	}
	
}

聊天室服务器需要的配置文件

serverConfig.properties基本设置

port=54188
maxClientCount=200

connection.properties数据库连接相关配置

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mec_chat_room?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
user=xxxx
password=123456

ClassTableMapping.xml数据库表和类对应映射关系

<?xml version="1.0" encoding="UTF-8"?>
<mappings>
	<mapping className = "com.mec.chatroom.server.model.UserInfo" tableName = "sys_user_info" key = "userId">
		<column propertyName = "userId" columnName = "user_id"> </column>
		<column propertyName = "netId" columnName = "net_id"> </column>
	</mapping>
</mappings>

actionMapping.xml响应动作配置

<?xml version="1.0" encoding="UTF-8"?>
<actions>
	<action actionName= "userLogin" className= "com.mec.chatroom.server.userAction.UserAction" methodName= "userLogin">
		<parameter name= "userId" type= "string"></parameter>
		<parameter name= "password" type= "string"></parameter>
	</action>
</actions>

需要的jar包

java cs应用框架选择 java做cs客户端框架_聊天室_03

聊天室客户端view模块

客户端的界面有三个分别是连接服务器界面(ConnectToServer),登陆界面(UserLoginView),和聊天室界面(ChatRoomView)。

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.UnknownHostException;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

import com.mec.csframework.core.Client;
import com.mec.csframework.core.ClientActionAdapter;
import com.mec.csframework.core.ClientActionNotSetException;
import com.mec.util.FrameIsNull;
import com.mec.util.IMecView;
import com.mec.util.ViewTool;

public class ConnectToServer implements IMecView {

	private Client client;
	private JFrame jfrmMain;
	private Container container;
	
	private JLabel jlblMessage;
	private int count;
	
	public ConnectToServer() throws UnknownHostException {
		count = 1;
		client = new Client();
		client.setClientAction(new ConnectToServerAction());
		initView();
	}
	
	public void setClientConfig(String path) {
		client.initConfig(path);
	}
	
	public void setClientConfigAbsolutePath(String path) {
		client.initConfigAbsolutePath(path);
	}
	
	@Override
	public void init() {
		jfrmMain = new JFrame("连接服务器");
		jfrmMain.setSize(400, 200);
		jfrmMain.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		jfrmMain.setLocationRelativeTo(null);
		jfrmMain.setResizable(false);
		
		container = new Container();
		container = jfrmMain.getContentPane();
		container.setLayout(new BorderLayout());
		
		JLabel jlblTopic = new JLabel("连接服务器", JLabel.CENTER);
		jlblTopic.setFont(topicFont);
		jlblTopic.setForeground(topicColor);
		container.add(jlblTopic, BorderLayout.NORTH);
		
		jlblMessage = new JLabel("正在进行第"+ count++ +"次连接....", JLabel.CENTER);
		jlblMessage.setFont(normalFont);
		container.add(jlblMessage, BorderLayout.CENTER);
	}

	@Override
	public void reinit() {

		boolean connectOk = false;
		while (!connectOk) {
			try {
				connectOk = client.connectToServer();
				if (connectOk == false) {
					int choice = ViewTool.getChoice(jfrmMain, "连接失败,是否继续连接", JOptionPane.YES_NO_OPTION);
					if (choice == JOptionPane.YES_OPTION) {
						jlblMessage.setText("正在进行第"+ count++ +"次连接....");
						continue;
					} else {
						closeView();
						break;
					}
				}
			} catch (ClientActionNotSetException e) {
				ViewTool.showError(jfrmMain, e.getMessage());
				closeView();
				break;
			}
		}
	}

	@Override
	public void dealEvent() {

		jfrmMain.addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				closeView();
			}
			
		});
	}

	@Override
	public JFrame getJFrame() {
		return jfrmMain;
	}

	private void closeView() {
		try {
			exitView();
		} catch (FrameIsNull e) {
			e.printStackTrace();
		}
	}
	
	class ConnectToServerAction extends ClientActionAdapter {

		@Override
		public void serverFull() {
			ViewTool.showMessage(jfrmMain, "服务器已经爆满,请稍后尝试");
			closeView();
		}

		@Override
		public void afterOnline() {
			try {
				new UserLoginView(client).showView();
			} catch (FrameIsNull e) {
				e.printStackTrace();
			} finally {
				closeView();	
			}
		}
		
		
	}
	
}
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;

import com.mec.chatroom.client.model.UserInfo;
import com.mec.csframework.action.ActionBeanFactory;
import com.mec.csframework.core.Client;
import com.mec.csframework.core.ClientActionAdapter;
import com.mec.util.ArgumentMaker;
import com.mec.util.FrameIsNull;
import com.mec.util.IMecView;
import com.mec.util.ViewTool;

public class UserLoginView implements IMecView {

	private JFrame jfrmUserLogin;
	private Client client;
	
	private JTextField jtxtUserName;
	private JPasswordField jpswPassword;
	private JLabel jlblRegistry;
	private JButton jbtnLogin;
	
	public UserLoginView(Client client) {
		this.client = client;
		this.client.setClientAction(new UserLoginAction());
		initView();
	}
	
	@Override
	public void init() {

		jfrmUserLogin = new JFrame("用户登录");
		jfrmUserLogin.setLayout(new BorderLayout());
		jfrmUserLogin.setSize(300, 200);
		jfrmUserLogin.setLocationRelativeTo(null);
		jfrmUserLogin.setResizable(false);
		jfrmUserLogin.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		
		JLabel jlblTopic = new JLabel("用户登录", JLabel.CENTER);
		jlblTopic.setFont(topicFont);
		jlblTopic.setForeground(topicColor);
		jfrmUserLogin.add(jlblTopic, BorderLayout.NORTH);
		
		JPanel jpnlContxt = new JPanel();
		jpnlContxt.setLayout(null);
		jfrmUserLogin.add(jpnlContxt, BorderLayout.CENTER);
		
		int normalSize = normalFont.getSize();
		int left = 8*PADDING;
		int top = 5*PADDING;
		int lblWidth = 2 * normalSize;
		int lblHeight = normalSize + 2;
		JLabel jlblUserName = new JLabel("账号");
		jlblUserName.setFont(normalFont);
		jlblUserName.setBounds(left, top, lblWidth, lblHeight);
		jpnlContxt.add(jlblUserName);
		
		jtxtUserName = new JTextField();
		jtxtUserName.setFont(normalFont);
		jtxtUserName.setBounds(left + lblWidth + PADDING, top, 11*normalSize, lblHeight+4);
		jpnlContxt.add(jtxtUserName);
		
		top += lblHeight + 3*PADDING;
		JLabel jlblPassword = new JLabel("密码");
		jlblPassword.setFont(normalFont);
		jlblPassword.setBounds(left, top, lblWidth, lblHeight);
		jpnlContxt.add(jlblPassword);
		
		jpswPassword = new JPasswordField();
		jpswPassword.setFont(normalFont);
		jpswPassword.setBounds(left + lblWidth + PADDING, top, 11*normalSize, lblHeight + 4);
		jpnlContxt.add(jpswPassword);
		
		JPanel jpnlButton = new JPanel();
		jpnlButton.setLayout(new FlowLayout());
		jfrmUserLogin.add(jpnlButton, BorderLayout.SOUTH);
		
		jlblRegistry = new JLabel("注册");
		jlblRegistry.setFont(new Font("微软雅黑", Font.BOLD, 16));
		jlblRegistry.setForeground(new Color(5,98,44));
		jlblRegistry.setCursor(handCursor);
		jpnlButton.add(jlblRegistry);
		
		JLabel jlblBlank = new JLabel("      ");
		jlblBlank.setFont(normalFont);
		jpnlButton.add(jlblBlank);
		
		jbtnLogin = new JButton("登陆");
		jbtnLogin.setFont(smallFont);
		jpnlButton.add(jbtnLogin);		
	}

	@Override
	public void reinit() {

	}

	@Override
	public void dealEvent() {

		jbtnLogin.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				String action = "userLogin";
				ActionBeanFactory.setObject(action, UserLoginView.this);
				String userId = jtxtUserName.getText();
				String mPassword = new String(jpswPassword.getPassword());
				String password = String.valueOf(mPassword.hashCode());
				client.sendRequest(action, new ArgumentMaker().add("userId", userId)
						.add("password", password).toString());
			}
		});
		
		jfrmUserLogin.addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				client.offline();
			}
			
		});
		
		jtxtUserName.addFocusListener(new FocusAdapter() {

			@Override
			public void focusGained(FocusEvent e) {
				jtxtUserName.selectAll();
			}
			
		});
		
		jtxtUserName.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {

				jpswPassword.requestFocus();
			}
		});
		
		jpswPassword.addFocusListener(new FocusAdapter() {

			@Override
			public void focusGained(FocusEvent e) {
				jpswPassword.setText("");
			}	
		});
		
		
	}

	@Override
	public JFrame getJFrame() {
		return jfrmUserLogin;
	}

	private void closeView() {
		try {
			exitView();
		} catch (FrameIsNull e) {
			e.printStackTrace();
		}
	}
	
	public void afterClickedLogin(UserInfo user) {
		if (user == null) {
			ViewTool.showError(jfrmUserLogin, "用户账号或密码错误");
			jpswPassword.setText("");
			jtxtUserName.selectAll();
			jtxtUserName.requestFocus();
			return;
		}
			user.setNetId(client.getId());
			try {
				new ChatRoomView(user, client).showView();
			} catch (FrameIsNull e) {
				e.printStackTrace();
			}
			closeView();
	}
	
	class UserLoginAction extends ClientActionAdapter {
		@Override
		public void serverForcedown() {
			ViewTool.showError(jfrmUserLogin, "服务器强制宕机,服务停止");
			closeView();
		}

		@Override
		public void serverAbnormalDrop() {
			ViewTool.showError(jfrmUserLogin, "服务器异常,服务停止");
			closeView();
		}

		@Override
		public boolean confirmOffline() {
			int choice = ViewTool.getChoice(jfrmUserLogin, "是否确定下线?", JOptionPane.YES_NO_OPTION);
			return choice == JOptionPane.YES_OPTION;
		}

		@Override
		public void afterOffline() {
			closeView();
		}
		
	}
	
}
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import com.mec.chatroom.client.model.UserInfo;
import com.mec.chatroom.core.AppMessage;
import com.mec.chatroom.core.EAppCommand;
import com.mec.chatroom.core.FriendPool;
import com.mec.csframework.core.Client;
import com.mec.csframework.core.ClientActionAdapter;
import com.mec.util.ArgumentMaker;
import com.mec.util.FrameIsNull;
import com.mec.util.IMecView;
import com.mec.util.ViewTool;

public class ChatRoomView implements IMecView {

	private JFrame jfrmChatRoomView;
	private UserInfo me;
	private Client client;
	
	private UserInfo allFriend;
	private final String ALL = "00000000";
	private FriendPool friendPool;
	
	private JTextArea jtatSystemMessage;
	private JTextArea jtatChat;
	private DefaultListModel<UserInfo> dlmFriendList;
	private JList<UserInfo> jlstFriendList;
	private JLabel jlblCurrentFriend;
	private JTextField jtxtSpeakContext;
	private JButton jbtnSend;
	
	public ChatRoomView(UserInfo userInfo, Client client) {
		this.me = userInfo;
		this.client = client;
		this.client.setClientAction(new ChatRoomAction());
		friendPool = new FriendPool();
		this.allFriend = new UserInfo();
		allFriend.setUserId(ALL);
		allFriend.setNick("所有人");
		initView();
	}
	
	@Override
	public void init() {
		jfrmChatRoomView = new JFrame("聊天室");
		jfrmChatRoomView.setDefaultCloseOperation(
				JFrame.DO_NOTHING_ON_CLOSE);
		jfrmChatRoomView.setSize(800, 600);
		jfrmChatRoomView.setLocationRelativeTo(null);
		
		Container container = jfrmChatRoomView.getContentPane();
		container.setLayout(new BorderLayout());
		
		JLabel jlblTopic = new JLabel("聊天室", JLabel.CENTER);
		jlblTopic.setFont(topicFont);
		jlblTopic.setForeground(topicColor);
		container.add(jlblTopic, BorderLayout.NORTH);
		
		jtatSystemMessage = new JTextArea(0, 15);
		jtatSystemMessage.setFont(normalFont);
		JScrollPane jscpSystemMessage = 
				new JScrollPane(jtatSystemMessage);
		jscpSystemMessage.setPreferredSize(new Dimension(200, 400));
		TitledBorder ttdbSystemMessage = new TitledBorder(" 系统信息 ");
		ttdbSystemMessage.setTitleFont(normalFont);
		ttdbSystemMessage.setTitlePosition(TitledBorder.ABOVE_TOP);
		ttdbSystemMessage.setTitleJustification(TitledBorder.CENTER);
		jscpSystemMessage.setBorder(ttdbSystemMessage);
		container.add(jscpSystemMessage, BorderLayout.EAST);
		
		jtatChat = new JTextArea();
		jtatChat.setFont(normalFont);
		JScrollPane jscpChat = new JScrollPane(jtatChat);
		TitledBorder ttdbChat = new TitledBorder(" 聊天信息 ");
		ttdbChat.setTitleFont(normalFont);
		ttdbChat.setTitleColor(Color.RED);
		ttdbChat.setTitlePosition(TitledBorder.ABOVE_TOP);
		ttdbChat.setTitleJustification(TitledBorder.CENTER);
		jscpChat.setBorder(ttdbChat);
		container.add(jscpChat, BorderLayout.CENTER);
		
		JPanel jpnlFriends = new JPanel(new BorderLayout());
		container.add(jpnlFriends, BorderLayout.WEST);
		
		JPanel jpnlWelcome = new JPanel(new GridLayout(0, 1));
		jpnlFriends.add(jpnlWelcome, BorderLayout.NORTH);
		
		JLabel jlblWelcome = new JLabel(" 欢迎 ", JLabel.CENTER);
		jlblWelcome.setFont(normalFont);
		jlblWelcome.setForeground(Color.RED);
		jpnlWelcome.add(jlblWelcome);
		
		JLabel jlblUserSelf = new JLabel(
				me.getNick(), JLabel.CENTER);
		jlblUserSelf.setFont(normalFont);
		jlblUserSelf.setForeground(topicColor);
		jpnlWelcome.add(jlblUserSelf);
		
		dlmFriendList = new DefaultListModel<>();
		jlstFriendList = new JList<>(dlmFriendList);
		jlstFriendList.setFont(normalFont);
		JScrollPane jscpFriendList = 
				new JScrollPane(jlstFriendList);
		jscpFriendList.setPreferredSize(new Dimension(200, 300));
		TitledBorder ttdbFriendList = 
				new TitledBorder(" 好友列表 ");
		ttdbFriendList.setTitleFont(normalFont);
		ttdbFriendList.setTitlePosition(TitledBorder.TOP);
		ttdbFriendList.setTitleJustification(
				TitledBorder.CENTER);
		jscpFriendList.setBorder(ttdbFriendList);
		jpnlFriends.add(jscpFriendList, BorderLayout.CENTER);
		
		JPanel jpnlCurFriend = 
				new JPanel(new GridLayout(0, 1));
		jpnlFriends.add(jpnlCurFriend, BorderLayout.SOUTH);
		
		JLabel jlblCurrentFriendCaption = 
				new JLabel("当前好友", 0);
		jlblCurrentFriendCaption.setFont(normalFont);
		jpnlCurFriend.add(jlblCurrentFriendCaption);
		
		jlblCurrentFriend = new JLabel("", JLabel.CENTER);
		jlblCurrentFriend.setFont(normalFont);
		jlblCurrentFriend.setForeground(Color.RED);
		jpnlCurFriend.add(jlblCurrentFriend);
		
		JPanel jpnlFooter = new JPanel();
		container.add(jpnlFooter, BorderLayout.SOUTH);
		
		JLabel jlblYousay = new JLabel("你说 ");
		jlblYousay.setFont(normalFont);
		jpnlFooter.add(jlblYousay);
		
		jtxtSpeakContext = new JTextField(40);
		jtxtSpeakContext.setFont(normalFont);
		jpnlFooter.add(jtxtSpeakContext);
		
		jbtnSend = new JButton("发送");
		jbtnSend.setFont(smallFont);
		jpnlFooter.add(jbtnSend);
	}

	@Override
	public void reinit() {
		dlmFriendList.addElement(allFriend);
		jlstFriendList.setSelectedIndex(0);
		
		AppMessage appMessage = new AppMessage();
		appMessage.setCommand(EAppCommand.I_AM_COMING)
		.setParameter(ArgumentMaker.gson.toJson(this.me));
		client.toAll(ArgumentMaker.gson.toJson(appMessage));
		
		jtatChat.setEditable(false);
		jtatChat.setFocusable(false);
		
		jtatSystemMessage.setEditable(false);
		jtatSystemMessage.setFocusable(false);
		jtxtSpeakContext.requestFocus();
	}

	@Override
	public void dealEvent() {
		jfrmChatRoomView.addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				client.offline();
			}	
		});
		
		jlstFriendList.addListSelectionListener(new ListSelectionListener() {
			
			@Override
			public void valueChanged(ListSelectionEvent e) {
				if (e.getValueIsAdjusting() == false) {
					UserInfo user = jlstFriendList.getSelectedValue();
					jlblCurrentFriend.setText(user.getNick());
				}
			}
		});
		
		jbtnSend.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				UserInfo sendUser = jlstFriendList.getSelectedValue();
				String message = jtxtSpeakContext.getText();
				if (message.length() <=0) {
					ViewTool.showError(jfrmChatRoomView, "不能发送空的消息");
					return;
				}
				jtxtSpeakContext.setText("");
				if (sendUser.getUserId().equals(ALL)) {
					groupSend(message);
				} else {
					singleSend(message, sendUser);
				}
			}
		});
		
	}

	private void singleSend(String message, UserInfo targetUser) {
		showChatMessage("你对" + targetUser.getNick() + "悄悄地说:" + "[" + message + "]");
		AppMessage appMessage = new AppMessage();
		ArgumentMaker argumentMaker = new ArgumentMaker();
		appMessage.setCommand(EAppCommand.TO_ONE)
		.setParameter(argumentMaker.add("from", me).add("content", message).toString());
		client.toOne(targetUser.getNetId(), ArgumentMaker.gson.toJson(appMessage));
	}
	
	private void groupSend(String message) {
		showChatMessage("你向大家说:["	+ message + "]");
		AppMessage appMessage = new AppMessage();
		ArgumentMaker argumentMaker = new ArgumentMaker();
		appMessage.setCommand(EAppCommand.TO_ALL).setParameter(argumentMaker.add("from", me)
				.add("content", message).toString());
		client.toAll(ArgumentMaker.gson.toJson(appMessage));
	}
	
	@Override
	public JFrame getJFrame() {
		return jfrmChatRoomView;
	}

	
	private void showChatMessage(String message) {
		jtatChat.append(message + "\n");
		jtatChat.setCaretPosition(jtatChat.getText().length());
	}
	
	private void showSystemMessage(String message) {
		jtatSystemMessage.append(message + "\n");
		jtatSystemMessage.setCaretPosition(jtatSystemMessage.getText().length());
	}
	
	private void closeView() {
			try {
				exitView();
			} catch (FrameIsNull e) {
				e.printStackTrace();
			}		
	}
	
	class ChatRoomAction extends ClientActionAdapter {
	
		@Override
		public void serverForcedown() {
			ViewTool.showError(jfrmChatRoomView, "服务强制宕机,服务停止");
			closeView();
		}

		@Override
		public void serverAbnormalDrop() {
			ViewTool.showError(jfrmChatRoomView, "服务器异常,服务停止");
			closeView();
		}

		@Override
		public boolean confirmOffline() {
			int choice = ViewTool.getChoice(jfrmChatRoomView, "是否确认下线?", JOptionPane.YES_NO_OPTION);
			return choice == JOptionPane.YES_OPTION;
		}

		@Override
		public void beforeOffline() {
			AppMessage appMessage = new AppMessage();
			appMessage.setCommand(EAppCommand.I_AM_GONE).setParameter(ArgumentMaker.gson.toJson(me));
			client.toAll(ArgumentMaker.gson.toJson(appMessage));
		}

		@Override
		public void afterOffline() {
			closeView();
		}

		@Override
		public void otherClientDrop(String id) {
			UserInfo user = null;
			for (int i = 1; i < dlmFriendList.size(); i++) {
				user = dlmFriendList.get(i);
				if (user.getNetId().equals(id)) {
					break;
				}
			}
			showSystemMessage("你的好友[" + user.getNick()	+ "]异常离开聊天室");
			if (jlstFriendList.getSelectedValue().equals(user)) {
				ViewTool.showError(jfrmChatRoomView, "你的聊天对象已经下线");
				jlstFriendList.setSelectedIndex(0);
				jlblCurrentFriend.setText(allFriend.toString());
			}
			dlmFriendList.removeElement(user);
		}

		@Override
		public void dealToOne(String sourceId, String message) {
			AppMessage appMessage = ArgumentMaker.gson.fromJson(message, AppMessage.class);
			EAppCommand command = appMessage.getCommand();
			String parameter = appMessage.getParameter();
			
			switch (command) {
			case I_AM_HERE:
				UserInfo user = ArgumentMaker.gson.fromJson(parameter, UserInfo.class);
				dlmFriendList.addElement(user);
				friendPool.addFriend(user);
				break;
			case TO_ONE:
				ArgumentMaker argumentMaker = new ArgumentMaker(parameter);
				String spokenMessage = (String) argumentMaker.getArgumentByName("content", String.class);
				UserInfo friend = (UserInfo) argumentMaker.getArgumentByName("from", UserInfo.class);
				showChatMessage(friend.getNick() + "悄悄地对你说:" + "["	 + spokenMessage +"]");
				break;
			default:
				break;
			}
		}

		@Override
		public void dealToAll(String sourceId, String message) {
			AppMessage appMessage = ArgumentMaker.gson.fromJson(message, AppMessage.class);
			EAppCommand command = appMessage.getCommand();
			String parameter = appMessage.getParameter();
			switch (command) {
			case I_AM_COMING:
				UserInfo newUser = ArgumentMaker.gson.fromJson(parameter, UserInfo.class);
				showSystemMessage("你的好友[" + newUser.getNick()	+ "]已经进入聊天室");
				friendPool.addFriend(newUser);
				dlmFriendList.addElement(newUser);
				
				AppMessage toNewUser = new AppMessage();
				toNewUser.setCommand(EAppCommand.I_AM_HERE).setParameter(ArgumentMaker.gson.toJson(me));
				client.toOne(newUser.getNetId(), ArgumentMaker.gson.toJson(toNewUser));
				break;
			case I_AM_GONE:
				UserInfo exitUser = ArgumentMaker.gson.fromJson(parameter, UserInfo.class);
				showSystemMessage("你的好友[" + exitUser.getNick()	+ "]已经离开聊天室");
				if (jlstFriendList.getSelectedValue().equals(friendPool.getUserByUserId(exitUser.getUserId()))) {
					ViewTool.showError(jfrmChatRoomView, "你的聊天对象已经下线");
					jlstFriendList.setSelectedIndex(0);
					jlblCurrentFriend.setText(allFriend.toString());
				}
				dlmFriendList.removeElement(friendPool.getUserByUserId(exitUser.getUserId()));
				break;
			case TO_ALL:
				ArgumentMaker argumentMaker = new ArgumentMaker(parameter);
				String spokenMessage = (String) argumentMaker.getArgumentByName("content", String.class);
				UserInfo friend = (UserInfo) argumentMaker.getArgumentByName("from", UserInfo.class);
				showChatMessage(friend.getNick() + "对大家说:" + spokenMessage);
				break;
			default:
				break;
			}
		}
	
	}
	
}

聊天室客户端model模块

和服务器一样,描述一个用户所拥有的信息

public class UserInfo {

	private String userId;
	private String password;
	private String nick;
	private String netId;
	
	public UserInfo() {
		
	}

	public String getUserId() {
		return userId;
	}

	public UserInfo setUserId(String userId) {
		this.userId = userId;
		return this;
	}

	public String getPassword() {
		return password;
	}

	public UserInfo setPassword(String password) {
		this.password = password;
		return this;
	}

	public String getNick() {
		return nick;
	}

	public UserInfo setNick(String nick) {
		this.nick = nick;
		return this;
	}

	public String getNetId() {
		return netId;
	}

	public UserInfo setNetId(String netId) {
		this.netId = netId;
		return this;
	}

	@Override
	public String toString() {
		return nick;
	}
		
}

聊天室客户端controller模块

是聊天室发送消息的规范和好友列表的表示。聊天消息当然不是底层我们CSFramework的消息规范了,它在底层,已经成为我们的工具了,被屏蔽了,看不见,因此我们需要APP层的消息规范。

public class AppMessage {

	private EAppCommand command;
	private String parameter;
	
	public AppMessage() {
		
	}

	public EAppCommand getCommand() {
		return command;
	}

	public AppMessage setCommand(EAppCommand command) {
		this.command = command;
		return this;
	}

	public String getParameter() {
		return parameter;
	}

	public AppMessage setParameter(String parameter) {
		this.parameter = parameter;
		return this;
	}
	
}
public enum EAppCommand {

	I_AM_COMING,
	I_AM_HERE,
	I_AM_GONE,
	
	TO_ONE,
	TO_ALL,
}
import java.util.HashMap;
import java.util.Map;

import com.mec.chatroom.client.model.UserInfo;

public class FriendPool {

	private Map<String, UserInfo> friendPool;
	
	public FriendPool() {
		friendPool = new HashMap<>();
	}
	
	public void addFriend(UserInfo user) {
		friendPool.put(user.getUserId(), user);
	}
	
	public boolean removeFriend(UserInfo user) {
		return friendPool.remove(user.getUserId()) != null;
	}
	
	public UserInfo getUserByUserId(String userId) {
		return friendPool.get(userId);
	}
}

聊天室客户端需要的配置文件

clientConfig.properties基本配置

serverIp=127.0.0.1
port=54188

clientAction.xml用户动作响应需要和服务器那边保持一致同步,是对等的,以后扩展开发增加新响应两边都要改

<?xml version="1.0" encoding="UTF-8"?>
<actions>
	<action actionName= "userLogin" className= "com.mec.chatroom.client.view.UserLoginView" methodName= "afterClickedLogin">
		<parameter name= "user" type= "com.mec.chatroom.client.model.UserInfo"></parameter>
	</action>
</actions>

需要的jar包

java cs应用框架选择 java做cs客户端框架_服务器_04

测试

import com.mec.chatroom.server.view.ServerView;
import com.mec.csframework.action.ActionBeanFactory;

public class ServerMain {

	public static void main(String[] args) {

		try {
			ActionBeanFactory.scanActionMappingXMLPath();
			ServerView sv = new ServerView();
			sv.setServerConfig("/serverConfig.properties");
			sv.showView();
		} catch (Exception e1) {
			e1.printStackTrace();
		}
	}

}
import java.net.UnknownHostException;

import com.mec.chatroom.client.view.ConnectToServer;
import com.mec.csframework.action.ActionBeanFactory;
import com.mec.util.FrameIsNull;

public class ClientMain {

	public static void main(String[] args) {

		try {
			ActionBeanFactory.scanActionMappingXMLPath("/clientAction.xml");
			ConnectToServer cts = new ConnectToServer();
			cts.setClientConfig("/clientConfig.properties");
			cts.showView();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (FrameIsNull e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

java cs应用框架选择 java做cs客户端框架_java cs应用框架选择_05


java cs应用框架选择 java做cs客户端框架_java_06


java cs应用框架选择 java做cs客户端框架_服务器_07


java cs应用框架选择 java做cs客户端框架_服务器_08

总结

  一个聊天室就做好了,虽然很简陋,但基本群聊、私聊和异常掉线等功能都能得到满足。将聊天室服务器工程和聊天室客户端工程分别打成jar包,就不像CSFramework是个框架了,已经是个聊天APP了。
需要注意/学习的两点:

  1. 聊天室APP可以再次进行二次开发,如果你觉得界面不好看了,或者需要更多按钮的响应了,就可以继续开发,但要协商好两边,就相当于版本升级过程
  2. CSFramework框架不是只能开发出聊天室,它还可以开发出棋牌室等网络APP。我们可以拿着 CSFramework给这个公司开发他们的聊天室,可以给另外一个公司开发他们的聊天室,还可以给其他公司开发棋牌室。对于开发来说这就是代码复用,这就是省钱。从这理解了框架重要性,就是为了避免写重复代码,加快开发效率。