实现客户端监控
目的:在服务器端写线程,让服务端线程代理客户端的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