多用户 实时通讯

多用户 实时通讯

项目介绍与技术

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 message=new Message();//验证if(checkUser(u.getUserId(),u.getPasswd())){//登录成功message.setMesType(MessageType.MESSAGE_LOGIN_SUCCEED);//将message对象回复oos.writeObject(message);//创建一个线程,和客户端保持通讯,该线程需要持有socket对象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 {//如果服务端退出了while循环,服务器端不再监听。需要关闭serverSockettry {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;}@Overridepublic void run() {//线程处于运行状态,可以发送或接收消息while(true) {System.out.println("服务端和客户端" + userId + "保持通讯,读取数据...");try {ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());//根据message的类型,做相关业务处理Message message=(Message) ois.readObject();if(message.getMesType().equals(MessageType.MESSAGE_GET_ONLINE_FRIEND)){//客户端要在线用户列表System.out.println(message.getSender()+"要在线用户列表");String onlineUser = ManageClientThreads.getOnlineUser();//构建一个message对象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();//关闭连接//退出while循环break;}else if(message.getMesType().equals(MessageType.MESSAGE_COMM_MES)){//服务器端转发ServerConnectClientThread serverConnectClientThread = ManageClientThreads.getServerConnectClientThread(message.getGetter());//得到对应socket的对象输入流,将message对象转发给指定客户端ObjectOutputStream oos = new ObjectOutputStream(serverConnectClientThread.getSocket().getOutputStream());oos.writeObject(message);//转发,如果客户不在线,可以保存到数据库,可以实现离线留言。}else if(message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MES)){//服务器转发到除了发送者之外的用户//遍历管理线程的集合,把所有线程的socket得到,把message进行转发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();//得到对应socket的对象输入流,将message对象转发给指定客户端ObjectOutputStream oos = new ObjectOutputStream(os);oos.writeObject(message);}}}else{System.out.println("其它类型的message,暂时不处理");}} catch (Exception e) {e.printStackTrace();}}}
}

客户端连接服务端线程

public class ClientConnectServerThread extends Thread{//该线程必须持有socketprivate Socket socket;//构造器可以接受一个socket对象public ClientConnectServerThread(Socket socket){this.socket=socket;}@Overridepublic 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();}}}//提供get方法public Socket getSocket(){return socket;}
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部