2025-10-11 10:23:09

Java实现简单聊天室【含源码】

1. 前言

Socket通信与多线程问题对于初学者来说是比较混乱的东西,尤其两者又时常一起出现,因此经常把初学者搞得晕头转向。本文将对通过实现一个简单的聊天项目帮助初学者更好的理解Socket通信与多线程,重点在于实现功能的技术,因此图形化设计的过程省略了,将整个界面以及输入输出都放在控制台显示。

2. 成果演示

聊天室程序演示

3. 消息如何传送?

学过Socket通信的小伙伴都知道,如果两台主机之间要进行TCP通信,则需要一台充当服务器,另一台充当客户端,两者之间可以建立起一条Socket通道。

可是,当三台或者更多的主机要互相通信的时候怎么办呢? 首先连接要建立起来肯定是需要服务器侦听的,属于客户-服务器模式,所以多台客户机肯定不能够直接连接通信,他们需要连接到同一个服务器作为中转站。比如客户A要发消息给客户B,那么A发出来的信息就要经过服务器,由服务器转发给客户B

由此又会产生一个问题:服务器连接那么多客户端,它怎么知道往哪里发送才能给到客户B呢? 事实上,我们可以将一些基本信息封装到一个Message类中,如发送者,接受者,内容……发送的信息等,以类为单位,当服务器接收到这个Message时就能够得知里边一些基本内容,从而知道这封信来自何方,去往何处,该如何处理

//Serializable是实现序列化,若不实现这个接口的话类无法再流中传送

public class Message implements Serializable {

private static final long serialVersionUID=1L; //版本兼容标志

private String sender; //发送者

private String getter; //接收者

private String content; //内容

private String sendTime; //发送时间

private String messageType; //消息类型

}

服务器收到不同类型的Message时应该做出不同的操作,如收到请求登录的Message时需要判断账号密码是否正确;收到A发送给B的信息Message时需要转发给B;收到A请求关闭连接的Message时要回收Socket通道……本项目中的信息状态如下

public interface MessageType {

String MESSAGE_LOGIN_SUCCESS="1"; //登录成功

String MESSAGE_LOGIN_FAIL="2"; //登录失败

String MESSAGE_COMM_MES="3";//普通信息包

String MESSAGE_GET_ONLINE_FRIEND="4"; //得到在线用户列表

String MESSAGE_RET_ONLINE_FRIEND="5";//返回在线用户列表

String MESSAGE_CLIENT_EXIT="6"; //客户请求退出

String MESSAGE_CLIENT_NO_EXIST="7"; //发送目标不存在

String MESSAGE_CLIENT_OFFLINE="8"; //发送目标不在线

}

4. 多线程体现在哪些地方?

① 服务器的各条Socket

一条Socket通道对应一条连接,服务器的Socket是通过侦听得来的。因此服务器处需要利用一个循环不断进行侦听,当侦听到连接需求时,就建立起一条连接,账号密码验证通过后【将用户输入与账号密码表进行对比】,将这条连接保存到一个线程类中,启动这个线程类对这条通道进行维护,通过接收到的Message类型不同而进行不同应答,主函数则继续侦听……

利用ConcurrentHashMap<用户名,线程类>映射【用法同HashMap,不过在多线程中使用ConcurrentHashMap更安全】对线程类进行维护,当我们需要取得某条通道的时候,只需要通过用户名就能得出来;当某条Socket通道被弃用时,将其从映射中移除,通过这个映射,我们也可以得知有几个用户目前在线。

② 客户端的接收功能

我们的客户端界面会根据我们的输入进行信息的发送,但是同时我们还需要进行接收,因此接收的功能可以另开一个线程去实现,这样就可以实现发送/接收并发进行

原理类似,当账号密码都正确时,开启一个线程类,这个线程类可以对Socket通道的接收进行维护,根据接收的不同类型Message而作出相应处理工作

5. 各功能实现

①用户账号密码数据库

由于本项目的重点在于Socket通信与多线程理解,因此对于数据库方面利用了ConcurrentHashMap<用户名,用户>映射来进行模拟账号密码表。

即客户端的登录信息通过Socket发送到服务器端,服务端通过比对两者账号密码,如果一致就返回登录成功的信息,并进行线程开启等等一系列操作……

//模拟用户数据库

private static ConcurrentHashMap userMap = new ConcurrentHashMap<>();

//数据库数据

static {

userMap.put("123", new User("123", "123"));

userMap.put("tom", new User("tom", "123"));

userMap.put("捉妖龙", new User("捉妖龙", "123"));

}

② 如何查看当前在线人数

前面提到,服务器中有一个映射维护了当前所有的Socket通道。因此客户端想知道当前有哪些用户在线,只需要向服务器发送一条Message,类型是MESSAGE_GET_ONLINE_FRIEND【请求得到在线用户】,服务器收到这条信息后遍历自己的映射,将关键字【即用户名】进行拼接再返回给客户端,客户端根据需要进行拼接读取即可。

③ 如何实现私聊

前边提过,几台主机之间互相通信需要依赖服务器作为中介。所以我们可以将要发送方、接收方、内容……封装到Message,服务器收到后得到其中的信息,知道这个信息的接受者。服务器在映射中寻找这个通道,如果找到了,那么用这条通道将信息发送出去,另一方接收后显示;如果映射中没有这个通道,则信息从哪里来回哪里去,同时告诉发送方,信息接收对象不存在。

实现离线留言

按照日常习惯,即使信息发送时对方不在线,信息也是可以发送出去的,对方上线后应该就能接收到。我们来尝试实现这个功能

如果要发送的对象不在数据库中,那么说明这个对象真的不存在,离线留言是没有意义的,因此这个时候告诉发送方:接收对象不存在!如果要发送的对象确实在数据库中,只是此时没有上线,则我们需要将这部分Message暂时保存在数据库中,其中一个不错的方法是使用ConcurrentHashMap>进行保存【万能的映射哈哈】。将要发送给对应用户的信息保存在Vector中,当有一个新的连接建立时就以此连接作为关键字ID去映射中取值【即这个新用户是不是我们要等的人】,如果取得到值,则拿出对应Vector里面的Message进行发送;若取不到值说明此用户没有未接收的离线信息,不进行其他操作

④ 如何实现群发

群发其实原理与私聊类似,不过私聊是具体到某个人,而群发是遍历映射,对除了自己以外的所有通道发送Message

⑤ 如何实现退出客户端

当客户端选择退出时,说明客户不再从Socket通道接收数据,也不再从Socket通道发送数据,因此此时应该是完全的进程退出,可以使用System.exit(0)。在退出关闭Socket通道之前必须要告知服务器,否则服务器不知道通道已经关闭而一直使用不存在的通道,这显然会带来很多异常。

服务器收到客户端发来的请求退出的Message后,将映射中维护的此Socket移除,同时需要让自己的循环等待接收Message的线程结束,关闭这条通道的socket

友情提示:服务器开启后才能用客户端连接!!

6. 源码

QQServer项目

common包

1. Message.java

package common;

import java.io.Serializable;

public class Message implements Serializable {

private static final long serialVersionUID=1L;

private String sender; //发送者

private String getter; //接收者

private String content; //内容

private String sendTime; //发送时间

private String messageType; //消息类型

public Message(String sender, String messageType) {

this.sender = sender;

this.messageType = messageType;

}

public Message() {

}

public String getSender() {

return sender;

}

public void setSender(String sender) {

this.sender = sender;

}

public String getGetter() {

return getter;

}

public void setGetter(String getter) {

this.getter = getter;

}

public String getContent() {

return content;

}

public void setContent(String content) {

this.content = content;

}

public String getSendTime() {

return sendTime;

}

public void setSendTime(String sendTime) {

this.sendTime = sendTime;

}

public String getMessageType() {

return messageType;

}

public void setMessageType(String messageType) {

this.messageType = messageType;

}

}

2. MessageType.java

package common;

public interface MessageType {

String MESSAGE_LOGIN_SUCCESS="1"; //登录成功

String MESSAGE_LOGIN_FAIL="2"; //登录失败

String MESSAGE_COMM_MES="3";//普通信息包

String MESSAGE_GET_ONLINE_FRIEND="4"; //得到在线用户列表

String MESSAGE_RET_ONLINE_FRIEND="5";//返回在线用户列表

String MESSAGE_CLIENT_EXIT="6"; //客户请求退出

String MESSAGE_CLIENT_NO_EXIST="7"; //发送目标不存在

String MESSAGE_CLIENT_OFFLINE="8"; //发送目标不在线

}

3. User.java

package common;

import java.io.Serializable;

public class User implements Serializable {

private static final long serialVersionUID=1L;

private String userId; //用户ID

private String passwd; //用户密码

public User(String userId, String passwd) {

this.userId = userId;

this.passwd = passwd;

}

public User() {

}

public String getUserId() {

return userId;

}

public void setUserId(String userId) {

this.userId = userId;

}

public String getPasswd() {

return passwd;

}

public void setPasswd(String passwd) {

this.passwd = passwd;

}

}

4. Utility.java

package common;

import java.util.Scanner;

public class Utility {

public static Scanner scanner = new Scanner(System.in);

/**

* 从控制台读取长度字符串

* @return

*/

public static String readString(){

String content = scanner.nextLine();//读取首行

return content;

}

}

main包

1. QQServer.java【服务器主函数】

package main;

import common.Message;

import common.MessageType;

import common.User;

import server.ManageServerConnectClientThread;

import server.ServerConnectClientThread;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.concurrent.ConcurrentHashMap;

public class QQServer {

ServerSocket serverSocket;

//模拟用户数据库

private static ConcurrentHashMap userMap = new ConcurrentHashMap<>();

static {

userMap.put("123", new User("123", "123"));

userMap.put("tom", new User("tom", "123"));

userMap.put("捉妖龙", new User("捉妖龙", "123"));

}

public static void main(String[] args) {

new QQServer();

}

public QQServer() {

try {

System.out.println("在9999端口监听……");

serverSocket = new ServerSocket(9999);

while (true) {

Socket socket = serverSocket.accept();

ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());

ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

User user = (User) ois.readObject();

//构建一个Message对象,准备回复

Message message = new Message();

//验证账号密码是否正确

if (isUser(user.getUserId(), user.getPasswd())) {

message.setMessageType(MessageType.MESSAGE_LOGIN_SUCCESS);

oos.writeObject(message);

ServerConnectClientThread thread = new ServerConnectClientThread(socket, user.getUserId());

thread.start();

ManageServerConnectClientThread.addThread(user.getUserId(), thread);

ManageServerConnectClientThread.sendOffLineMessage(user.getUserId(), oos);

} else {

message.setMessageType(MessageType.MESSAGE_LOGIN_FAIL);

oos.writeObject(message);

socket.close();

}

}

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

serverSocket.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

public static ConcurrentHashMap getUserMap() {

return userMap;

}

public boolean isUser(String userId, String pw) {

User user = userMap.get(userId);

//没有这个用户

if (user == null) {

return false;

}

//密码不正确

if (!user.getPasswd().equals(pw)) {

return false;

}

return true;

}

public static boolean isUser(String userId) {

User user = userMap.get(userId);

if (user == null) {

return false;

}

return true;

}

}

server包

1. ManageServerConnectClientThread.java

package server;

import common.Message;

import common.MessageType;

import java.io.IOException;

import java.io.ObjectOutputStream;

import java.net.Socket;

import java.util.*;

import java.util.concurrent.ConcurrentHashMap;

import java.util.concurrent.ConcurrentMap;

/**

* 管理线程的类

*/

public class ManageServerConnectClientThread {

public static ConcurrentHashMap map = new ConcurrentHashMap<>();

public static ConcurrentHashMap> messageMap = new ConcurrentHashMap<>();

public static String getOnlineFriends() {

StringBuilder builder = new StringBuilder();

for (Map.Entry entry : map.entrySet()) {

builder.append(entry.getKey() + " ");

}

return builder.toString();

}

public static Socket getSocketById(String userId) {

ServerConnectClientThread thread = map.get(userId);

if (thread == null) {

return null;

} else {

return thread.getSocket();

}

}

/**

* 向所有的Socket发送消息

* @param socket 除了这个

* @param oos

*/

public static void sendAll(Socket socket, ObjectOutputStream oos, Message message) {

try {

for (Map.Entry entry : map.entrySet()) {

Socket socket1 = getSocketById(entry.getKey());

if (socket1 != socket) {

oos = new ObjectOutputStream(socket1.getOutputStream());

oos.writeObject(message);

}

}

} catch (IOException e) {

e.printStackTrace();

}

}

/**

* 服务器暂存离线信息

* @param userId 接受者ID

* @param message 需要发送的信息

*/

public static void addMessage(String userId, Message message) {

Vector vector = messageMap.get(userId);

if (vector == null) {

vector = new Vector<>();

messageMap.put(userId, vector);

}

vector.add(message);

}

/**

* 尝试将服务器库存信息进行发送

* @param userId 接受者ID

* @param oos 输出流

*/

public static void sendOffLineMessage(String userId, ObjectOutputStream oos) {

Vector vector = messageMap.get(userId); //得到库存信息

if (!(vector == null || vector.isEmpty())) {

try {

//说明当前用户有待发送消息

Socket socket = getSocketById(userId);

while (!vector.isEmpty()) {

Message message = vector.get(0);

//将消息按顺序发出去

oos = new ObjectOutputStream(socket.getOutputStream());

oos.writeObject(message);

vector.remove(message);

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

public static ConcurrentHashMap getMap() {

return map;

}

public static void deleteSocket(String userId) {

map.remove(userId);

}

public static void addThread(String useId, ServerConnectClientThread thread) {

map.put(useId, thread);

}

}

2. ServerConnectClientThread.java

package server;

import common.Message;

import common.MessageType;

import common.User;

import main.QQServer;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.net.Socket;

/**

* 该类的一个对象和某个客户端保持通讯

*/

public class ServerConnectClientThread extends Thread {

private Socket socket; //这个线程对应的Socket

private String userId; //对应客户的ID

private boolean flag = true; //是否结束线程的标志

private ObjectInputStream ois; //输入流

private ObjectOutputStream oos; //输出流

public ServerConnectClientThread(Socket socket, String userId) {

this.socket = socket;

this.userId = userId;

}

@Override

public void run() {

System.out.println("服务器与客户【" + userId + "】保持通信……");

while (flag) {

try {

ois = new ObjectInputStream(socket.getInputStream());

Message message = (Message) ois.readObject();

actionByMessageType(message);

} catch (Exception e) {

e.printStackTrace();

}

}

}

public void actionByMessageType(Message message) {

try {

switch (message.getMessageType()) {

case MessageType.MESSAGE_GET_ONLINE_FRIEND:

//对方请求列表

System.out.println("【" + message.getSender() + "】要看在线用户列表……");

Message message1 = new Message();

message1.setMessageType(MessageType.MESSAGE_RET_ONLINE_FRIEND);

message1.setGetter(message.getSender());//发送者变接受者

//消息内容为找到的内容

message1.setContent(ManageServerConnectClientThread.getOnlineFriends());

oos = new ObjectOutputStream(socket.getOutputStream());

oos.writeObject(message1);

break;

case MessageType.MESSAGE_CLIENT_EXIT:

ManageServerConnectClientThread.deleteSocket(userId);

flag = false; //此线程结束的标志

ManageServerConnectClientThread.deleteSocket(userId); //从线程集合中除名

socket.close(); //将这个Socket移除

System.out.println("用户【" + userId + "】断开连接!");

break;

case MessageType.MESSAGE_COMM_MES:

if (message.getGetter().equals("All")) {

//说明这是群发消息

ManageServerConnectClientThread.sendAll(socket, oos, message);

break;

}

//得到目标的Socket

Socket socket = ManageServerConnectClientThread.getSocketById(message.getGetter());

Message message2 = new Message();

if (QQServer.isUser(message.getGetter())) {//看此用户是否在数据库中

//注册的用户里有这号人

if (socket == null) {

//发回原处,告知当前用户离线,已经留言

socket = this.socket;

message2.setMessageType(MessageType.MESSAGE_CLIENT_OFFLINE);

message2.setGetter(message.getGetter());

//把消息放进消息盒

ManageServerConnectClientThread.addMessage(message.getGetter(), message);

} else {

message2 = message;

}

} else {

//数据库的用户里没有这号人

socket = this.socket;

message2.setMessageType(MessageType.MESSAGE_CLIENT_NO_EXIST);

message2.setGetter(message.getGetter());

}

oos = new ObjectOutputStream(socket.getOutputStream());

oos.writeObject(message2);

break;

}

} catch (Exception e) {

System.out.println("出现异常!");

}

}

public Socket getSocket() {

return socket;

}

}

QQClient项目

common包

1. Message.java

package common;

import java.io.Serializable;

public class Message implements Serializable {

private static final long serialVersionUID=1L; //版本兼容标志

private String sender; //发送者

private String getter; //接收者

private String content; //内容

private String sendTime; //发送时间

private String messageType; //消息类型

public Message(String sender, String messageType) {

this.sender = sender;

this.messageType = messageType;

}

public Message() {

}

public String getSender() {

return sender;

}

public void setSender(String sender) {

this.sender = sender;

}

public String getGetter() {

return getter;

}

public void setGetter(String getter) {

this.getter = getter;

}

public String getContent() {

return content;

}

public void setContent(String content) {

this.content = content;

}

public String getSendTime() {

return sendTime;

}

public void setSendTime(String sendTime) {

this.sendTime = sendTime;

}

public String getMessageType() {

return messageType;

}

public void setMessageType(String messageType) {

this.messageType = messageType;

}

}

2. MessageType.java

package common;

public interface MessageType {

String MESSAGE_LOGIN_SUCCESS="1"; //登录成功

String MESSAGE_LOGIN_FAIL="2"; //登录失败

String MESSAGE_COMM_MES="3";//普通信息包

String MESSAGE_GET_ONLINE_FRIEND="4"; //得到在线用户列表

String MESSAGE_RET_ONLINE_FRIEND="5";//返回在线用户列表

String MESSAGE_CLIENT_EXIT="6"; //客户请求退出

String MESSAGE_CLIENT_NO_EXIST="7"; //发送目标不存在

String MESSAGE_CLIENT_OFFLINE="8"; //发送目标不存在

}

3. User.java

package common;

import java.io.Serializable;

public class User implements Serializable {

private static final long serialVersionUID=1L;

private String userId; //用户ID

private String passwd; //用户密码

public User(String userId, String passwd) {

this.userId = userId;

this.passwd = passwd;

}

public User() {

}

public String getUserId() {

return userId;

}

public void setUserId(String userId) {

this.userId = userId;

}

public String getPasswd() {

return passwd;

}

public void setPasswd(String passwd) {

this.passwd = passwd;

}

}

4. Utility.java

package common;

import java.util.Scanner;

public class Utility {

public static Scanner scanner = new Scanner(System.in);

/**

* 从控制台读取长度字符串

* @return

*/

public static String readString(){

String content = scanner.nextLine();//读取首行

return content;

}

}

client包

1. QQView.java【客户端主函数】

package client;

import client.service.UserClientService;

import common.Utility;

public class QQView {

private boolean loop = true; //控制是否显示菜单

private String key = ""; //接收用户键盘输入

private UserClientService userClientService = new UserClientService();

public static void main(String[] args) {

new QQView().mainMenu();

}

public void mainMenu() {

while (loop) {

System.out.println("============欢迎登录网络通信系统============");

System.out.println("\t\t 1 登录系统");

System.out.println("\t\t 9 退出系统");

key = Utility.readString();

switch (key) {

case "1":

System.out.print("请输入用户号:");

String userId = Utility.readString();

System.out.print("请输入密 码:");

String pwd = Utility.readString();

//去服务端看看用户是否合法

if (userClientService.checkUser(userId, pwd)) {

System.out.println("============欢迎【" + userId + "】登录网络通信系统============");

//由此进入二级菜单

while (loop) {

System.out.println("============【" + userId + "】网络通信系统二级菜单============");

System.out.println("\t\t 1 显示在线用户列表");

System.out.println("\t\t 2 群发消息");

System.out.println("\t\t 3 私聊消息");

System.out.println("\t\t 9 退出系统");

System.out.print("请输入你的选择:");

key = Utility.readString();

String name; //发送给谁

String contents; //消息内容

switch (key) {

case "1":

userClientService.onlineFriendList();

break;

case "2":

System.out.print("群发内容:" );

contents = Utility.readString();

userClientService.sendAll(contents);

break;

case "3":

System.out.print("发送给:");

name = Utility.readString();

System.out.print("内容:" );

contents = Utility.readString();

userClientService.Send(name,contents);

break;

case "9":

userClientService.closedComm();

System.out.println("客户端退出...");

loop = false;

break;

}

try {

Thread.sleep(5); //为了输出好看些

} catch (InterruptedException e) {

e.printStackTrace();

}

}

} else {

System.out.println("登录失败!");

break;

}

case "9":

loop = false;

break;

}

}

}

}

client.service包

1. ClientConnectServerThread.java

package client.service;

import common.Message;

import common.MessageType;

import java.io.ObjectInputStream;

import java.net.Socket;

public class ClientConnectServerThread extends Thread {

//该线程需要持有Socket对象

private Socket socket;

private String userId;

private ObjectInputStream ois;

public ClientConnectServerThread(Socket socket, String userId) {

this.socket = socket;

this.userId = userId;

}

@Override

public void run() {

//后台Socket服务器要一直保持通讯

while (true) {

try {

ois = new ObjectInputStream(socket.getInputStream());

//如果在流中没有读取到这一对象,则会停顿在此处

Message message = (Message) ois.readObject();

actionByMessageType(message);//根据收到的消息类型做出反应

} catch (Exception e) {

e.printStackTrace();

}

}

}

public void actionByMessageType(Message message) {

switch (message.getMessageType()) {

case MessageType.MESSAGE_RET_ONLINE_FRIEND:

//客户收到服务器的返回信息,信息内容就是在线人数

String[] split = message.getContent().split(" ");

System.out.println("在线用户如下:");

for (String name : split) {

System.out.println("用户:" + name);

}

break;

case MessageType.MESSAGE_COMM_MES:

//收到普通消息,提出内容、发送者、发送时间打印在控制台

System.out.println(message.getSendTime());

if (message.getGetter().equals("All")) {

//说明这是群发消息

System.out.println("【" + message.getSender() + "】对【所有人】说:");

} else {

System.out.println("【" + message.getSender() + "】对【我】说:");

}

System.out.print(message.getContent() + "\n\n请输入你的选择:");

break;

case MessageType.MESSAGE_CLIENT_NO_EXIST:

//私聊目标不存在

System.out.println("客户【" + message.getGetter() + "】不存在,无法发送!");

break;

case MessageType.MESSAGE_CLIENT_OFFLINE:

System.out.println("客户【" + message.getGetter() + "】不在线,其在线后会收到消息!");

}

}

public Socket getSocket() {

return socket;

}

}

2. UserClientService.java

package client.service;

import common.Message;

import common.MessageType;

import common.User;

import common.Utility;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.net.InetAddress;

import java.net.Socket;

import java.util.Calendar;

/**

* 用于客户端发送数据给服务端

*/

public class UserClientService {

private User user = new User(); //当前客户

private Socket socket; //当前客户对应的Socket

private boolean flag = false; //登录是否成功的标志

private ObjectOutputStream oos;

private ObjectInputStream ois;

public boolean checkUser(String userId, String pwd) {

try {

//封装一个User对象,发送到服务器进行检查

user.setUserId(userId);

user.setPasswd(pwd);

//连接到服务器

socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);

oos = new ObjectOutputStream(socket.getOutputStream());

//将用户对象发送出去

oos.writeObject(user);

ois = new ObjectInputStream(socket.getInputStream());

//对面会将消息封装为一个Message对象

Message message = (Message) ois.readObject();

//此时登录成功

if (message.getMessageType().equals(MessageType.MESSAGE_LOGIN_SUCCESS)) {

ClientConnectServerThread thread = new ClientConnectServerThread(socket, userId);

thread.start();

flag = true;

} else {

socket.close();

}

} catch (Exception e) {

e.printStackTrace();

}

return flag;

}

/**

* 拉取在线客户

*/

public void onlineFriendList() {

//这是一条拉取列表的信息

Message message = new Message(user.getUserId(), MessageType.MESSAGE_GET_ONLINE_FRIEND);

try {

//每次使用流就要重新绑定一次

oos = new ObjectOutputStream(socket.getOutputStream());

oos.writeObject(message);

} catch (IOException e) {

e.printStackTrace();

}

}

/**

* 发送一条结束通道的信息给服务器

*/

public void closedComm() {

try {

Message message = new Message(user.getUserId(), MessageType.MESSAGE_CLIENT_EXIT);

oos = new ObjectOutputStream(socket.getOutputStream());

oos.writeObject(message);

socket.close(); //将当前类的socket通道关闭

System.exit(0);//结束进程及由此进程引发的所有线程

} catch (IOException e) {

e.printStackTrace();

}

}

/**

* 与某人私聊,并发送信息

*/

public void Send(String name, String contents) {

try {

Message message1 = new Message();

message1.setMessageType(MessageType.MESSAGE_COMM_MES);//这是一条普通消息

message1.setGetter(name);//接收人

message1.setContent(contents);

message1.setSender(user.getUserId());

message1.setSendTime(formatTime());

oos = new ObjectOutputStream(socket.getOutputStream());

oos.writeObject(message1);

} catch (IOException e) {

e.printStackTrace();

}

}

public void sendAll(String contents) {

Send("All", contents);

}

public String formatTime() {

Calendar instance = Calendar.getInstance();

int hour = instance.get(Calendar.HOUR_OF_DAY);

int min = instance.get(Calendar.MINUTE);

StringBuilder builder = new StringBuilder("\n");

if (hour < 10)

builder.append("0");

builder.append(hour+":");

if (min < 10)

builder.append("0");

builder.append(min);

return builder.toString();

}

}

7. 总结

本程序参考了韩顺平老师的网课B站链接,韩老师的课很好,很适合初学者,同时韩老师也是建议初学者可以把这个小项目吃透,这样对Socket和多线程会有更深一步的理解,这也是我写这篇博客的原因,帮助自己从头到尾整理一遍思路,当然,如果能帮助到你就更好了!