一,团队介绍
团队成员 | 职务 | 负责部分 | 博客链接 |
蔡佳生 | 组长 | 网络编程,多线程,数据库 | 面向对象综合程序设计课程设计-优快云博客 |
蔡朝辉 | 组员 | 客户端页面设计 |
二,项目git地址
三、项目功能架构图及主要功能流程图
四,项目重要代码
客户端
private static void Chat(String content,ClientFrame cframe) {
System.out.println(content);
String name=content.substring(0,content.indexOf("&"));
String sender = content.substring(content.indexOf("&")+1, content.indexOf("/"));
String word = content.substring(content.indexOf("/") + 1);
//在聊天窗打印聊天信息
cframe.jtaChat.append(cframe.sdf.format(new Date()) + " \n来自 " + name + ":" + word + " \n");
//显示最新消息
cframe.jtaChat.setCaretPosition(cframe.jtaChat.getDocument().getLength());
}
private static void OnlineListUpdate(String content,ClientFrame cframe) throws UnknownHostException {
//提取在线列表的数据模型
DefaultTableModel tbm = (DefaultTableModel) cframe.jtbOnline.getModel();
//清除在线名单列表
tbm.setRowCount(0);
//更新在线名单
String[] onlinelist = content.split(",");
//逐一添加当前在线者
for(String member : onlinelist)
{
String[] tmp = new String[3];
//如果是自己则不在名单中显示
if(member.substring(member.indexOf("&")+1).equals(InetAddress.getLocalHost().getHostAddress() + ":" + s.getLocalPort()))
{tmp[0]=member.substring(0,member.indexOf("&"));Client.name=tmp[0]; continue;}
tmp[0] = member.substring(0,member.indexOf("&"));
tmp[1] = member.substring(member.indexOf("&")+1, member.indexOf(":"));
tmp[2] = member.substring(member.indexOf(":") + 1);
//添加当前在线者之一
tbm.addRow(tmp);
}
//提取在线列表的渲染模型
DefaultTableCellRenderer tbr = new DefaultTableCellRenderer();
//表格数据居中显示
tbr.setHorizontalAlignment(JLabel.CENTER);
cframe.jtbOnline.setDefaultRenderer(Object.class, tbr);
}
服务端
class ServerThread implements Runnable
{
//获取的客户端Socket
Socket s = null;
//获取的服务器ServerSocket
ServerSocket ss = null;
//获取的客户端IP
String ip = null;
//获取的客户端端口
int port = 0;
//组合客户端的ip和端口字符串得到uid字符串
String uid = null;
String name=null;
//name&ip:port
//静态ArrayList存储所有uid,uid由ip和端口字符串拼接而成
static ArrayList<String> uid_arr = new ArrayList<String>();
//静态HashMap存储所有uid, ServerThread对象组成的对
static HashMap<String, ServerThread> hm = new HashMap<String, ServerThread>();
//放uid和name
static HashMap<String,String> hm2=new HashMap<>();
public ServerThread(Socket s, ServerSocket ss, String ip, int port)
{
this.s = s;
this.ss = ss;
this.ip = ip;
this.port = port;
uid = ip + ":" + port;
}
@Override
public void run()
{
//将当前客户端uid存入ArrayList
uid_arr.add(uid);
//将当前uid和ServerThread对存入HashMap
hm.put(uid, this);
//时间显示格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//控制台打印客户端IP和端口
System.out.println("Client connected: " + uid);
try
{
//获取输入流
InputStream in = s.getInputStream();
//获取输出流
OutputStream out = s.getOutputStream();
//向当前客户端传输连接成功信息
String welcome = ip+":"+port;
out.write(welcome.getBytes());
//广播更新在线名单
updateOnlineList(out);
//准备缓冲区
byte[] buf = new byte[1024];
int len = 0;
//持续监听并转发客户端消息
while(true)
{
len = in.read(buf);
String msg = new String(buf, 0, len);
System.out.println(msg);
//消息类型:退出或者聊天
String type = msg.substring(0, msg.indexOf("/"));
//消息本体:空或者聊天内容
String content = msg.substring(msg.indexOf("/") + 1);
//根据消息类型分别处理
//客户端要退出
if("Exit".equals(type))
{
//更新ArrayList和HashMap, 删除退出的uid和线程
uid_arr.remove(uid_arr.indexOf(uid));
hm.remove(uid);
//广播更新在线名单
updateOnlineList(out);
//控制台打印客户端IP和端口
System.out.println("Client exited: " + uid);
//结束循环,结束该服务线程
break;
}
//客户端要聊天
else if("Chat".equals(type))
{
//提取收信者地址
String[] receiver_arr = content.substring(0, content.indexOf("/")).split(",");
//提取聊天内容
String word = content.substring(content.indexOf("/") + 1);
//向收信者广播发出聊天信息
chatOnlineList(out, uid, receiver_arr, word);
} else if ("Name".equals(type)) {
String name=content.substring(0,content.indexOf("&"));
System.out.println("放进hm2的是:"+name);
hm2.put(uid,name);
updateOnlineList(out);
} /*else if (type.equals("File")) {
BufferedInputStream bis=new BufferedInputStream(s.getInputStream());
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("test\\Serverdir\\1.png"));
byte[] bytes=new byte[1024];
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
}*/
}
}
catch(Exception e){}
}
//向所有已连接的客户端更新在线名单
//OnlineListUpdate/192.168.125.15:60572,192.168.125.15:60583
public void updateOnlineList(OutputStream out) throws Exception
{
for(String tmp_uid : uid_arr)
{
//获取广播收听者的输出流
out = hm.get(tmp_uid).s.getOutputStream();
//将当前在线名单以逗号为分割组合成长字符串一次传送
StringBuilder sb = new StringBuilder("OnlineListUpdate/");
for(String member : uid_arr)
{ String name=hm2.get(member);
sb.append(name+"&"+member);
//以逗号分隔uid,除了最后一个
if(uid_arr.indexOf(member) != uid_arr.size() - 1) {
sb.append(",");
}
}
out.write(sb.toString().getBytes());
}
}
//向指定的客户端发送聊天消息
//Chat/192.168.125.15:60572/2
public void chatOnlineList(OutputStream out, String uid, String[] receiver_arr, String word) throws Exception
{
for(String tmp_uid : receiver_arr)
{
//获取广播收听者的输出流
out = hm.get(tmp_uid).s.getOutputStream();
//发送聊天信息
String name=hm2.get(uid);
out.write(("Chat/" +name+"&"+ uid + "/" + word).getBytes());
}
}
}
五,项目运行截图
六、课程设计总结及展望
总结:深入学习了Java网络编程的相关知识,掌握了TCP协议的工作原理以及如何在Java中实现Socket通信。我们还学习了多线程编程,使聊天室能够同时处理多个客户端连接。在聊天室应用中,用户之间的交互是核心。设计易于使用且符合直觉的交互逻辑对于提升用户体验至关重要。你需要仔细思考消息如何发送、接收、展示以及如何让用户方便地进行这些操作。从设计和实现一个响应式用户界面到处理后端逻辑,再到优化网络通信,我们将获得宝贵的经验,并且在遇到困难和挑战时也会锻炼你的问题解决能力。总的来说,面向对象的程序设计会给您带来一个结构化、有条不紊的开发过程,同时也带来了不同层面的挑战。
展望:没有实现文件传输的功能。聊天室的功能比较简单,没有考虑用户界面的美观。在设计过程中,我们没有考虑到安全性问题,例如数据加密等。