一、目的
使用Socket实现群聊和私聊功能
二、功能和使用规范
- 1.每个客户端一个名称
- 2.向某一个客户端发起私聊
- 3.群聊
客户端只能向服务器端发送文件或者字符
服务器端只能得到客户端发来的数据
必须客户端和服务器端有一个规范
客户端的需求可以在发送的字符里面体现 - 1.登录 U+ 姓名 U+
- 2.返回结果 成功1 失败-1
- 3.私聊 P+ 姓名:聊天内容 P+
- 4.群聊 a+ 聊天内容 a+
- 5.发文件 f+
- 6.发语音 v+
三、具体实现
1.定义规范
public interface ChatProtocol {
//登录
String LOGIN_FLAG="u+";
//私聊
String PRIVATE_FLAG="p+";
//群聊
String PUBLIC_FLAG="a+";
//分隔符
String SPLIT_FLAG="♥";
//成功的状态
String SUCCESS="1";
String FALURE="-1";
}
2.定义UserManager类
管理所有的登录的用户Map
判断某个用户是否已经登录
(1)保存所以用户信息
public Map users = new HashMap<>();
(2)判断用户是否已经登录
public synchronized boolean isLogined(String name) {
//遍历数组
for (String key : users.keySet()) {
if (key.equals(name)) {
return true;
}
}
return false;
}
(3)保存当前登录的用户信息
public synchronized void sava(String name, Socket socket) {
users.put(name, socket);
}
(4)通过用户名找到对应的socket
public synchronized Socket socketByName(String name) {
return users.get(name);
}
(5)通过socket对象找到对应的名称
public synchronized String nameBySocket(Socket socket) {
for (String key : users.keySet()) {
//取出这个key对应的socket
if (socket == users.get(key)) {
return key;
}
}
return null;
}
(6)获取所有人的socket对象
public synchronized Collection allUsers(){
return users.values();
}
3.定义Server类模拟服务器端
(1)主线程管理监听客户端的连接
public class Server {
//用于保存每一个用户对应的姓名和socket
public static UserManager manager=new UserManager();
public static void main(String[] args) {
//创建ServerSocket
try (ServerSocket ss = new ServerSocket(8888)) {
//监听所有来连接的客户端
while (true){
Socket socket=ss.accept();
//让子线程处理这个socket
new ServerThread(socket).start();
}
}catch (IOException e){
}
}
}
(2)子线程处理每个客户端的输入输出
class ServerThread extends Thread{
public Socket socket;
public ServerThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader br=null;
PrintStream ps=null;
//登录
//得到对应的输入流对象
try {
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//得到对应的输出流
ps=new PrintStream(socket.getOutputStream());
(3)判断是否是登录
String line=null;
while ((line=br.readLine())!=null){
//登录 u+....u+
if (line.startsWith(ChatProtocol.LOGIN_FLAG)&&line.endsWith(ChatProtocol.LOGIN_FLAG)){
//u+jacku+
String name=line.substring(2,line.length()-2);
//判断这个用户是否已经登录
if (Server.manager.isLogined(name)){
//登录过了
//发送结果给客户端
ps.println(ChatProtocol.FALURE);
}else{
//没有登录
//保存当前登录的用户信息
System.out.println(name+"已登录");
Server.manager.sava(name,socket);
//发送结果给客户端
ps.println(ChatProtocol.SUCCESS);
}
}
(4)私聊
//判断是不是私聊
else if (line.startsWith(ChatProtocol.PRIVATE_FLAG)&&line.endsWith(ChatProtocol.PRIVATE_FLAG)){
//p+jack♥hellop+
//获取信息
String msg=line.substring(2,line.length()-2);
//分割 jack hello
String[] items=msg.split(ChatProtocol.SPLIT_FLAG);
//用户名
String name=items[0];
//聊天内容
String message=items[1];
//通过用户名找到对应socket
Socket desSocket=Server.manager.socketByName(name);
PrintStream desPs=new PrintStream(desSocket.getOutputStream());
//获取当前用户的名称
String currentName=Server.manager.nameBySocket(socket);
//发送私聊消息
desPs.println(currentName+"向你发来私聊:"+message);
}
(5)群聊
else {
//群聊
//
String msg=line.substring(2,line.length()-2);
//获取当前用户的名称
String currentName=Server.manager.nameBySocket(socket);
//遍历所有用户信息
Collection sockets=Server.manager.allUsers();
for (Socket s:sockets){
PrintStream tempps=new PrintStream(s.getOutputStream());
tempps.println(currentName+":"+msg);
//tempps.close();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.定义Client类模拟客户端
(1)主线程
public class Client {
public static void main(String[] args) {
BufferedReader br=null;
PrintStream ps=null;
BufferedReader brServer=null;
//连接服务器
try (Socket socket = new Socket("10.129.11.163", 9300)) {
//登录
//接收终端收入流
br=new BufferedReader(new InputStreamReader(System.in));
//发给服务器的输出流
ps=new PrintStream(socket.getOutputStream());
//接收服务器端的输入流
brServer=new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true){
//接收终端输入信息
//BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//String line=br.readLine();
String line=JOptionPane.showInputDialog("请输入用户名");
//拼接登陆格式
String loginStr=ChatProtocol.LOGIN_FLAG+line+ChatProtocol.LOGIN_FLAG;
//发送给服务器端
ps.println(loginStr);
//接收服务器端返回的结果
String result=brServer.readLine();
//判断登录结果
if (result.equals(ChatProtocol.SUCCESS)){
System.out.println(line+"登陆成功");
break;
}else{
System.out.println("用户名以存在 请重新登录");
}
}
//登录成功
//开启线程处理服务器端的输入
new ClientThread(socket).start();
//接收终端输入 发送给服务器端
String line=null;
while ((line=br.readLine())!=null){
//发送给服务器
ps.println(line);
}
}catch (IOException e){
}
}
}
(2)子线程
class ClientThread extends Thread{
private Socket socket;
public ClientThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader br=null;
try {
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line=null;
while ((line=br.readLine())!=null){
System.out.println(line);
}
} catch (IOException e) {
System.out.println("网络出错");
}finally {
try {
if (br!=null){
br.close();
}
if (socket!=null){
socket.close();
}
}catch (IOException e){
}
}
}
}