群聊内实现私聊功能
首先我们想到的是,消息发过来,我怎么知道是公聊消息还是私聊消息呢。所以,这里需要对消息进行处理,比如说在消息前后都加上一些特殊的字符,我们称为协议字符。为此,我们可以定义一个接口,专门来定义协议字符。
第二个问题就是,如果是私聊信息,客户端会将目的用户(私聊对象)发给服务器端,那么服务器端是如何将找到那个目的用户的呢。这里,很明显,我们需要建立一个用户和Socket的映射关系,所以我们采用了map,但是这里的map我们需要改进一下,因为其实我们这里不仅仅是key不能重复,而且value也不能重复,我们也需要通过value能够查找到key,所以我们进行了改进。
还有一点针对本实现需要指出的是,服务器子线程负责接收和发送消息,这里面也包括客户端首次建立连接的时候,需要判断用户名是否重复,也就是要保证key不重复,于此想对应的,客户端在首次建立连接时,其需要进行不断的尝试,直到提供的名字不重复为止。
代码如下:
public interface CrazyitProtocol {
public static final int PROTOCOL_LEN=2; //默认的类型就是public static final,不加也是可以的
public static final String MSG_ROUND="△▽";
public static final String USR_ROUND="□☆";
public static final String LOGIN_SUCCESS="☆▷";
public static final String NAME_REP="-1";
public static final String PRAVITE_ROUND="◆★";
public static final String SPLIT_SIGN="☀";
}
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
public class CrazyitMap<K,V> extends HashMap<K,V> {
// 根据value来删除指定项
public void removeByValue(Object value)
{
for(Object key :keySet())
{
if(get(key)==value||get(key).equals(value))
{
remove(key);
break;
}
}
}
// 获取value集合
public Set<V> valueSet()
{
Set<V> result=new HashSet<V>();
for(Object key : keySet())
{
result.add(get(key));
}
return result;
}
// 重写HashMap的put方法,该方法不允许value重复
public V put(K key,V value)
{
for(V val : valueSet())
{
if(val==value||val.equals(value))
{
throw new RuntimeException("MyMap实例中不允许有重复value");
}
}
return super.put(key, value);
}
// 通过value查找key
public K getKeyByValue(Object value)
{
for(K key : keySet())
{
if(get(key)==value||get(key).equals(value))
{
return key;
}
}
return null;
}
}
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private static final int PORT=30000;
public static CrazyitMap<String,PrintStream> clients=new CrazyitMap<>();
void init()
{
try (
ServerSocket ss=new ServerSocket(PORT);
)
{
while(true)
{
Socket s=ss.accept();
new Thread(new ServerThread(s)).start();
}
}catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("服务器启动失败,是否端口被占用?");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Server s=new Server();
s.init();
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class ServerThread implements Runnable {
private Socket s;
private BufferedReader br=null;
private PrintStream ps=null;
public ServerThread(Socket s)
{
this.s=s;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
ps=new PrintStream(s.getOutputStream());
String content=null;
while((content=br.readLine())!=null)
{
if(content.startsWith(CrazyitProtocol.USR_ROUND) //发过来的是名字信息
&&content.startsWith(CrazyitProtocol.USR_ROUND))
{
String userName=getRealMsg(content);
if(Server.clients.containsKey(userName)) // 姓名重复
{
System.out.println("重复");
ps.println(CrazyitProtocol.NAME_REP);
}
else // 姓名不重复
{
System.out.println("成功");
Server.clients.put(userName, ps);
ps.println(CrazyitProtocol.LOGIN_SUCCESS);
}
}
else if(content.startsWith(CrazyitProtocol.PRAVITE_ROUND)
&&content.startsWith(CrazyitProtocol.PRAVITE_ROUND))// 发过来的是实际的消息,且为私聊消息
{
String userAndMsg=getRealMsg(content);
String userName=userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[0];
String Msg=userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[1];
// 获取私聊用户的输出流
Server.clients.get(userName).println(Server.clients.getKeyByValue(ps)
+"悄悄的对你说"+Msg);
}
else // 公聊信息
{
String Msg=getRealMsg(content);
for(PrintStream ps : Server.clients.valueSet())