目的:
- 知道怎么使用Socket实现群聊和私聊
- 使用一个子线程来实现方法
- 定义一个规范来实现你的功能
技术:
- 客户端只能向服务器端发送文件或者字符
- 服务器端只能够得到客户端发来的数据
- 必须客户端和服务器端有一个规范
客户端的需求可以在发送的字符里面体现
1.登录 u+ 姓名 u+
2.返回结果 成功1 失败-1
3.私聊 p+姓名♥聊天内容 p+
4.群聊 a+ 聊天内容 a+
5.发文件 f+
6.发语音 v+
public interface ChartProtocal {
//登录
String LOGTN_FLAG="u+";
//私聊
String PRIVATE_FLAG="p+";
//群聊
String PUBLIC_FLAG="a+";
//分隔符
String SPLIT_FLAG="♥";
//成功状态
String SUCCESS="1";
String FAILURE="-1";
}
- 定义UserManager类 管理理所有登录⽤用的信息
public class UserManager {
//用于保存每一个用户对应的姓名和Socket
private Map<String, Socket> users=new HashMap<>();
//判断某个用户是否已经登录
public synchronized boolean isLodined(String name){
//遍历数组
for (String key:users.keySet()){
if (key.equals(name)){
return true;
}
}
return false;
}
//保存当前登录的用户信息
public synchronized void save(String name,Socket socket){
users.put(name,socket);
}
//通过用户名找到对应的socket
public synchronized Socket socketByname(String name){
return users.get(name);
}
//通过socket对象找到对应的名称
public synchronized String nameBySocket(Socket socket){
for (String key:users.keySet()){
//取出这个key对应的socket
if (socket==users.get(key)){
return key;
}
}
return null;
}
//获取所有人的socket对象
public synchronized Collection<Socket> allusers(){
return users.values();
}
}
- 定义Server类来实现服务器端
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){
}
}
}
- 定义Client来实现客户端
public class Client {
public static void main(String[] args){
BufferedReader br=null;
PrintStream ps=null;
BufferedReader brServer=null;
//连接服务器
try( Socket socket=new Socket("127.0.0.1",8888)){
//登录
//从接收终端输入流
ps=new PrintStream(socket.getOutputStream());
//发送给服务器端的输出流
brServer=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//接收服务器端的输入流
br=new BufferedReader(new InputStreamReader(System.in));
while (true){
//接收终端输入的信息
String line= JOptionPane.showInputDialog("请输入姓名");
//拼接登录格式
String loginStr=ChartProtocal.LOGTN_FLAG+line+ChartProtocal.LOGTN_FLAG;
//发送给服务器端
ps.println(loginStr);
//接收服务器端的结果
String reault=brServer.readLine();
//判断登录结果
if (reault.equals(ChartProtocal.SUCCESS)){
System.out.println(line+"登录成功");
break;
}else {
System.out.println("用户名已存在 请重新登录");
}
}
//登录成功
//开启线程处理服务器端的输入
new ClientThread(socket).start(); 子线程接收服务器端输入
//接收终端输入发送给服务器端
String line;
while ((line=br.readLine())!=null){ 主线程接收终端输入传到服务器
//发送给服务器端
ps.println(line);
}
}catch (IOException e){
}
}
}
- 定义一个子线程来实现客户端的操作
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){
}
}
}
}
- 定义一个子线程来实现服务器端的操作
- 登录的操作:
class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader br=null;
PrintStream ps=null;
try {
//1.获取对应的输入流对象
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
ps=new PrintStream(socket.getOutputStream());
String line=null;
while ((line=br.readLine())!=null){
//登录u+...u+
if (line.startsWith(ChartProtocal.LOGTN_FLAG)&&line.endsWith(ChartProtocal.LOGTN_FLAG)){
// String[] items=line.substring(2).split("u+");
// String name=items[0];
String name=line.substring(2,line.length()-2);
//判断用户是否登录
if (Server.manager.isLodined(name)){
//登录过了
ps.println(ChartProtocal.FAILURE);
}else {
//没有登录
//保存当前登录的用户信息
Server.manager.save(name,socket);
System.out.println(name+"已登录");
ps.println(ChartProtocal.SUCCESS);
}
}
- 私信的操作:
//p+...♥...p+
//判断是不是私聊
else if (line.startsWith(ChartProtocal.PRIVATE_FLAG)&&line.endsWith(ChartProtocal.PRIVATE_FLAG)){
//获取信息
String msg=line.substring(2,line.length()-2);
//分割
String[] items=msg.split(ChartProtocal.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);
}
- 群聊的操作
else {
//a+...a+
//群聊
//处理数据
String msg=line.substring(2,line.length()-2);
//获取当前用户的名称
String currentName=Server.manager.nameBySocket(socket);
//获取所有的用户信息
Collection<Socket> sockets=Server.manager.allusers();
for (Socket s:sockets){
PrintStream temps=new PrintStream(s.getOutputStream());
temps.println(currentName+"发来群聊:"+msg);
//temps.close();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
}
}
}
最后的结果:
总结:
在昨天的基础上进一步进行了今天这个群聊和私聊的操作。在自己重新写了一遍昨天的代码过后再来听今天的课程还算是能够跟上,虽然说还是有许多地方不懂尤其是当创建多个类然后这边看一下那边看一下感觉很容易就懵了,不过能够看了一遍代码然后自己慢慢的跟着写出来其实这也是一种成就感,感觉挺厉害的一些代码写完然后计算机就会跟着你的代码来实现非常的棒。