关于Socket和ServerSocket的知识可以看:Java Socket 通信
ChatServer类:
package bio.server;
import java.io.*;
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 ChatServer {
private int DEFAULT_PORT = 8888;//默认端口
private final String QUIT = "quit";//退出指令
private ExecutorService executorService;
private ServerSocket serverSocket;
private Map<Integer, Writer> connectedClients;//存储客户端信息
public ChatServer(){
executorService = Executors.newFixedThreadPool(5);//创建线程池
connectedClients = new HashMap<>();
}
/**
* 客户端连接,添加一个客户端
* synchronized 保证线程的安全性,防止多个线程同时添加客户端
* @param socket 客户端的socket
* @throws IOException
*/
public synchronized void addClient(Socket socket) throws IOException {
if(socket != null){
int port = socket.getPort();
BufferedWriter bufferedWriter = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);
connectedClients.put(port, bufferedWriter);
System.out.println("客户端["+port+"]已连接到服务器");
}
}
/**
* 客户端断开连接,从列表中删除客户端
* synchronized 保证线程安全性, 防止多个线程同时删除客户端
* @param socket 断开连接的客户端socket
* @throws IOException
*/
public synchronized void removeClient(Socket socket) throws IOException {
if(socket != null){
int port = socket.getPort();
if(connectedClients.containsKey(port)){
connectedClients.get(port).close();
}
connectedClients.remove(port);
System.out.println("客户端["+port+"]已断开连接");
}
}
/**
* 向服务器中其他的客户端转发消息,除了消息本身的拥有者
* synchronized 保证线程安全性,防止多个线程同时去转发消息,访问connectedClients
* @param socket 消息所属的客户端的socket
* @param fwdMsg 消息的具体内容
* @throws IOException
*/
public synchronized void forwardMessage(Socket socket, String fwdMsg) throws IOException {
for(Integer id : connectedClients.keySet()){
if(!id.equals(socket.getPort())){
Writer writer = connectedClients.get(id);
writer.write(fwdMsg);
writer.flush();
}
}
}
/**
* 如果用户发送quit则准备退出
* @param msg 用户返送的消息
* @return
*/
public boolean readyToQuit(String msg){
return QUIT.equals(msg);
}
/**
* synchronized 保护serverSocket的状态
* 关闭serverSocket
*/
public synchronized void close(){
if(serverSocket != null){
try {
serverSocket.close();
System.out.println("关闭serverSocket");
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 开启服务器,监听端口,并且启动ChatHandler线程
*/
public void start(){
try {
// 绑定监听端口
serverSocket = new ServerSocket(DEFAULT_PORT);
System.out.println("启动服务器,监听端口:" + DEFAULT_PORT + "...");
while(true){
//等待客户端连接
Socket socket = serverSocket.accept();
//创建额外的ChatHandler线程
executorService.execute(new ChatHandler(this, socket));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
close();
}
}
public static void main(String[] args) {
ChatServer chatServer = new ChatServer();
chatServer.start();
}
}
ChatHandler类:
package bio.server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class ChatHandler implements Runnable{
private ChatServer server; //用来操作服务器端的connectedClients
private Socket socket;
public ChatHandler(ChatServer server, Socket socket){
this.server = server;
this.socket = socket;
}
/**
* 用于对客户端进行服务
*/
@Override
public void run() {
try {
//存储新上线用户
server.addClient(socket);
//读取用户发送的消息
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
String msg = null;
while((msg = bufferedReader.readLine()) != null){
String fwdMsg = "客户端["+socket.getPort() +"]:" + msg + "\n";//readLine()函数读取需要加换行符
System.out.print(fwdMsg);
//将收到的消息转发给聊天室里在线的其他用户
server.forwardMessage(socket, fwdMsg);
//检查用户是否准备退出
if(server.readyToQuit(msg)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
server.removeClient(socket);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
ChatClient类:
package bio.client;
import java.io.*;
import java.net.Socket;
public class ChatClient {
private final String QUIT = "quit";
private final String DEFAULT_SERVER_HOST = "127.0.0.1";
private final int DEFAULT_SERVER_PORT = 8888;
private Socket socket;
private BufferedReader bufferedReader = null;
private BufferedWriter bufferedWriter = null;
/**
* 发送消息给服务器
* @param msg 用户发送的消息
* @throws IOException
*/
public void send(String msg) throws IOException {
if(!socket.isOutputShutdown()){//确定socket的输出流未关闭
bufferedWriter.write(msg + "\n");
bufferedWriter.flush();
}
}
/**
* 从服务器接收消息
* @return
* @throws IOException
*/
public String receive() throws IOException {
String msg = null;
if(!socket.isInputShutdown()){//确定socket的输入流未关闭
msg = bufferedReader.readLine();
}
return msg;
}
/**
* 检查用户是否准备退出
* @param msg 用户发送的消息
* @return
*/
public boolean readyToQuit(String msg){
return QUIT.equals(msg);
}
/**
* 关闭socket
*/
public void close(){
if(bufferedWriter != null){
try {//关闭bufferedWriter的同时也关闭socket
System.out.println("关闭socket");
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 创建实例、IO流、处理用户输入以及读取服务器转发的消息
*/
public void start(){
try {
//创建socket实例
socket = new Socket(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);
//创建IO流
bufferedReader = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
bufferedWriter = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);
//创建额外的线程处理用户的输入
new Thread(new UserInputHandler(this)).start();
//读取服务器转发的消息
String msg = null;
while((msg = receive()) != null){
System.out.println(msg);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
close();
}
}
public static void main(String[] args) {
ChatClient chatClient = new ChatClient();
chatClient.start();
}
}
UserInputHandler类:
package bio.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class UserInputHandler implements Runnable{
private ChatClient chatclient;
public UserInputHandler(ChatClient chatClient){
this.chatclient = chatClient;
}
public UserInputHandler(nio.test.client.ChatClient chatClient) {
}
/**
*
*/
@Override
public void run() {
try {
//等待用户输入的消息
BufferedReader consoleReader = new BufferedReader(
new InputStreamReader(System.in)
);
while(true){
String input = consoleReader.readLine();
//向服务器发送消息
chatclient.send(input);
//检查用户是否准备退出
if(chatclient.readyToQuit(input)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}