赞
踩
继上一篇实现群聊,现在记录私聊的实现,下一篇记录一下实现文件的传输。
目录
用户输入@name的方式发送消息(类似于QQ你@别人),但是想通过服务器端从信息中解析出这个@的人的名字不太可能,因为服务器端不知道截多长才是用户的名字。所以只能判断信息中是否包含了@name。String类的contains()方法可以做到。
服务端有以下两种思路去实现私聊
先判断用户是否@人了。
如果@人了,则发送消息的用户的服务套接字让其他的服务套接字判断这个@的名字是不是自己服务的用户的名字,如果是就转发,不是就不转发。
如果没@,则全部转发。
先判断用户是否@人了。
如果@人了,发送消息的用户的服务套接字根据@的名字让该名字的服务套接字转发。(哈希表)
如果没@,则全部转发。
我们的用户端只负责输入,因此只需要改变服务端的代码。(这里也只记录服务器端的代码,用户端的代码和工具类代码在上一篇)。
- import java.io.Closeable;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.IOException;
- import java.net.InetAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.ArrayList;
- import java.util.List;
-
- import com.csi.qunliaoTest.OpenClient.ReceiveMsgThread;
-
-
-
- public class OpenServer {
- //一个列表存储服务Socket
- List<ClientSocket> clientSockets =new ArrayList<>();
-
- ServerSocket serverSocket = null;
-
- //构造方法中创建服务器ServerSocket
- public OpenServer() {
- try {
- //因为本代码是在云服务器运行,直接用InetAddress.getLocalHost()方法绑定云服务器的IP地址及端口号
- serverSocket = new ServerSocket(7777, 50, InetAddress.getLocalHost());
- System.out.println("----------服务器----------");
-
- } catch (IOException e) {
- //发生异常。调用自己写的Utils类关闭服务器
- Utils.close(serverSocket);
- }
-
- //ServerSocket绑定成功,开始等待用户接入
- if(serverSocket.isBound())
- acceptClient();
- }
-
- //等待用户,并为他生成服务Socket
- private void acceptClient(){
- //while循环让服务器一直可以接入用户,一个用户接入服务器,服务器就生成一个为该用户服务的Socket
- while(true) {
- Socket socket=null;
- try {
-
- //ServerScoket的accept()方法是一个阻塞方法,他会在这里等用户接入,直到有用户接入,才会运行下面的代码
- socket=serverSocket.accept();
- System.out.println("一个用户接入.....");
- //用ClientSocket类包装Scoket类,ClientSocket类是自己写的内部线程类,该类实现了接收和转发用户消息
- ClientSocket clientSocket=new ClientSocket(socket);
- //开启服务线程
- clientSocket.start();
-
- //将这个包装了服务Scoket的对象添加进列表
- clientSockets.add(clientSocket);
- } catch (IOException e) {
- Utils.close(socket);
- }
-
- }
- }
-
- public static void main(String[] args) {
- //执行服务器代码
- new OpenServer();
- }
-
- class ClientSocket extends Thread{
- Socket socket=null;
- DataInputStream dataInputStream=null;
- DataOutputStream dataOutputStream=null;
- String name = null;
-
- public ClientSocket(Socket socket) {
- this.socket = socket;
- //包装服务Socket的输入输出流,异常就调用closeScoket()方法
- try {
- dataInputStream=new DataInputStream(socket.getInputStream());
- } catch (IOException e) {
- closeSocket(dataInputStream,socket);
- }
- try {
- dataOutputStream=new DataOutputStream(socket.getOutputStream());
- } catch (IOException e) {
- closeSocket(dataOutputStream,socket);
- }
- }
-
-
- //上面的代码我们关闭的都是装饰流,因为关闭装饰流会将内部流也关闭,Socket也会因此关闭,同时我们也要将列表里对应Socket的删除
- public void closeSocket(Closeable...closeables) {
- Utils.close(closeables);
- System.out.println("一位用户退出");
- clientSockets.remove(this);
- }
-
- @Override
- public void run() {
-
-
- //读取用户姓名,并让列表所有用户转发欢迎信息
- //数据流DataIn/OutputStream的readUTF()和writeUTF(String data)要一起用,是将数据以UTF-8的编码方式发出或者接收
-
- try {
- //readUTF()方法也是阻塞方法,读取用户发来的名字
- name=dataInputStream.readUTF();
- } catch (IOException e1) {
- closeSocket(dataInputStream);
- }
- //循环列表,除了自己,其他服务Scoket全部转发消息
- for(ClientSocket clientSocket:clientSockets) {
- if(clientSocket!=this) {
- try {
- clientSocket.dataOutputStream.writeUTF("欢迎"+name+"进入聊天室");
- clientSocket.dataOutputStream.flush();//清空缓存区,让缓存区的数据全部出来
- } catch (IOException e) {
- clientSocket.closeSocket(clientSocket.dataOutputStream);
- }
-
- }
- }
-
- //知道名字后就一直等待接收用户端发的消息,异常就关闭Scoket并跳出循环
- while(true) {
- String msg = null;
- //在读取到的消息前加上姓名
- try {
- msg = name+":"+dataInputStream.readUTF();
-
- } catch (IOException e) {
- closeSocket(dataInputStream);
- break;
- }
- //判断信息中是否@人了,如果@人了,其他每个服务Socket判断@的名字里是不是自己服务的用户名字;如果没有@,则全部转发
- if(msg.contains("@")) {
- for(ClientSocket clientSocket:clientSockets) {
- if(clientSocket!=this) {
- try {
-
- if(msg.contains("@"+clientSocket.name)) { //找到了是自己的用户端名字
- //将@名字替换为空,replace返回一个心得字符串
- clientSocket.dataOutputStream.writeUTF(msg.replace("@"+clientSocket.name, ""));
- clientSocket.dataOutputStream.flush();
- }
- } catch (IOException e) {
- clientSocket.closeSocket(clientSocket.dataOutputStream);
- }
- }
- }
- }
- else {
- for(ClientSocket clientSocket:clientSockets) {
- if(clientSocket!=this) {
- try {
- clientSocket.dataOutputStream.writeUTF(msg);
- clientSocket.dataOutputStream.flush();
- } catch (IOException e) {
- clientSocket.closeSocket(clientSocket.dataOutputStream);
- }
- }
- }
- }
-
-
-
- }
-
- }
- }
-
-
- }
用户名不要写一样的名字,他是根据名字找套接字,当查找到这个名字就去哈希表找对应的套接字,如果名字相同,他只会找第一个与这个名字相同的套接字。
- package com.csi.siliaoTest;
-
- import java.io.Closeable;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.IOException;
- import java.net.InetAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
-
- import com.csi.siliaoTest.OpenServer.ClientSocket;
-
-
-
- public class OpenServer2 {
- //一个哈希表存储用户名字和服务套接字
- HashMap<String, ClientSocket> map= new HashMap<>();
- //一个列表存储服务Socket
- List<ClientSocket> clientSockets =new ArrayList<>();
- //一个列表存储用户名字
- List<String> names = new ArrayList<>();
- ServerSocket serverSocket = null;
-
- //构造方法中创建服务器ServerSocket
- public OpenServer2() {
- try {
- //因为本代码是在云服务器运行,直接用InetAddress.getLocalHost()方法绑定云服务器的IP地址及端口号
- serverSocket = new ServerSocket(7777, 50, InetAddress.getLocalHost());
- System.out.println("----------服务器----------");
-
- } catch (IOException e) {
- //发生异常。调用自己写的Utils类关闭服务器
- Utils.close(serverSocket);
- }
-
- //ServerSocket绑定成功,开始等待用户接入
- if(serverSocket.isBound())
- acceptClient();
- }
-
- //等待用户,并为他生成服务Socket
- private void acceptClient(){
- //while循环让服务器一直可以接入用户,一个用户接入服务器,服务器就生成一个为该用户服务的Socket
- while(true) {
- Socket socket=null;
- try {
-
- //ServerScoket的accept()方法是一个阻塞方法,他会在这里等用户接入,直到有用户接入,才会运行下面的代码
- socket=serverSocket.accept();
- System.out.println("一个用户接入.....");
- //用ClientSocket类包装Socket类,ClientSocket类是自己写的内部线程类,该类实现了接收和转发用户消息
- ClientSocket clientSocket=new ClientSocket(socket);
- //开启服务线程
- clientSocket.start();
-
- //将这个包装了服务Socket的对象添加进列表
- clientSockets.add(clientSocket);
- } catch (IOException e) {
- Utils.close(socket);
- }
-
- }
- }
-
- public static void main(String[] args) {
- //执行服务器代码
- new OpenServer2();
- }
-
- class ClientSocket extends Thread{
- Socket socket=null;
- DataInputStream dataInputStream=null;
- DataOutputStream dataOutputStream=null;
- String name = null;
-
- public ClientSocket(Socket socket) {
- this.socket = socket;
- //包装服务Socket的输入输出流,异常就调用closeScoket()方法
- try {
- dataInputStream=new DataInputStream(socket.getInputStream());
- } catch (IOException e) {
- closeSocket(dataInputStream,socket);
- }
- try {
- dataOutputStream=new DataOutputStream(socket.getOutputStream());
- } catch (IOException e) {
- closeSocket(dataOutputStream,socket);
- }
- }
-
-
- //上面的代码我们关闭的都是装饰流,因为关闭装饰流会将内部流也关闭,Socket也会因此关闭,同时我们也要将列表里对应Socket的删除
- public void closeSocket(Closeable...closeables) {
- Utils.close(closeables);
- System.out.println("一位用户退出");
- clientSockets.remove(this);
- names.remove(name);
- map.remove(name);
- }
-
- @Override
- public void run() {
-
-
- //读取用户姓名,并让列表所有用户转发欢迎信息
- //数据流DataIn/OutputStream的readUTF()和writeUTF(String data)要一起用,是将数据以UTF-8的编码方式发出或者接收
-
- try {
- //readUTF()方法也是阻塞方法,读取用户发来的名字并添加哈希表和列表
- name=dataInputStream.readUTF();
- names.add(name);
- map.put(name, this);
- } catch (IOException e1) {
- closeSocket(dataInputStream);
- }
- //循环列表,除了自己,其他服务Socket全部转发消息
- for(ClientSocket clientSocket:clientSockets) {
- if(clientSocket!=this) {
- try {
- clientSocket.dataOutputStream.writeUTF("欢迎"+name+"进入聊天室");
- clientSocket.dataOutputStream.flush();//清空缓存区,让缓存区的数据全部出来
- } catch (IOException e) {
- clientSocket.closeSocket(clientSocket.dataOutputStream);
- }
-
- }
- }
-
- //知道名字后就一直等待接收用户端发的消息,异常就关闭Scoket并跳出循环
- while(true) {
- String msg = null;
- //在读取到的消息前加上姓名
- try {
- msg = name+":"+dataInputStream.readUTF();
-
- } catch (IOException e) {
- closeSocket(dataInputStream);
- break;
- }
- //判断信息中是否@人了,如果@人了,查找是否有@的这个名字的用户
- if(msg.contains("@")) {
- ClientSocket clientSocket = null;
- for(String aname: names) {
- if (msg.contains("@"+aname)) {
- try {clientSocket =map.get(aname);
- clientSocket.dataOutputStream.writeUTF(msg.replace("@"+aname, ""));
- clientSocket.dataOutputStream.flush();
- } catch (IOException e) {
- clientSocket.closeSocket(clientSocket.dataOutputStream);
-
- }
-
- }
- }
- }
- else {
- for(ClientSocket clientSocket:clientSockets) {
- if(clientSocket!=this) {
- try {
- clientSocket.dataOutputStream.writeUTF(msg);
- clientSocket.dataOutputStream.flush();
- } catch (IOException e) {
- clientSocket.closeSocket(clientSocket.dataOutputStream);
- }
- }
- }
- }
-
-
-
- }
-
- }
- }
-
-
- }
-
注意:不要写一样的用户名字,他是在哈希表里根据名字找套接字,如果名字相同,他只会找第一个与这个名字相同的套接字。如果写一样得名字就只有一个用户会收到@的信息,并且会收到好几条(有几个重名就有几条)。如下图,我写了两个张三。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。