網(wǎng)絡(luò)編程 客戶(hù)端實(shí)現(xiàn)登錄 私聊和群聊
內(nèi)容
1.建立多個(gè)客戶(hù)端
2.向某一個(gè)客戶(hù)端發(fā)起私聊
3.實(shí)現(xiàn)客戶(hù)端的群聊
必須客戶(hù)端和服務(wù)器端有一個(gè)規(guī)范
- 客戶(hù)端的需求可以在發(fā)送的字符里面體現(xiàn)
1.登錄 u+ 姓名 u+
2.返回結(jié)果 成功1 失敗-1
3.私聊 p+ 姓名 內(nèi)容 p+
4.群聊 群聊不加任何符號(hào) 一發(fā)消息就顯示在公屏里每個(gè)人可見(jiàn)
代碼
- 定義一個(gè)接口庐氮,確定一套規(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 FAILURE = "-1";
}
- 定義UserManager類(lèi) 管理所有登錄用的信息
/**
* 管理所有的登錄的用戶(hù) Map<String,Socket></>
* 判斷某個(gè)用戶(hù)是否已經(jīng)登錄
*/
public class UserManager {
// 保存所有用戶(hù)信息
private Map<String, Socket> users = new HashMap<>();
/**
* 判斷用戶(hù)是否已經(jīng)登錄
*/
public boolean isLogined(String name){
// 遍歷數(shù)組
for(String key : users.keySet()){
if (key.equals(name)){
return true;
}
}
return false;
}
/**
* 保存當(dāng)前登錄的用戶(hù)信息
*/
public void save(String name,Socket socket){
users.put(name,socket);
}
/**
* 通過(guò)用戶(hù)名找到對(duì)應(yīng)的socket
*/
public Socket socketByName(String name){
return users.get(name);
}
/**
* 通過(guò)socket對(duì)象找到對(duì)應(yīng)的名稱(chēng)
*/
public String nameBySocket(Socket socket){
for(String key:users.keySet()){
// 取出這個(gè)key對(duì)應(yīng)的socket
if (socket == users.get(key)){
return key;
}
}
return null;
}
/**
* 獲取所有人的socket對(duì)象
*/
public synchronized Collection<Socket> allUsers(){
return users.values();
}
}
- Server類(lèi)
主線(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) {
e.printStackTrace();
}
}
}
子線(xiàn)程處理每個(gè)客戶(hù)端的輸入輸出
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.得到對(duì)應(yīng)的輸入流
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
ps = new PrintStream(socket.getOutputStream());
String line = null;
判斷是不是登錄
while ((line = br.readLine())!=null){
// 登錄
if (line.startsWith(ChatProtocol.LOGIN_FLAG)&&line.endsWith(ChatProtocol.LOGIN_FLAG)){
// 提取用戶(hù)名
String name = line.substring(2,line.length()-2);
// 判斷這個(gè)用戶(hù)是否已經(jīng)登錄
if (Server.manager.isLogined(name)){
//登錄過(guò)了
// 發(fā)送結(jié)果給客戶(hù)端
ps.println(ChatProtocol.FAILURE);
}else {
// 沒(méi)有登錄
// 保存當(dāng)前登錄的用戶(hù)信息
Server.manager.save(name,socket);
ps.println(ChatProtocol.SUCCESS);
}
}
私聊
else if(line.startsWith(ChatProtocol.PRIVATE_FLAG)&&line.endsWith(ChatProtocol.PRIVATE_FLAG)){
// p+jack?hellop+
// 獲取信息
String msg = line.substring(2,line.length()-2);
// 分割
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);
}
群聊
else {
//群聊
//處理數(shù)據(jù)
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 temps = new PrintStream(s.getOutputStream());
temps.println(currentName+":"+msg);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客戶(hù)端
public class Client {
public static void main(String[] args){
BufferedReader br = null;
PrintStream ps = null ;
BufferedReader brServer;
try(Socket socket = new Socket("10.129.28.234",8888)) {
ps = new PrintStream(socket.getOutputStream());
brServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
br = new BufferedReader(new InputStreamReader(System.in));
// 登錄
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("登錄成功");
break;
}else{
System.out.println("用戶(hù)名已存在 請(qǐng)重新登錄");
}
}
// 登錄成功
// 開(kāi)啟線(xiàn)程處理服務(wù)器的輸入
new ClientThread(socket).start();
String line = null;
while((line = br.readLine())!= null){
//發(fā)送給服務(wù)器
ps.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
子線(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) {
e.printStackTrace();
}
}
}
}
運(yùn)行結(jié)果心得
今天寫(xiě)這個(gè)demo业簿,一開(kāi)始還能跟上壁却,到后面就跟不上了批狱,出了一些bug。不過(guò)還是能夠懂大概的過(guò)程展东。后面自己按照自己的理解重新將代碼推敲了一遍赔硫。總算是找出了出錯(cuò)的地方盐肃。修改了之后已經(jīng)沒(méi)問(wèn)題了爪膊。自己也將代碼重寫(xiě)了一遍,不過(guò)還是有些地方比較模糊砸王,還是看了原代碼推盛。雖然并不能自己獨(dú)立寫(xiě)出,不過(guò)我還是蠻高興的谦铃。