我们每天都在使用着微信、QQ等聊天软件,但不知你是否有想过这些聊天软件是如何实现的?是否想过要制作一个属于自己的聊天室?

本篇博客将带你打造一个简单的属于自己的聊天室,将cmd作为聊天窗口,可通过内网,与周围的小伙伴相互通信,当然也可以挂到服务器上,实现通过外网的通信。同时还能通过服务端窗口对连入的用户进行管理。

先来看看我做的效果

这是服务器控制界面

输入端口号,点击启动,再打开cmd,输入telnet localhost 端口号,然后输入账号密码登陆

输入消息

下面就来讲讲如何实现的吧

首先我们需要先建立好用户的信息

UserInfo.java

  1. public class UserInfo {
  2. private String name;
  3. private String possward;
  4.  
  5. public String getName(){
  6. return this.name;
  7. }
  8. public void setName(String name) {
  9. this.name=name;
  10. }
  11.  
  12. public void setPwd(String possward) {
  13. this.possward=possward;
  14. }
  15. }

一个聊天室,我们可以将其分为服务端和客户端,而通信的简易过程如下图所示

对于客户端,我们需要做的是1、验证用户登陆信息。2、接收用户发送的信息并转发给目标用户

服务端目前则使用cmd进行

首先,我们先建立一个简易的储存账号密码的数据库

DaoTools.java

  1. public class DaoTools {
  2. //内存用户信息数据库
  3. private static Map<String,UserInfo>userDB=new HashMap();
  4.  
  5. public static boolean checkLogin(UserInfo user) {
  6. //验证用户名是否存在
  7. if(userDB.containsKey((user.getName()))){
  8. return true;
  9. }
  10. System.out.println("认证失败!:"+user.getName());
  11. return false;
  12. }
  13. //系统内部自动创建10个账号
  14. static {
  15. for(int i=0;i<10;i++) {
  16. UserInfo user=new UserInfo();
  17. user.setName("user"+i);
  18. user.setPwd("pwd"+i);
  19. userDB.put(user.getName(), user);
  20. }
  21. }
  22.  
  23. }

服务端服务器创建

ChatServer.java

  1. public class ChatServer extends Thread {
  2. private int port;// 端口
  3. private boolean running = false;// 服务器是否运行中的标记
  4. private ServerSocket sc;// 服务器对象
  5.  
  6. /*
  7. * 创建服务器对象时,必须传入端口号
  8. *
  9. * @param port:服务器所在端口号
  10. */
  11. public ChatServer(int port) {
  12. this.port = port;
  13. }
  14.  
  15. // 线程中启动服务器
  16. public void run() {
  17. setupServer();
  18. }
  19.  
  20. // 在指定端口上启动服务器
  21. public void setupServer() {
  22. try {
  23. ServerSocket sc = new ServerSocket(this.port);
  24. running = true;
  25. System.out.println("服务器创建成功:" + port);
  26. while (running) {
  27. Socket client = sc.accept();// 等待连结进入
  28. System.out.println("进入了一个客户机连接" + client.getRemoteSocketAddress());
  29. // 启动一个处理线程,去处理这个连结对象
  30. ServerThread ct = new ServerThread(client);
  31. ct.start();
  32. }
  33. } catch (IOException e) {
  34. // TODO Auto-generated catch block
  35. e.printStackTrace();
  36. }
  37. }
  38.  
  39. /*
  40. * 查询服务器是否在运行
  41. *
  42. * @return: true为运行中
  43. */
  44. public boolean isRunning() {
  45. return this.running;
  46. }
  47.  
  48. // 关闭服务器
  49. public void stopchatServer() {
  50. this.running = false;
  51. try {
  52. sc.close();
  53. } catch (Exception e) {}
  54. }
  55. }

验证用户登陆信息,创建服务器线程

ServerThread.java

  1. public class ServerThread extends Thread {
  2. private Socket client;//线程中处理的客户对象
  3. private OutputStream ous;//输出流对象
  4. private UserInfo user;//这个线程处理对象代表的用户的信息
  5. //创建对象时必须传入一个Socket对象
  6. public ServerThread(Socket client) {
  7. this.client=client;
  8. }
  9. //获取这个线程对象代表的用户对象
  10. public UserInfo getOwerUser() {
  11. return this.user;
  12. }
  13. public void run() {
  14. processSocket();
  15. }
  16.  
  17. public void sendMsg2Me(String msg) {
  18. try {
  19. msg+="\r\n";
  20. ous.write(msg.getBytes());
  21. ous.flush();
  22. } catch (Exception e) {
  23. // TODO Auto-generated catch block
  24. e.printStackTrace();
  25. }
  26. }
  27. //读取客户机发来的消息
  28. private void processSocket() {
  29. try {
  30. InputStream ins=client.getInputStream();
  31. ous=client.getOutputStream();
  32. //将输入流ins封装为可以读取一行字符串也就是以\r\n结尾的字符串
  33. BufferedReader brd=new BufferedReader(new InputStreamReader(ins));
  34. sendMsg2Me("欢迎您来聊天!请输入你的用户名:");
  35. String userName=brd.readLine();
  36. sendMsg2Me(userName+"请输入你的密码");
  37. String pws=brd.readLine();
  38. user=new UserInfo();
  39. user.setName(userName);
  40. user.setPwd(pws);
  41. //调用数据库模块,验证用户是否存在
  42. boolean loginState=DaoTools.checkLogin(user);
  43. if(!loginState) {//不存在则账号关闭
  44. this.closeMe();
  45. return;
  46. }
  47. ChatTools.addClient(this);//认证成功:将这个对象加入服务器队列
  48. String input=brd.readLine();//一行一行的读取客户机发来的消息
  49. while(!"bye".equals(input)) {
  50. System.out.println("服务器收到的是"+input);
  51. //读到一条消息后,就发送给其他的客户机
  52. ChatTools.castMsg(this.user, input);
  53. input=brd.readLine();//读取下一条
  54. }
  55. } catch (Exception e) {
  56. // TODO Auto-generated catch block
  57. e.printStackTrace();
  58. }
  59. ChatTools.castMsg(this.user, "我下线了,再见!");
  60. this.closeMe();
  61. }
  62. //关闭这个线程
  63. public void closeMe() {
  64. try {
  65. client.close();
  66. } catch (IOException e) {
  67. // TODO Auto-generated catch block
  68. e.printStackTrace();
  69. }
  70. }
  71.  
  72. }

通过服务端对用户操作

ChatTools.java

  1. public class ChatTools {
  2. // 保存处理线程的队列对象
  3. private static List<ServerThread> stList = new ArrayList();
  4.  
  5. private ChatTools() {
  6. }
  7.  
  8. /*
  9. * 取得保存处理线程对象的队列
  10. */
  11. public static List<ServerThread> getAllThread() {
  12. return stList;
  13. }
  14.  
  15. /*
  16. * 将一个客户对应的处理线程对象加入到队列中
  17. *
  18. * @param ct:处理线程对象
  19. */
  20. public static void addClient(ServerThread ct) {
  21. // 通知大家一下,有人上限了
  22. castMsg(ct.getOwerUser(), "我上线啦!!目前人数" + stList.size());
  23. stList.add(ct);
  24. SwingUtilities.updateComponentTreeUI(MainServerUI.table_onlineUser);
  25. }
  26.  
  27. // 给队列中某一个用户发消息
  28. public static void sendMsg2One(int index, String msg) {
  29. stList.get(index).sendMsg2Me(msg);
  30. }
  31.  
  32. // 根据表中选中索引,取得处理线程对象对应的用户对象
  33. public static UserInfo getUser(int index) {
  34. return stList.get(index).getOwerUser();
  35. }
  36.  
  37. /*
  38. * 移除队列中指定位置的处理线程对象,界面踢人时调用
  39. *
  40. * @param index:要移除的位置
  41. */
  42. public static void removeClient(int index) {
  43. stList.remove(index).closeMe();
  44. }
  45.  
  46. /*
  47. * 从队列中移除一个用户对象对应的处理线程
  48. *
  49. * @param user:要一处的用户对象
  50. */
  51. public static void removeAllClient(UserInfo user) {
  52. for (int i = 0; i < stList.size(); i++) {
  53. ServerThread ct = stList.get(i);
  54. stList.remove(i);
  55. ct.closeMe();
  56. ct = null;
  57. castMsg(user, "我下线啦");
  58. }
  59. }
  60.  
  61. /*
  62. * 服务器关闭时,调用这个方法,移除,停止队列中所有处理线程对象
  63. */
  64. public static void removeAllClient() {
  65. for (int i = 0; i < stList.size(); i++) {
  66. ServerThread st = stList.get(i);
  67. st.sendMsg2Me("系统消息:服务器即将关闭");
  68. st.closeMe();
  69. stList.remove(i);
  70. System.out.println("关闭了一个..." + i);
  71. st = null;
  72. }
  73. }
  74.  
  75. /*
  76. * 将一条消息发送给队列中的其他客户机处理对象
  77. *
  78. * @param sender:发送者用户对象
  79. *
  80. * @param msg:要发送的消息内容
  81. */
  82. public static void castMsg(UserInfo sender, String msg) {
  83. msg = sender.getName() + "说:" + msg;
  84. for (int i = 0; i < stList.size(); i++) {
  85. ServerThread st = stList.get(i);
  86.  
  87. // ServerThread类中定义有一个将消息发送出去的方法
  88. st.sendMsg2Me(msg);// 发送给每一个客户机
  89. }
  90. }
  91. }

服务端界面

MainServerUI.java

  1. package MyChatV1;
  2.  
  3. import java.awt.Dimension;
  4. import java.awt.FlowLayout;
  5. import java.awt.event.ActionEvent;
  6. import java.awt.event.ActionListener;
  7. import java.awt.event.KeyAdapter;
  8. import java.awt.event.KeyEvent;
  9. import java.util.List;
  10.  
  11. import javax.swing.JButton;
  12. import javax.swing.JDialog;
  13. import javax.swing.JFrame;
  14. import javax.swing.JLabel;
  15. import javax.swing.JMenuItem;
  16. import javax.swing.JOptionPane;
  17. import javax.swing.JPopupMenu;
  18. import javax.swing.JScrollPane;
  19. import javax.swing.JTable;
  20. import javax.swing.JTextField;
  21. import javax.swing.SwingUtilities;
  22.  
  23. /*
  24. * 服务器端管理界面程序
  25. * 1.启/停
  26. * 2.发布公告消息
  27. * 3.显示在线用户信息
  28. * 4.踢人
  29. * 5.对某一个人发消息
  30. *
  31. */
  32. public class MainServerUI {
  33.  
  34. private ChatServer cserver;// 服务器对象
  35. private JFrame jf;// 管理界面
  36. static JTable table_onlineUser;// 在线用户表
  37. private JTextField jta_msg;// 发送消息输入框
  38. private JTextField jta_port;// 服务器端口号输入端
  39. private JButton bu_control_chat;// 启动服务器的按钮
  40.  
  41. public static void main(String[] args) {
  42. // TODO Auto-generated method stub
  43. MainServerUI mu = new MainServerUI();
  44. mu.showUI();
  45. }
  46.  
  47. // 初始化界面
  48. public void showUI() {
  49. jf = new JFrame("javaKe服务器管理程序");
  50. jf.setSize(500, 300);
  51. FlowLayout f1 = new FlowLayout();
  52. jf.setLayout(f1);
  53.  
  54. JLabel la_port = new JLabel("服务器端口:");
  55. jf.add(la_port);
  56. jta_port = new JTextField(4);
  57. jta_port.addKeyListener(new KeyAdapter() {
  58. public void keyTyped(KeyEvent e) {
  59. int keyChar = e.getKeyChar();
  60. if (keyChar >= KeyEvent.VK_0 && keyChar <= KeyEvent.VK_9) {
  61.  
  62. } else {
  63. e.consume();// 屏蔽掉非法输入
  64. }
  65. }
  66. });
  67. jf.add(jta_port);
  68. bu_control_chat = new JButton("启动服务器");
  69. jf.add(bu_control_chat);
  70. //启动的事件监听器
  71. bu_control_chat.addActionListener(new ActionListener() {
  72. public void actionPerformed(ActionEvent e) {
  73. actionServer();
  74. }
  75. });
  76.  
  77. JLabel la_msg = new JLabel("要发送的消息");
  78. jf.add(la_msg);
  79. // 服务器要发送消息的输入框
  80. jta_msg = new JTextField(30);
  81. // 定义一个监听器对象:发送广播消息
  82. ActionListener sendCaseMsgAction = new ActionListener() {
  83. public void actionPerformed(ActionEvent e) {
  84. sendAllMsg();
  85. }
  86. };
  87.  
  88. // 给输入框加航事件监听器,按回车时发送
  89. jta_msg.addActionListener(sendCaseMsgAction);
  90. JButton bu_send = new JButton("Send");
  91. // 给按钮加上发送广播消息的监听器
  92. bu_send.addActionListener(sendCaseMsgAction);
  93. jf.add(jta_msg);
  94. jf.add(bu_send);
  95.  
  96. // 界面上用以显示在线用户列表的表格
  97. table_onlineUser = new JTable();
  98. // 创建我们自己的Model对象:创建时,传入处理线程列表
  99. List<ServerThread> sts = ChatTools.getAllThread();
  100. UserInfoTableModel utm = new UserInfoTableModel(sts);
  101. table_onlineUser.setModel(utm);
  102. // 将表格放到滚动面板对象上
  103. JScrollPane scrollPane = new JScrollPane(table_onlineUser);
  104. // 设定表格在面板上的大小
  105. table_onlineUser.setPreferredScrollableViewportSize(new Dimension(400, 100));
  106. // 超出大小后,JScrollPane自动出现滚动条
  107. scrollPane.setAutoscrolls(true);
  108. jf.add(scrollPane);// 将scrollPane对象加到界面上
  109.  
  110. // 取得表格上的弹出菜单对象,加到表格上
  111. JPopupMenu pop = getTablePop();
  112. table_onlineUser.setComponentPopupMenu(pop);
  113. jf.setVisible(true);
  114. jf.setDefaultCloseOperation(3);// 界面关闭时,程序退出
  115.  
  116. }
  117.  
  118. /*
  119. * 创建表格上的弹出菜单对象,实现发信,踢人功能
  120. */
  121. private JPopupMenu getTablePop() {
  122. JPopupMenu pop = new JPopupMenu();// 弹出菜单对象
  123. JMenuItem mi_send = new JMenuItem("发信");
  124. ;// 菜单项对象
  125. mi_send.setActionCommand("send");// 设定菜单命令关键字
  126. JMenuItem mi_del = new JMenuItem("踢掉");// 菜单项对象
  127. mi_del.setActionCommand("del");// 设定菜单命令关键字
  128. // 弹出菜单上的事件监听器对象
  129. ActionListener al = new ActionListener() {
  130. public void actionPerformed(ActionEvent e) {
  131. String s = e.getActionCommand();
  132. // 哪个菜单项点击了,这个s就是其设定的ActionCommand
  133. popMenuAction(s);
  134. }
  135. };
  136. mi_send.addActionListener(al);
  137. mi_del.addActionListener(al);// 给菜单加上监听器
  138. pop.add(mi_send);
  139. pop.add(mi_del);
  140. return pop;
  141. }
  142.  
  143. // 处理弹出菜单上的事件
  144. private void popMenuAction(String command) {
  145. // 得到在表格上选中的行
  146. final int selectIndex = table_onlineUser.getSelectedRow();
  147. if (selectIndex == -1) {
  148. JOptionPane.showMessageDialog(jf, "请选中一个用户");
  149. return;
  150. }
  151. if (command.equals("del")) {
  152. // 从线程中移除处理线程对象
  153. ChatTools.removeClient(selectIndex);
  154. } else if (command.equals("send")) {
  155. UserInfo user = ChatTools.getUser(selectIndex);
  156. final JDialog jd = new JDialog(jf, true);// 发送对话框
  157. jd.setLayout(new FlowLayout());
  158. jd.setTitle("您将对" + user.getName() + "发信息");
  159. jd.setSize(200, 100);
  160. final JTextField jtd_m = new JTextField(20);
  161. //jtd_m.setPreferredSize(new Dimension(150,30));
  162. JButton jb = new JButton("发送!");
  163. // jb.setPreferredSize(new Dimension(50,30));
  164. jd.add(jtd_m);
  165. jd.add(jb);
  166. // 发送按钮的事件实现
  167. jb.addActionListener(new ActionListener() {
  168. public void actionPerformed(ActionEvent e) {
  169. System.out.println("发送了一条消息啊...");
  170. String msg = "系统悄悄说:" + jtd_m.getText();
  171. ChatTools.sendMsg2One(selectIndex, msg);
  172. jtd_m.setText("");// 清空输入框
  173. jd.dispose();
  174. }
  175. });
  176. jd.setVisible(true);
  177. } else {
  178. JOptionPane.showMessageDialog(jf, "未知菜单:" + command);
  179. }
  180. // 刷新表格
  181. SwingUtilities.updateComponentTreeUI(table_onlineUser);
  182. }
  183.  
  184. // 按下发送服务器消息的按钮,给所有在线用户发送消息
  185. private void sendAllMsg() {
  186. String msg = jta_msg.getText();// 得到输入框的消息
  187. UserInfo user = new UserInfo();
  188. user.setName("系统");
  189. user.setPwd("pwd");
  190. ChatTools.castMsg(user, msg); // 发送
  191. jta_msg.setText("");// 清空输入框
  192.  
  193. }
  194.  
  195. // 响应启动/停止按钮事件
  196. private void actionServer() {
  197. // 1.要得到服务器状态
  198. if (null == cserver) {
  199. String sPort = jta_port.getText();// 得到输入的端口
  200. int port = Integer.parseInt(sPort);
  201. cserver = new ChatServer(port);
  202. cserver.start();
  203. jf.setTitle("QQ服务器管理程序 :正在运行中");
  204. bu_control_chat.setText("Stop!");
  205. } else if (cserver.isRunning()) {// 己经在运行
  206. cserver.stopchatServer();
  207. cserver = null;
  208. // 清除所有己在运行的客户端处理线程
  209. ChatTools.removeAllClient();
  210. jf.setTitle("QQ服务器管理程序 :己停止");
  211. bu_control_chat.setText("Start!");
  212. }
  213.  
  214. }
  215. }

其中用到了一个自己创建的类UserInfoTableModel,用于菜单中的信息显示

  1. public class UserInfoTableModel implements TableModel {
  2. List<ServerThread> sts = ChatTools.getAllThread();
  3. public UserInfoTableModel(List<ServerThread> sts) {
  4. this.sts=sts;
  5.  
  6. }
  7. @Override
  8. public void addTableModelListener(TableModelListener l) {
  9. // TODO Auto-generated method stub
  10.  
  11. }
  12.  
  13. @Override
  14. public Class<?> getColumnClass(int columnIndex) {
  15. // TODO Auto-generated method stub
  16. return String.class;
  17. }
  18.  
  19. @Override
  20. public int getColumnCount() {
  21. // TODO Auto-generated method stub
  22. return 1;
  23. }
  24.  
  25. @Override
  26. public String getColumnName(int columnIndex) {
  27. // TODO Auto-generated method stub
  28. return null;
  29. }
  30.  
  31. @Override
  32. public int getRowCount() {
  33. // TODO Auto-generated method stub
  34. return sts.size();
  35. }
  36.  
  37. @Override
  38. public Object getValueAt(int rowIndex, int columnIndex) {
  39. // TODO Auto-generated method stub
  40. return sts.get(rowIndex).getOwerUser().getName();
  41. }
  42.  
  43. @Override
  44. public boolean isCellEditable(int rowIndex, int columnIndex) {
  45. // TODO Auto-generated method stub
  46. return false;
  47. }
  48.  
  49. @Override
  50. public void removeTableModelListener(TableModelListener l) {
  51. // TODO Auto-generated method stub
  52.  
  53. }
  54.  
  55. @Override
  56. public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
  57. // TODO Auto-generated method stub
  58.  
  59. }
  60.  
  61. }

这样我们的聊天室就大功告成了。(这个聊天室的客户端界面我还没做,打算对上面代码重新整理之后再写。当然也期待你为这个聊天室添加上一个界面)

JAVA-Socket通信 打造属于自己的聊天室(服务端)的更多相关文章

  1. AgileEAS.NET SOA 中间件平台.Net Socket通信框架-简单例子-实现简单的服务端客户端消息应答

    一.AgileEAS.NET SOA中间件Socket/Tcp框架介绍 在文章AgileEAS.NET SOA 中间件平台Socket/Tcp通信框架介绍一文之中我们对AgileEAS.NET SOA ...

  2. JAVA Socket通信 打造属于自己的网盘

    近一个月没敲JAVA代码了,最近老师布置了一个写JAVA网盘的作业,总共花了十几个小时,总算写完了,debug真的累,感觉自己还是菜了,没有那种有一个想法就能马上用代码实现的能力....不扯了,下面开 ...

  3. java 用socket制作一个简易多人聊天室

    代码: 服务器端Server import java.io.*; import java.net.*; import java.util.ArrayList; public class Server{ ...

  4. 与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室

    原文:与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...

  5. 与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室

    原文:与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...

  6. Java Socket通信实例

    一.简单的客户端与服务器一对一连接: Socket通信的步骤: 1.创建ServerSocket和Socket 2.打开连接到Scket的输入/输出流 3.按照协议对Socket进行读/写操作 4.关 ...

  7. java socket通信-传输文件图片--传输图片

    ClientTcpSend.java   client发送类 package com.yjf.test; import java.io.DataOutputStream; import java.io ...

  8. java Socket通信使用BufferedReader和BufferedWriter的注意事项

    注意事项:readLine()要求有换行标识,write()要输出换行标识,要调用flush()刷新缓冲区. 以下是取自java socket通信中的一小段代码. BufferedReader rea ...

  9. Java Socket通信读取相关信息代码

    转自:http://developer.51cto.com/art/201003/190206.htm Java Socket通信读取有不少需要我们注意的知识点.当我们在使用的时候有很多的问题摆在我们 ...

随机推荐

  1. c读入实型

    读入: 如果读入的数为整型,然后转为实型,则%lf 否则%f也可以 读出: %f,这样在codeblocks才能看到正确的结果

  2. Linux shell 日期,时间相关的命令

    在shell脚本中,经常要用到跟获取日期相关的东西,这里记录一下Linux shell 获取日期的方法 获取当前日期:today=`date +"%Y-%m-%d"` 获取昨天的日 ...

  3. JAVA-Servlet内容

    Servlet重定向 HttpServletResponse接口的sendRedirect()方法可以用于将响应重定向到另一个资源,资源可能是servlet,jsp或html文件. 它接受相对和绝对U ...

  4. 【原创】javascript模板引擎的简单实现

    本来想把之前对artTemplate源码解析的注释放上来分享下,不过隔了一年,找不到了,只好把当时分析模板引擎原理后,自己尝试 写下的模板引擎与大家分享下,留个纪念,记得当时还对比了好几个模板引擎来着 ...

  5. 一个中国地图的SVG,可以带参数

    <script src="http://files.cnblogs.com/files/LoveOrHate/jquery.min.js"></script> ...

  6. html5 canvas结构基础

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. JavaScript1.6数组新特性和JQuery的几个工具方法

    JavaScript 1.6 引入了几个新的Array 方法,具体的介绍见:New in JavaScript 1.6 .这些方法已经被写进了ECMA262 V5.现代浏览器(IE9/Firefox/ ...

  8. Vue.js绑定内联样式

    1.对象语法 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www. ...

  9. 【转】教你何时开启水果机上的HDR拍照

    原址:http://news.mydrivers.com/1/175/175922.htm 苹果在iOS 4.1操作系统中为iPhone 4增加了一项有趣的新功能:HDR拍照.虽然目前市场上支持HDR ...

  10. NIO学习(1)-入门学习

    一.NIO概念 IO:标准IO,也既阻塞式IO NIO:非阻塞式IO 二.NIO与标准IO的IO工作方式 标准IO基于字节流和字符流进行操作 NIO是基于通道(Channel)和缓冲区(Buffer) ...