實現客戶端監控
目的:在服務器端寫線程,讓服務端線程代理客戶端的socket
技術:利用線程池來管理所有的客戶端套接字
package chat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TCPServer {
private ServerSocket serverSocket;//創建服務器端的套接字
private ExecutorService executorService;//線程池,代理監控到的客戶端套接字對象
private Map<String , PrintWriter> storeInfo;//儲存客戶端的信息,儲存客戶端名稱+IO流
public TCPServer(){
try {
serverSocket = new ServerSocket(8888);
executorService = Executors.newCachedThreadPool();
storeInfo = new HashMap<>();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 儲存客戶端的信息(key--暱稱,value--ip)
* @param key
* @param value
*/
public synchronized void putIn(String key,PrintWriter value){
storeInfo.put(key, value);
}
/**
* 發送消息給所有客戶端
* @param message
*/
public synchronized void sendToAll(String message){
/**
* 原理Map接口還是這樣子做遍歷
*/
for(PrintWriter pw : storeInfo.values()){
pw.println(message);
}
}
/**
* 發送消息給指定客戶端(私聊)
* @param name
* @param message
*/
public synchronized void sendToSomeone(String name,String message){
PrintWriter pw = storeInfo.get(name);
if(name != null){
pw.println(message);
}
}
/**
* 移除客戶端的信息
* @param name
*/
public synchronized void remove(String name){
storeInfo.remove(name);
System.out.println("當前在線人數:"+storeInfo.size());
}
/*
* 啓動服務器端套接字
*/
public void start(){
try {
while(true){
System.out.println("等待客戶端連接....");
Socket socket = serverSocket.accept();
//獲取客戶端IP地址
InetAddress inetAddress = socket.getInetAddress();
System.out.println("客戶端:"+inetAddress.getHostAddress()+"連接成功");
//線程來代理客戶端的套接字對象
//線程代理後,啓動run方法
executorService.execute(new ListenerClient(socket));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/*
* 用線程代理客戶端的套接字對象
*/
class ListenerClient implements Runnable{
private Socket socket;
private String name;
public ListenerClient(Socket socket){
this.socket = socket;
}
/*
* 1.獲取輸出流,發送信息
* 2.讀取客戶端的信息,讀取客戶端的暱稱
* 3.檢驗是羣聊和私聊(客戶端如果發送@...,代表私聊)
* 4.客戶端關閉,程序還得把客戶端套接字關閉
*
*/
@Override
public void run() {
//一定要注意把客戶端套接字的流保存storeInfo裏面
PrintWriter pw;
try {
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"),true);
String name = getName();
putIn(name, pw);
Thread.sleep(100);
sendToAll("[系統通知]"+name+"已上線");
//獲取輸入信息
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
//讀取信息,判斷是羣聊還是私聊
String message = null;
while((message = br.readLine()) != null){
if(message.startsWith("@")){
int index = message.indexOf(":");
if(index>0){
//@對方的名字
String theName = message.substring(1,index);
//@對方的內容
String info = message.substring(index+1);
//組合消息內,自己的名字+發送的內容(對方看到)
info = name +":"+info;
//調用私聊方法
sendToSomeone(theName, info);
continue;
}
}
//羣聊
sendToAll(name+":"+message);
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
remove(name);
sendToAll("[系統通知]:"+name+"已經下線");
if(socket != null){
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 獲取客戶端暱稱
* @return
*/
private String getName(){
try {
//服務器發送消息給客戶的輸入的信息是否有重複
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8"),true);
//讀取客戶端輸入的暱稱
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
while(true){
String name = br.readLine();
if(name.trim().length() == 0 || storeInfo.containsKey(name)){//未寫或者重複
pw.println("FAIL");
}else {
pw.println("OK");
return name;
}
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
public static void main(String[] args) {
TCPServer tcpServer = new TCPServer();
tcpServer.start();
}
}
package chat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TCPClient {
private static Socket socket;
public TCPClient() throws UnknownHostException, IOException{
System.out.println("設置服務器端的地址:");
Scanner scanner = new Scanner(System.in);
String ip = scanner.nextLine();
socket = new Socket(ip,8888);
start();
}
/**
* 啓動客戶端套接字
* 同時需要有線程代理管理套接字
* @throws IOException
* @throws UnsupportedEncodingException
*/
private void start() throws UnsupportedEncodingException, IOException{
/**
* 連上服務器後輸入暱稱
* 客戶端設置暱稱----必須根據服務器端返回的是否能使用來設置
* 發送聊天內容----聊天內容需要線程
*/
Scanner scanner = new Scanner(System.in);
setName(scanner);
/**
* 聊天內容需要線程代理----專門讀取羣聊和私聊信息
*/
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new ListenerClient());
/**
* 看到信息後,在發送信息給對方
*/
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8"),true);
while(true){
pw.println(scanner.nextLine());
}
}
public void setName(Scanner scanner) throws UnsupportedEncodingException, IOException{
String name;
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8"),true);
while(true){
System.out.println("請輸入你的暱稱:");
name = scanner.nextLine();
if(name.trim().length()==0){
System.out.println("暱稱不能爲空");
}else {
pw.println(name);//把暱稱發給服務器端
String pass = br.readLine();//得到"FAIL"或"OK"
if(pass != null && (!pass.equals("OK"))){
System.out.println("暱稱已被佔用");
}else {
System.out.println("暱稱"+name+"已設置成功,可以使用了");
break;
}
}
}
}
/**
* 專門監聽服務器端返回的信息
*
* @author asus
*
*/
class ListenerClient implements Runnable{
@Override
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
String message = null;
while((message = br.readLine()) != null){
System.out.println(message);
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) throws UnknownHostException, IOException {
new TCPClient();
}
}
請關注“知了堂學習社區”,地址:http://www.zhiliaotang.com/portal.php