一、目的
使用Socket實現(xiàn)群聊和私聊功能
二、功能和使用規(guī)范
- 1.每個客戶端一個名稱
- 2.向某一個客戶端發(fā)起私聊
- 3.群聊
客戶端只能向服務(wù)器端發(fā)送文件或者字符
服務(wù)器端只能得到客戶端發(fā)來的數(shù)據(jù)
必須客戶端和服務(wù)器端有一個規(guī)范
客戶端的需求可以在發(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ā)語音 v+
三、具體實現(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類
管理所有的登錄的用戶Map<String,Socket>
判斷某個用戶是否已經(jīng)登錄
(1)保存所以用戶信息
public Map<String, Socket> users = new HashMap<>();
(2)判斷用戶是否已經(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)前登錄的用戶信息
public synchronized void sava(String name, Socket socket) {
users.put(name, socket);
}
(4)通過用戶名找到對應(yīng)的socket
public synchronized Socket socketByName(String name) {
return users.get(name);
}
(5)通過socket對象找到對應(yīng)的名稱
public synchronized String nameBySocket(Socket socket) {
for (String key : users.keySet()) {
//取出這個key對應(yīng)的socket
if (socket == users.get(key)) {
return key;
}
}
return null;
}
(6)獲取所有人的socket對象
public synchronized Collection<Socket> allUsers(){
return users.values();
}
3.定義Server類模擬服務(wù)器端
(1)主線程管理監(jiān)聽客戶端的連接
public class Server {
//用于保存每一個用戶對應(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)聽所有來連接的客戶端
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;
//登錄
//得到對應(yīng)的輸入流對象
try {
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//得到對應(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);
//判斷這個用戶是否已經(jīng)登錄
if (Server.manager.isLogined(name)){
//登錄過了
//發(fā)送結(jié)果給客戶端
ps.println(ChatProtocol.FALURE);
}else{
//沒有登錄
//保存當(dāng)前登錄的用戶信息
System.out.println(name+"已登錄");
Server.manager.sava(name,socket);
//發(fā)送結(jié)果給客戶端
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];
//聊天內(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);
}
(5)群聊
else {
//群聊
//
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 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;
//連接服務(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("請輸入用戶名");
//拼接登陸格式
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("用戶名以存在 請重新登錄");
}
}
//登錄成功
//開啟線程處理服務(wù)器端的輸入
new ClientThread(socket).start();
//接收終端輸入 發(fā)送給服務(wù)器端
String line=null;
while ((line=br.readLine())!=null){
//發(fā)送給服務(wù)器
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("網(wǎng)絡(luò)出錯");
}finally {
try {
if (br!=null){
br.close();
}
if (socket!=null){
socket.close();
}
}catch (IOException e){
}
}
}
}