一徒像、目的
使用Socket實(shí)現(xiàn)群聊和私聊功能
二讶舰、功能和使用規(guī)范
- 1.每個(gè)客戶(hù)端一個(gè)名稱(chēng)
- 2.向某一個(gè)客戶(hù)端發(fā)起私聊
- 3.群聊
客戶(hù)端只能向服務(wù)器端發(fā)送文件或者字符
服務(wù)器端只能得到客戶(hù)端發(fā)來(lái)的數(shù)據(jù)
必須客戶(hù)端和服務(wù)器端有一個(gè)規(guī)范
客戶(hù)端的需求可以在發(fā)送的字符里面體現(xiàn) - 1.登錄 U+ 姓名 U+
- 2.返回結(jié)果 成功1 失敗-1
- 3.私聊 P+ 姓名:聊天內(nèi)容 P+
- 4.群聊 a+ 聊天內(nèi)容 a+
- 5.發(fā)文件 f+
- 6.發(fā)語(yǔ)音 v+
三洲拇、具體實(shí)現(xiàn)
1.定義規(guī)范
public interface ChatProtocol {
//登錄
String LOGIN_FLAG="u+";
//私聊
String PRIVATE_FLAG="p+";
//群聊
String PUBLIC_FLAG="a+";
//分隔符
String SPLIT_FLAG="?";
//成功的狀態(tài)
String SUCCESS="1";
String FALURE="-1";
}
2.定義UserManager類(lèi)
管理所有的登錄的用戶(hù)Map<String棉饶,Socket>
判斷某個(gè)用戶(hù)是否已經(jīng)登錄
(1)保存所以用戶(hù)信息
public Map<String, Socket> users = new HashMap<>();
(2)判斷用戶(hù)是否已經(jīng)登錄
public synchronized boolean isLogined(String name) {
//遍歷數(shù)組
for (String key : users.keySet()) {
if (key.equals(name)) {
return true;
}
}
return false;
}
(3)保存當(dāng)前登錄的用戶(hù)信息
public synchronized void sava(String name, Socket socket) {
users.put(name, socket);
}
(4)通過(guò)用戶(hù)名找到對(duì)應(yīng)的socket
public synchronized Socket socketByName(String name) {
return users.get(name);
}
(5)通過(guò)socket對(duì)象找到對(duì)應(yīng)的名稱(chēng)
public synchronized String nameBySocket(Socket socket) {
for (String key : users.keySet()) {
//取出這個(gè)key對(duì)應(yīng)的socket
if (socket == users.get(key)) {
return key;
}
}
return null;
}
(6)獲取所有人的socket對(duì)象
public synchronized Collection<Socket> allUsers(){
return users.values();
}
3.定義Server類(lèi)模擬服務(wù)器端
(1)主線(xiàn)程管理監(jiān)聽(tīng)客戶(hù)端的連接
public class Server {
//用于保存每一個(gè)用戶(hù)對(duì)應(yīng)的姓名和socket
public static UserManager manager=new UserManager();
public static void main(String[] args) {
//創(chuàng)建ServerSocket
try (ServerSocket ss = new ServerSocket(8888)) {
//監(jiān)聽(tīng)所有來(lái)連接的客戶(hù)端
while (true){
Socket socket=ss.accept();
//讓子線(xiàn)程處理這個(gè)socket
new ServerThread(socket).start();
}
}catch (IOException e){
}
}
}
(2)子線(xiàn)程處理每個(gè)客戶(hù)端的輸入輸出
class ServerThread extends Thread{
public Socket socket;
public ServerThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader br=null;
PrintStream ps=null;
//登錄
//得到對(duì)應(yīng)的輸入流對(duì)象
try {
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//得到對(duì)應(yīng)的輸出流
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);
//判斷這個(gè)用戶(hù)是否已經(jīng)登錄
if (Server.manager.isLogined(name)){
//登錄過(guò)了
//發(fā)送結(jié)果給客戶(hù)端
ps.println(ChatProtocol.FALURE);
}else{
//沒(méi)有登錄
//保存當(dāng)前登錄的用戶(hù)信息
System.out.println(name+"已登錄");
Server.manager.sava(name,socket);
//發(fā)送結(jié)果給客戶(hù)端
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);
//用戶(hù)名
String name=items[0];
//聊天內(nèi)容
String message=items[1];
//通過(guò)用戶(hù)名找到對(duì)應(yīng)socket
Socket desSocket=Server.manager.socketByName(name);
PrintStream desPs=new PrintStream(desSocket.getOutputStream());
//獲取當(dāng)前用戶(hù)的名稱(chēng)
String currentName=Server.manager.nameBySocket(socket);
//發(fā)送私聊消息
desPs.println(currentName+"向你發(fā)來(lái)私聊:"+message);
}
(5)群聊
else {
//群聊
//
String msg=line.substring(2,line.length()-2);
//獲取當(dāng)前用戶(hù)的名稱(chēng)
String currentName=Server.manager.nameBySocket(socket);
//遍歷所有用戶(hù)信息
Collection<Socket> 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類(lèi)模擬客戶(hù)端
(1)主線(xiàn)程
public class Client {
public static void main(String[] args) {
BufferedReader br=null;
PrintStream ps=null;
BufferedReader brServer=null;
//連接服務(wù)器
try (Socket socket = new Socket("10.129.11.163", 9300)) {
//登錄
//接收終端收入流
br=new BufferedReader(new InputStreamReader(System.in));
//發(fā)給服務(wù)器的輸出流
ps=new PrintStream(socket.getOutputStream());
//接收服務(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("請(qǐng)輸入用戶(hù)名");
//拼接登陸格式
String loginStr=ChatProtocol.LOGIN_FLAG+line+ChatProtocol.LOGIN_FLAG;
//發(fā)送給服務(wù)器端
ps.println(loginStr);
//接收服務(wù)器端返回的結(jié)果
String result=brServer.readLine();
//判斷登錄結(jié)果
if (result.equals(ChatProtocol.SUCCESS)){
System.out.println(line+"登陸成功");
break;
}else{
System.out.println("用戶(hù)名以存在 請(qǐng)重新登錄");
}
}
//登錄成功
//開(kāi)啟線(xiàn)程處理服務(wù)器端的輸入
new ClientThread(socket).start();
//接收終端輸入 發(fā)送給服務(wù)器端
String line=null;
while ((line=br.readLine())!=null){
//發(fā)送給服務(wù)器
ps.println(line);
}
}catch (IOException e){
}
}
}
(2)子線(xiàn)程
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ò)出錯(cuò)");
}finally {
try {
if (br!=null){
br.close();
}
if (socket!=null){
socket.close();
}
}catch (IOException e){
}
}
}
}