- QQ群:
- 功能:私聊
- 好友
- 拍照
- 拍视频
- 视频通话
- 发文件
- QQ空间
- 1.每个客户端有个名称
- 2.私聊 向一个客户端发起私聊
- 3.群聊
- 4.发文件
- 客户端只能像服务器端发送文件或字符
- 服务器端只能得到客户端发过来的数据
- 必须客户端和服务器端有一个规范
- 客户端的需求可以再发送的字符里体现
实现功能:
- 1.登陆 u+ 姓名 u+
- 2.返回结果 成功1 失败-1
- 3.私聊 p+ 姓名♥聊天内容p+
- 4.群聊 a+ 聊天内容 a+
- 5.发文件 f+
- 6.发语音
定义规范
//登陆
String LOGIN_FLAG = "u+";
//私聊
String PRIVAIE_FLAG = "p+";
//群聊
String PUBLIC_FLAG = "a+";
//分隔符
String SPLIT_FLAG = "♥";
//成功失败的状态
String SUNCESS="1";
String FAILASE="-1";
定义用户管理
功能
- 管理所有的登陆用户Map
- 判断用户是否登陆
- 保存登陆用户信息
- 通过用户名找到对应的socket
- 通过socket对象找到对应的名称
- 遍历 获取所有人的socket对象
public class usermanager {
//保存所有的用户信息
private Mapusers=new HashMap<>();
/**
* 判断用户是否登路
*/
public boolean isLogined(String name)
{
//遍历数组
for (String key:users.keySet())
{
if (key.equals(name))
{
return true;
}
}
return false;
}
/**
* 保存当前用户的登陆信息
*/
public void save (String name,Socket socket)
{
users.put(name,socket);
}
/**
* 通过用户名找到对应的socket
*/
public Socket socketByname(String name)
{
return users.get(name);
}
/**
* 通过socket对象找到对应的名称
*/
public String nameBySocket(Socket socket)
{
for (String key:users.keySet())
{
//取出这个key对应的socket
if (socket==users.get(key))
{
return key;
}
}
return null;
}
/**
* 获取所有人的socket对象
*/
public synchronized Collection AllUsers(){
return users.values();
}
}
定义服务器端:
- 保存每一个用户的姓名和Socket
- 开启子线程 处理客户端数据
public class Server {
//Map 键值对
//用于保存每一个用户的姓名和Socket
public static usermanager manager=new usermanager();
public static void main(String[] args){
//创建ServerSocket
try(ServerSocket ss=new ServerSocket(8989);){
while (true)
{
Socket socket=ss.accept();
//开启子线程处理socket
new ServerThread(socket).start();
}
}catch (IOException e){
}
}
}
服务器端⼦线程
- 处理每个客户端的输⼊输出
- 定义socket 构造方法
- 获取输入流对象 输出流对象
- 实现功能登陆 私聊 群聊
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 {
//获取输入流对象
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//输出流
ps=new PrintStream(socket.getOutputStream());
String line=null;
while ((line=br.readLine())!=null)
{
子线程功能一 登陆 u+.....u+
- 使用line.substring获取对象的名称
- 判断这个用户是否登陆
- 登陆过了
- 发送结果给客户端 显示成功
- 没有登陆
- 保存当前用户的登陆信息 重新登陆
if (line.startsWith(ChatClass.LOGIN_FLAG)&&line.endsWith(ChatClass.LOGIN_FLAG))
{
// String []items=line.substring(2).split("u+");
// String name=items[0];
//获取名字
String name=line.substring(2,line.length()-2);
//判断这个用户是否登陆
if (Server.manager.isLogined(name))
{
//登陆过了
//发送结果给客户端
ps.println(ChatClass.FAILASE);
}else {
//没有登陆
//保存当前用户的登陆信息
Server.manager.save(name,socket);
ps.println(ChatClass.SUNCESS);
}
}
子线程功能二判断私聊 p+ 姓名♥聊天内容p+
- 获取分割输入流的内容 名称和聊天内容
- 找到当前用户名称
else if(line.startsWith(ChatClass.PRIVAIE_FLAG)&&line.endsWith(ChatClass.PRIVAIE_FLAG))
{
//私聊 p+ 姓名♥聊天内容p+
//获取信息 jack♥hellow
String msg = line.substring(2, line.length() - 2);
//分割 Jack hellow
String[] items = msg.split(ChatClass.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 {
//处理数据
String msg=line.substring(2,line.length()-2);
//获取当前用户的名称
String currentName=Server.manager.nameBySocket(socket);
//遍历所有的用户信息
Collection sockets=Server.manager.AllUsers();
for (Socket s:sockets)
{
PrintStream temps=new PrintStream(s.getOutputStream());
temps.println(currentName+"发来群聊"+msg);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
定义客户端
- 连接服务器
- 发给服务器端的输出流 接受终端的输入流 接受服务器端的输入流
- 接受终端输入 发送个服务端 接收服务器返沪的结果 判断登陆结果
- 如果登陆成功
- 开启线程处理服务器端的输入
public class Client {
public static void main(String[] args){
BufferedReader br=null;
PrintStream ps=null;
BufferedReader brServer=null;
//连接服务器
try(Socket socket=new Socket("192.168.43.164",8989))
{
//发给服务器端的输出流
ps=new PrintStream(socket.getOutputStream());
//接受终端的输入流
br=new BufferedReader(new InputStreamReader(System.in));
//接受服务器端的输入流
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=ChatClass.LOGIN_FLAG+line+ChatClass.LOGIN_FLAG;
//发送个服务端
ps.println(loginStr);
//接收服务器返沪的结果
String result=brServer.readLine();
//判断登陆结果
if (result.equals(ChatClass.SUNCESS))
{
System.out.println("登陆已成功");
break;
}else {
System.out.println("用户已存在 请重新输入");
}
}
//登陆成功
//开启线程处理服务器端的输入
new ClientThread(socket).start();
String line=null;
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)
{
e.printStackTrace();
}
}
}
}
感悟:虽然这个程序敲的我一脸迷糊,不明所以,但是当程序成功运行时,心里是非常的高兴,敲三百多行代码的辛苦,换回了运行成功的几行代码,为的就是那一刻的成就感。