- QQ群:
- 功能:私聊
- 好友
- 拍照
- 拍視頻
- 視頻通話
- 發(fā)文件
- QQ空間
- 1.每個客戶端有個名稱
- 2.私聊 向一個客戶端發(fā)起私聊
- 3.群聊
- 4.發(fā)文件
- 客戶端只能像服務(wù)器端發(fā)送文件或字符
- 服務(wù)器端只能得到客戶端發(fā)過來的數(shù)據(jù)
- 必須客戶端和服務(wù)器端有一個規(guī)范
- 客戶端的需求可以再發(fā)送的字符里體現(xiàn)
實現(xiàn)功能:
- 1.登陸 u+ 姓名 u+
- 2.返回結(jié)果 成功1 失敗-1
- 3.私聊 p+ 姓名?聊天內(nèi)容p+
- 4.群聊 a+ 聊天內(nèi)容 a+
- 5.發(fā)文件 f+
- 6.發(fā)語音
定義規(guī)范
//登陸
String LOGIN_FLAG = "u+";
//私聊
String PRIVAIE_FLAG = "p+";
//群聊
String PUBLIC_FLAG = "a+";
//分隔符
String SPLIT_FLAG = "?";
//成功失敗的狀態(tài)
String SUNCESS="1";
String FAILASE="-1";
定義用戶管理
功能
- 管理所有的登陸用戶Map<String ,Socket>
- 判斷用戶是否登陸
- 保存登陸用戶信息
- 通過用戶名找到對應(yīng)的socket
- 通過socket對象找到對應(yīng)的名稱
- 遍歷 獲取所有人的socket對象
public class usermanager {
//保存所有的用戶信息
private Map<String, Socket>users=new HashMap<>();
/**
* 判斷用戶是否登路
*/
public boolean isLogined(String name)
{
//遍歷數(shù)組
for (String key:users.keySet())
{
if (key.equals(name))
{
return true;
}
}
return false;
}
/**
* 保存當(dāng)前用戶的登陸信息
*/
public void save (String name,Socket socket)
{
users.put(name,socket);
}
/**
* 通過用戶名找到對應(yīng)的socket
*/
public Socket socketByname(String name)
{
return users.get(name);
}
/**
* 通過socket對象找到對應(yīng)的名稱
*/
public String nameBySocket(Socket socket)
{
for (String key:users.keySet())
{
//取出這個key對應(yīng)的socket
if (socket==users.get(key))
{
return key;
}
}
return null;
}
/**
* 獲取所有人的socket對象
*/
public synchronized Collection<Socket> AllUsers(){
return users.values();
}
}
定義服務(wù)器端:
- 保存每一個用戶的姓名和Socket
- 開啟子線程 處理客戶端數(shù)據(jù)
public class Server {
//Map 鍵值對
//用于保存每一個用戶的姓名和Socket
public static usermanager manager=new usermanager();
public static void main(String[] args){
//創(chuàng)建ServerSocket
try(ServerSocket ss=new ServerSocket(8989);){
while (true)
{
Socket socket=ss.accept();
//開啟子線程處理socket
new ServerThread(socket).start();
}
}catch (IOException e){
}
}
}
服務(wù)器端?線程
- 處理每個客戶端的輸?輸出
- 定義socket 構(gòu)造方法
- 獲取輸入流對象 輸出流對象
- 實現(xiàn)功能登陸 私聊 群聊
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獲取對象的名稱
- 判斷這個用戶是否登陸
- 登陸過了
- 發(fā)送結(jié)果給客戶端 顯示成功
- 沒有登陸
- 保存當(dāng)前用戶的登陸信息 重新登陸
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))
{
//登陸過了
//發(fā)送結(jié)果給客戶端
ps.println(ChatClass.FAILASE);
}else {
//沒有登陸
//保存當(dāng)前用戶的登陸信息
Server.manager.save(name,socket);
ps.println(ChatClass.SUNCESS);
}
}
子線程功能二判斷私聊 p+ 姓名?聊天內(nèi)容p+
- 獲取分割輸入流的內(nèi)容 名稱和聊天內(nèi)容
- 找到當(dāng)前用戶名稱
else if(line.startsWith(ChatClass.PRIVAIE_FLAG)&&line.endsWith(ChatClass.PRIVAIE_FLAG))
{
//私聊 p+ 姓名?聊天內(nèi)容p+
//獲取信息 jack?hellow
String msg = line.substring(2, line.length() - 2);
//分割 Jack hellow
String[] items = msg.split(ChatClass.SPLIT_FLAG);
//用戶名
String name = items[0];
//聊天內(nèi)容
String message = items[1];
//通過用戶名找到對應(yīng)的socket
Socket desSocket = Server.manager.socketByname(name);
PrintStream desps = new PrintStream(desSocket.getOutputStream());
//獲取當(dāng)前用戶的名稱
String currentName = Server.manager.nameBySocket(socket);
//發(fā)送私聊信息
desps.println(currentName + "向你發(fā)送私聊" + message);
//
}
子線程功能三 群聊
else {
//處理數(shù)據(jù)
String msg=line.substring(2,line.length()-2);
//獲取當(dāng)前用戶的名稱
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+"發(fā)來群聊"+msg);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
定義客戶端
- 連接服務(wù)器
- 發(fā)給服務(wù)器端的輸出流 接受終端的輸入流 接受服務(wù)器端的輸入流
- 接受終端輸入 發(fā)送個服務(wù)端 接收服務(wù)器返滬的結(jié)果 判斷登陸結(jié)果
- 如果登陸成功
- 開啟線程處理服務(wù)器端的輸入
public class Client {
public static void main(String[] args){
BufferedReader br=null;
PrintStream ps=null;
BufferedReader brServer=null;
//連接服務(wù)器
try(Socket socket=new Socket("192.168.43.164",8989))
{
//發(fā)給服務(wù)器端的輸出流
ps=new PrintStream(socket.getOutputStream());
//接受終端的輸入流
br=new BufferedReader(new InputStreamReader(System.in));
//接受服務(wù)器端的輸入流
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;
//發(fā)送個服務(wù)端
ps.println(loginStr);
//接收服務(wù)器返滬的結(jié)果
String result=brServer.readLine();
//判斷登陸結(jié)果
if (result.equals(ChatClass.SUNCESS))
{
System.out.println("登陸已成功");
break;
}else {
System.out.println("用戶已存在 請重新輸入");
}
}
//登陸成功
//開啟線程處理服務(wù)器端的輸入
new ClientThread(socket).start();
String line=null;
while ((line =br.readLine())!=null)
{
ps.println(line);
}
}catch (IOException e){
}
}
}
定義客戶端子線程
- 獲取服務(wù)器端的輸出流 并輸出
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("網(wǎng)絡(luò)出錯");
}finally {
try{
if (br!=null)
{
br.close();
}
if (socket!=null)
{
socket.close();
}
}catch (IOException e)
{
e.printStackTrace();
}
}
}
}
感悟:雖然這個程序敲的我一臉迷糊贰盗,不明所以,但是當(dāng)程序成功運行時童太,心里是非常的高興,敲三百多行代碼的辛苦书释,換回了運行成功的幾行代碼,為的就是那一刻的成就感爆惧。