Socket通信与多线程问题对于初学者来说是比较混乱的东西,尤其两者又时常一起出现,因此经常把初学者搞得晕头转向。本文将对通过实现一个简单的聊天项目帮助初学者更好的理解Socket通信与多线程,重点在于实现功能的技术,因此图形化设计的过程省略了,将整个界面以及输入输出都放在控制台显示。
聊天室程序演示
学过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"; //发送目标不在线
}
一条Socket通道对应一条连接,服务器的Socket是通过侦听得来的。因此在服务器需要利用一个循环不断进行侦听,当侦听到这个连接时,就建立起了一条连接,账号密码验证通过后将这条连接保存到一个线程类中,启动这个线程类对这条通道进行维护,通过接收到的Message类型不同而进行不同应答,主函数则继续侦听……
利用ConcurrentHashMap<用户名,线程类>映射【用法同HashMap,不过在多线程中使用ConcurrentHashMap更安全】对线程类进行维护,当我们需要取得某条通道的时候,只需要通过用户名就能得出来;当某条Socket通道被弃用时,将其从映射中移除,通过这个映射,我们也可以得知有几个用户目前在线。
我们的客户端界面会根据我们的输入进行信息的发送,但是同时我们还需要进行接收,因此接收的功能可以另开一个线程去实现,这样就可以实现发送/接收并发进行
原理类似,当账号密码都正确时,开启一个线程类,这个线程类可以对Socket通道的接收进行维护,根据接收的不同类型Message而作出相应处理工作
由于本项目的重点在于Socket通信与多线程理解,因此对于数据库方面利用了ConcurrentHashMap<用户名,用户>映射来进行模拟。
即客户端的登录信息通过Socket发送到服务器端,服务端通过比对两者账号密码,如果一致就返回登录成功的信息,并进行线程开启等等一系列操作……
//模拟用户数据库
private static ConcurrentHashMap<String, User> 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
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;
}
}
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"; //发送目标不在线
}
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;
}
}
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;
}
}
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<String, User> 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<String, User> 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;
}
}
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<String, ServerConnectClientThread> map = new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, Vector<Message>> messageMap = new ConcurrentHashMap<>();
public static String getOnlineFriends() {
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, ServerConnectClientThread> 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<String, ServerConnectClientThread> 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<Message> 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<Message> 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<String, ServerConnectClientThread> getMap() {
return map;
}
public static void deleteSocket(String userId) {
map.remove(userId);
}
public static void addThread(String useId, ServerConnectClientThread thread) {
map.put(useId, thread);
}
}
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;
}
}
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;
}
}
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"; //发送目标不存在
}
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;
}
}
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;
}
}
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;
}
}
}
}
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;
}
}
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();
}
}
本程序参考了韩顺平老师的网课B站链接,韩老师的课很好,很适合初学者,同时韩老师也是建议初学者可以把这个小项目吃透,这样对Socket和多线程会有更深一步的理解,这也是我写这篇博客的原因,帮助自己从头到尾整理一遍思路,当然,如果能帮助到你就更好了!