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

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

先来看看我做的效果

这是服务器控制界面

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

输入消息

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

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

UserInfo.java

public class UserInfo {
private String name;
private String possward; public String getName(){
return this.name;
}
public void setName(String name) {
this.name=name;
} public void setPwd(String possward) {
this.possward=possward;
}
}

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

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

服务端目前则使用cmd进行

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

DaoTools.java

public class DaoTools {
//内存用户信息数据库
private static Map<String,UserInfo>userDB=new HashMap(); public static boolean checkLogin(UserInfo user) {
//验证用户名是否存在
if(userDB.containsKey((user.getName()))){
return true;
}
System.out.println("认证失败!:"+user.getName());
return false;
}
//系统内部自动创建10个账号
static {
for(int i=0;i<10;i++) {
UserInfo user=new UserInfo();
user.setName("user"+i);
user.setPwd("pwd"+i);
userDB.put(user.getName(), user);
}
} }

服务端服务器创建

ChatServer.java

public class ChatServer extends Thread {
private int port;// 端口
private boolean running = false;// 服务器是否运行中的标记
private ServerSocket sc;// 服务器对象 /*
* 创建服务器对象时,必须传入端口号
*
* @param port:服务器所在端口号
*/
public ChatServer(int port) {
this.port = port;
} // 线程中启动服务器
public void run() {
setupServer();
} // 在指定端口上启动服务器
public void setupServer() {
try {
ServerSocket sc = new ServerSocket(this.port);
running = true;
System.out.println("服务器创建成功:" + port);
while (running) {
Socket client = sc.accept();// 等待连结进入
System.out.println("进入了一个客户机连接" + client.getRemoteSocketAddress());
// 启动一个处理线程,去处理这个连结对象
ServerThread ct = new ServerThread(client);
ct.start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} /*
* 查询服务器是否在运行
*
* @return: true为运行中
*/
public boolean isRunning() {
return this.running;
} // 关闭服务器
public void stopchatServer() {
this.running = false;
try {
sc.close();
} catch (Exception e) {}
}
}

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

ServerThread.java

public class ServerThread extends Thread {
private Socket client;//线程中处理的客户对象
private OutputStream ous;//输出流对象
private UserInfo user;//这个线程处理对象代表的用户的信息
//创建对象时必须传入一个Socket对象
public ServerThread(Socket client) {
this.client=client;
}
//获取这个线程对象代表的用户对象
public UserInfo getOwerUser() {
return this.user;
}
public void run() {
processSocket();
} public void sendMsg2Me(String msg) {
try {
msg+="\r\n";
ous.write(msg.getBytes());
ous.flush();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//读取客户机发来的消息
private void processSocket() {
try {
InputStream ins=client.getInputStream();
ous=client.getOutputStream();
//将输入流ins封装为可以读取一行字符串也就是以\r\n结尾的字符串
BufferedReader brd=new BufferedReader(new InputStreamReader(ins));
sendMsg2Me("欢迎您来聊天!请输入你的用户名:");
String userName=brd.readLine();
sendMsg2Me(userName+"请输入你的密码");
String pws=brd.readLine();
user=new UserInfo();
user.setName(userName);
user.setPwd(pws);
//调用数据库模块,验证用户是否存在
boolean loginState=DaoTools.checkLogin(user);
if(!loginState) {//不存在则账号关闭
this.closeMe();
return;
}
ChatTools.addClient(this);//认证成功:将这个对象加入服务器队列
String input=brd.readLine();//一行一行的读取客户机发来的消息
while(!"bye".equals(input)) {
System.out.println("服务器收到的是"+input);
//读到一条消息后,就发送给其他的客户机
ChatTools.castMsg(this.user, input);
input=brd.readLine();//读取下一条
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ChatTools.castMsg(this.user, "我下线了,再见!");
this.closeMe();
}
//关闭这个线程
public void closeMe() {
try {
client.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }

通过服务端对用户操作

ChatTools.java

public class ChatTools {
// 保存处理线程的队列对象
private static List<ServerThread> stList = new ArrayList(); private ChatTools() {
} /*
* 取得保存处理线程对象的队列
*/
public static List<ServerThread> getAllThread() {
return stList;
} /*
* 将一个客户对应的处理线程对象加入到队列中
*
* @param ct:处理线程对象
*/
public static void addClient(ServerThread ct) {
// 通知大家一下,有人上限了
castMsg(ct.getOwerUser(), "我上线啦!!目前人数" + stList.size());
stList.add(ct);
SwingUtilities.updateComponentTreeUI(MainServerUI.table_onlineUser);
} // 给队列中某一个用户发消息
public static void sendMsg2One(int index, String msg) {
stList.get(index).sendMsg2Me(msg);
} // 根据表中选中索引,取得处理线程对象对应的用户对象
public static UserInfo getUser(int index) {
return stList.get(index).getOwerUser();
} /*
* 移除队列中指定位置的处理线程对象,界面踢人时调用
*
* @param index:要移除的位置
*/
public static void removeClient(int index) {
stList.remove(index).closeMe();
} /*
* 从队列中移除一个用户对象对应的处理线程
*
* @param user:要一处的用户对象
*/
public static void removeAllClient(UserInfo user) {
for (int i = 0; i < stList.size(); i++) {
ServerThread ct = stList.get(i);
stList.remove(i);
ct.closeMe();
ct = null;
castMsg(user, "我下线啦");
}
} /*
* 服务器关闭时,调用这个方法,移除,停止队列中所有处理线程对象
*/
public static void removeAllClient() {
for (int i = 0; i < stList.size(); i++) {
ServerThread st = stList.get(i);
st.sendMsg2Me("系统消息:服务器即将关闭");
st.closeMe();
stList.remove(i);
System.out.println("关闭了一个..." + i);
st = null;
}
} /*
* 将一条消息发送给队列中的其他客户机处理对象
*
* @param sender:发送者用户对象
*
* @param msg:要发送的消息内容
*/
public static void castMsg(UserInfo sender, String msg) {
msg = sender.getName() + "说:" + msg;
for (int i = 0; i < stList.size(); i++) {
ServerThread st = stList.get(i); // ServerThread类中定义有一个将消息发送出去的方法
st.sendMsg2Me(msg);// 发送给每一个客户机
}
}
}

服务端界面

MainServerUI.java

package MyChatV1;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.List; import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities; /*
* 服务器端管理界面程序
* 1.启/停
* 2.发布公告消息
* 3.显示在线用户信息
* 4.踢人
* 5.对某一个人发消息
*
*/
public class MainServerUI { private ChatServer cserver;// 服务器对象
private JFrame jf;// 管理界面
static JTable table_onlineUser;// 在线用户表
private JTextField jta_msg;// 发送消息输入框
private JTextField jta_port;// 服务器端口号输入端
private JButton bu_control_chat;// 启动服务器的按钮 public static void main(String[] args) {
// TODO Auto-generated method stub
MainServerUI mu = new MainServerUI();
mu.showUI();
} // 初始化界面
public void showUI() {
jf = new JFrame("javaKe服务器管理程序");
jf.setSize(500, 300);
FlowLayout f1 = new FlowLayout();
jf.setLayout(f1); JLabel la_port = new JLabel("服务器端口:");
jf.add(la_port);
jta_port = new JTextField(4);
jta_port.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
int keyChar = e.getKeyChar();
if (keyChar >= KeyEvent.VK_0 && keyChar <= KeyEvent.VK_9) { } else {
e.consume();// 屏蔽掉非法输入
}
}
});
jf.add(jta_port);
bu_control_chat = new JButton("启动服务器");
jf.add(bu_control_chat);
//启动的事件监听器
bu_control_chat.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
actionServer();
}
}); JLabel la_msg = new JLabel("要发送的消息");
jf.add(la_msg);
// 服务器要发送消息的输入框
jta_msg = new JTextField(30);
// 定义一个监听器对象:发送广播消息
ActionListener sendCaseMsgAction = new ActionListener() {
public void actionPerformed(ActionEvent e) {
sendAllMsg();
}
}; // 给输入框加航事件监听器,按回车时发送
jta_msg.addActionListener(sendCaseMsgAction);
JButton bu_send = new JButton("Send");
// 给按钮加上发送广播消息的监听器
bu_send.addActionListener(sendCaseMsgAction);
jf.add(jta_msg);
jf.add(bu_send); // 界面上用以显示在线用户列表的表格
table_onlineUser = new JTable();
// 创建我们自己的Model对象:创建时,传入处理线程列表
List<ServerThread> sts = ChatTools.getAllThread();
UserInfoTableModel utm = new UserInfoTableModel(sts);
table_onlineUser.setModel(utm);
// 将表格放到滚动面板对象上
JScrollPane scrollPane = new JScrollPane(table_onlineUser);
// 设定表格在面板上的大小
table_onlineUser.setPreferredScrollableViewportSize(new Dimension(400, 100));
// 超出大小后,JScrollPane自动出现滚动条
scrollPane.setAutoscrolls(true);
jf.add(scrollPane);// 将scrollPane对象加到界面上 // 取得表格上的弹出菜单对象,加到表格上
JPopupMenu pop = getTablePop();
table_onlineUser.setComponentPopupMenu(pop);
jf.setVisible(true);
jf.setDefaultCloseOperation(3);// 界面关闭时,程序退出 } /*
* 创建表格上的弹出菜单对象,实现发信,踢人功能
*/
private JPopupMenu getTablePop() {
JPopupMenu pop = new JPopupMenu();// 弹出菜单对象
JMenuItem mi_send = new JMenuItem("发信");
;// 菜单项对象
mi_send.setActionCommand("send");// 设定菜单命令关键字
JMenuItem mi_del = new JMenuItem("踢掉");// 菜单项对象
mi_del.setActionCommand("del");// 设定菜单命令关键字
// 弹出菜单上的事件监听器对象
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) {
String s = e.getActionCommand();
// 哪个菜单项点击了,这个s就是其设定的ActionCommand
popMenuAction(s);
}
};
mi_send.addActionListener(al);
mi_del.addActionListener(al);// 给菜单加上监听器
pop.add(mi_send);
pop.add(mi_del);
return pop;
} // 处理弹出菜单上的事件
private void popMenuAction(String command) {
// 得到在表格上选中的行
final int selectIndex = table_onlineUser.getSelectedRow();
if (selectIndex == -1) {
JOptionPane.showMessageDialog(jf, "请选中一个用户");
return;
}
if (command.equals("del")) {
// 从线程中移除处理线程对象
ChatTools.removeClient(selectIndex);
} else if (command.equals("send")) {
UserInfo user = ChatTools.getUser(selectIndex);
final JDialog jd = new JDialog(jf, true);// 发送对话框
jd.setLayout(new FlowLayout());
jd.setTitle("您将对" + user.getName() + "发信息");
jd.setSize(200, 100);
final JTextField jtd_m = new JTextField(20);
//jtd_m.setPreferredSize(new Dimension(150,30));
JButton jb = new JButton("发送!");
// jb.setPreferredSize(new Dimension(50,30));
jd.add(jtd_m);
jd.add(jb);
// 发送按钮的事件实现
jb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("发送了一条消息啊...");
String msg = "系统悄悄说:" + jtd_m.getText();
ChatTools.sendMsg2One(selectIndex, msg);
jtd_m.setText("");// 清空输入框
jd.dispose();
}
});
jd.setVisible(true);
} else {
JOptionPane.showMessageDialog(jf, "未知菜单:" + command);
}
// 刷新表格
SwingUtilities.updateComponentTreeUI(table_onlineUser);
} // 按下发送服务器消息的按钮,给所有在线用户发送消息
private void sendAllMsg() {
String msg = jta_msg.getText();// 得到输入框的消息
UserInfo user = new UserInfo();
user.setName("系统");
user.setPwd("pwd");
ChatTools.castMsg(user, msg); // 发送
jta_msg.setText("");// 清空输入框 } // 响应启动/停止按钮事件
private void actionServer() {
// 1.要得到服务器状态
if (null == cserver) {
String sPort = jta_port.getText();// 得到输入的端口
int port = Integer.parseInt(sPort);
cserver = new ChatServer(port);
cserver.start();
jf.setTitle("QQ服务器管理程序 :正在运行中");
bu_control_chat.setText("Stop!");
} else if (cserver.isRunning()) {// 己经在运行
cserver.stopchatServer();
cserver = null;
// 清除所有己在运行的客户端处理线程
ChatTools.removeAllClient();
jf.setTitle("QQ服务器管理程序 :己停止");
bu_control_chat.setText("Start!");
} }
}

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

public class UserInfoTableModel implements TableModel {
List<ServerThread> sts = ChatTools.getAllThread();
public UserInfoTableModel(List<ServerThread> sts) {
this.sts=sts; }
@Override
public void addTableModelListener(TableModelListener l) {
// TODO Auto-generated method stub } @Override
public Class<?> getColumnClass(int columnIndex) {
// TODO Auto-generated method stub
return String.class;
} @Override
public int getColumnCount() {
// TODO Auto-generated method stub
return 1;
} @Override
public String getColumnName(int columnIndex) {
// TODO Auto-generated method stub
return null;
} @Override
public int getRowCount() {
// TODO Auto-generated method stub
return sts.size();
} @Override
public Object getValueAt(int rowIndex, int columnIndex) {
// TODO Auto-generated method stub
return sts.get(rowIndex).getOwerUser().getName();
} @Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
// TODO Auto-generated method stub
return false;
} @Override
public void removeTableModelListener(TableModelListener l) {
// TODO Auto-generated method stub } @Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
// TODO Auto-generated method stub } }

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

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. 【转】如何学习android开发

    1.Java基础 很多朋友一上手就开始学习Android,似乎太着急了一些.Android应用程序开发是以Java语言为基础的,所以没有扎实的Java基础知识,只 是机械的照抄别人的代码,是没有任何意 ...

  2. 关于Thinkphp5类命名导致的“模块不存在”问题

    不得不说,thinkphp5就是个十足的坑货, 在thinkphp3.2.3的基础上,函数.用法变了也就忍了, 在mac下写的一个类文件 GetRedisData.php,在mac+mamp环境下是正 ...

  3. POJ-2253 Frogger(最短路)

    https://vjudge.net/problem/POJ-2253 题意 公青蛙想到母青蛙那里去,期间有许多石头,公青蛙可以通过这些石头跳过去.问至少要跳的最大距离,即所有路径上石头间的最大距离的 ...

  4. Liberty Mutual Property Inspection, Winner's Interview: Qingchen Wang

    Liberty Mutual Property Inspection, Winner's Interview: Qingchen Wang The hugely popular Liberty Mut ...

  5. Your Prediction Gets As Good As Your Data

    Your Prediction Gets As Good As Your Data May 5, 2015 by Kazem In the past, we have seen software en ...

  6. JavaScript事件模拟元素拖动

    一.前言: 最近要实现一个元素拖放效果,鼠标拖动元素并且定位元素,首先想到的是HTML5中的拖放,在HTML5中,有一个draggable属性,且有dragstart, dragover, drop等 ...

  7. [转载]详解主流浏览器多进程架构:Chrome、IE

    http://www.cnbeta.com/articles/109595.htm 随着Web浏览器重要性的日益突出,恶意软件.木马.间谍软件等网络攻击也呈现逐渐的上升.而面对 如此众多的潜在威胁,为 ...

  8. python技巧 使用值来排序一个字典

    In [8]: a={'x':11,'y':22,'c':4} In [9]: import operator In [10]: sorted(a.items(),key=operator.itemg ...

  9. 无锁并发框架Disruptor学习入门

    刚刚听说disruptor,大概理一下,只为方便自己理解,文末是一些自己认为比较好的博文,如果有需要的同学可以参考. 本文目标:快速了解Disruptor是什么,主要概念,怎么用 1.Disrupto ...

  10. json 删除、添加对象

    1. 定义json对象    var entryJson = []; 2. 删除.添加对象 entryJson.pop();     //删除最后一个对象   entryJson.push({ //往 ...