多用户 实时通讯
项目介绍与技术
socket+多线程
利用socket进行服务端和客户端之间的通信,建立通信之后,使用ObjectOutputStream和ObjectInputStream进行流传输。
实现了,用户登录校验,查询在线用户,用户私聊,消息群发,服务器向用户推送消息,文件发送(我没做)功能。
因为没有用到数据库,所以数据存在内存的hashmap中。
socket可以看作是线程的一个属性。socket连通,然后开启线程。
学习加上实际动手写代码测试,花了两天。
登录模块
实现:客户端连接到本地的9999端口,服务端对9999端口进行监听。
连接成功建立之后,客户端向服务端发送User消息,包含用户的账号密码;
服务端监听到客户端发来的内容,通过ObjectInputStream读取,转换,然后进行后台数据校验,如果通过,说明登录成功,使用ObjectOutputStream给客户端返回Message,开启服务端连接客户端的线程,并且将成功登录的客户端信息存到hashMap中。
客户端向服务端发送用户的账号密码,得到服务端返回的Message,解析后,如果登录成功,创建一个保持通信的线程,开启线程,将线程放入hashmap管理。
在线用户查询
客户端向服务器请求,查询在线用户信息,根据客户端id,获取通信线程,得到socket,得到oos,将消息发给服务端。
服务端收到,解析消息类型,通过hashmap得到在线用户数据,封装成message对象返回给客户端。
用户私聊
用户a发起聊天,将消息以及getter信息发到服务端,服务端执行转发操作。
用户b收到来自服务端的消息,将消息显示到控制台即可。
消息群发
用户a向服务器发,之后服务器将消息转给除了a之外的所有用户。
用户收到类型为MESSAGE_TO_ALL_MES
服务器消息推送
服务器单独开一个线程,执行将消息发送给所有客户端用户。
部分代码
服务端
public class QQServer {
private ServerSocket ss=null;
private static HashMap<String,User> validUsers=new HashMap<>();
static {
validUsers.put("100",new User("100","123456"));
validUsers.put("300",new User("300","123456"));
validUsers.put("200",new User("200","123456"));
validUsers.put("至尊宝",new User("至尊宝","123456"));
validUsers.put("紫霞仙子",new User("紫霞仙子","123456"));
validUsers.put("菩提老祖",new User("菩提老祖","123456"));
}
private boolean checkUser(String userId,String passwd){
User user = validUsers.get(userId);
if(user==null){
return false;
}
if(!user.getPasswd().equals(passwd)){
return false;
}
return true;
}
public QQServer(){
try {
System.out.println("服务端在9999端口监听...");
new Thread(new SendNewsToAllService()).start();
ss=new ServerSocket(9999);
while (true){
Socket socket = ss.accept();
ObjectOutputStream oos=new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
User u=(User)ois.readObject();
Message message=new Message();
if(checkUser(u.getUserId(),u.getPasswd())){
message.setMesType(MessageType.MESSAGE_LOGIN_SUCCEED);
oos.writeObject(message);
ServerConnectClientThread serverConnectClientThread = new ServerConnectClientThread(socket, u.getUserId());
serverConnectClientThread.start();
ManageClientThreads.addClientThread(u.getUserId(),serverConnectClientThread);
}else{
System.out.println("用户id="+u.getUserId()+"pwd="+u.getPasswd()+"登录失败");
message.setMesType(MessageType.MESSAGE_LOGIN_FAIL);
oos.writeObject(message);
socket.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务端连接客户端线程
public class ServerConnectClientThread extends Thread{
private Socket socket;
private String userId;
public ServerConnectClientThread(Socket socket, String userId) {
this.socket = socket;
this.userId = userId;
}
public Socket getSocket(){
return socket;
}
@Override
public void run() {
while(true) {
System.out.println("服务端和客户端" + userId + "保持通讯,读取数据...");
try {
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Message message=(Message) ois.readObject();
if(message.getMesType().equals(MessageType.MESSAGE_GET_ONLINE_FRIEND)){
System.out.println(message.getSender()+"要在线用户列表");
String onlineUser = ManageClientThreads.getOnlineUser();
Message message2 = new Message();
message2.setMesType(MessageType.MESSAGE_RET_ONLINE_FRIEND);
message2.setContent(onlineUser);
message2.setGetter(message.getSender());
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(message2);
}else if (message.getMesType().equals(MessageType.MESSAGE_CLIENT_EXIT)){
System.out.println( message.getSender()+"退出" );
ManageClientThreads.removeServerConnectClientThread(message.getSender());
socket.close();
break;
}else if(message.getMesType().equals(MessageType.MESSAGE_COMM_MES)){
ServerConnectClientThread serverConnectClientThread = ManageClientThreads.getServerConnectClientThread(message.getGetter());
ObjectOutputStream oos = new ObjectOutputStream(serverConnectClientThread.getSocket().getOutputStream());
oos.writeObject(message);
}else if(message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MES)){
HashMap<String, ServerConnectClientThread> hm = ManageClientThreads.getHm();
Iterator<String> iterator = hm.keySet().iterator();
while(iterator.hasNext()){
String onLineUserId = iterator.next().toString();
if(!onLineUserId.equals(message.getSender())){
OutputStream os = hm.get(onLineUserId).getSocket().getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(message);
}
}
}
else{
System.out.println("其它类型的message,暂时不处理");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
客户端连接服务端线程
public class ClientConnectServerThread extends Thread{
private Socket socket;
public ClientConnectServerThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
while (true){
System.out.println("客户端线程,等待读取从服务器端发送的消息");
try {
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Message message = (Message) ois.readObject();
if(message.getMesType().equals(MessageType.MESSAGE_RET_ONLINE_FRIEND)){
String[] onlineUsers = message.getContent().split(" ");
System.out.println("=====当前在线用户列表=====");
for(int i=0;i<onlineUsers.length;i++){
System.out.println("用户:"+onlineUsers[i]);
}
}else if(message.getMesType().equals(MessageType.MESSAGE_COMM_MES)){
System.out.println("\n"+message.getSender()+"对"+message.getGetter()+"说"+message.getContent());
}else if(message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MES)){
System.out.println("\n"+message.getSender()+"对大家说"+message.getContent());
} else{
System.out.println("是其他类型的message,暂时不处理");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public Socket getSocket(){
return socket;
}
}