packagecom;importjava.io.BufferedReader;importjava.io.InputStream;importjava.io.InputStreamReader;importjava.io.OutputStream;importjava.io.OutputStreamWriter;importjava.io.PrintWriter;importjava.net.InetAddress;importjava.net.ServerSocket;importjava.net.Socket;importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;
@SuppressWarnings("unused")public classServer {private ExecutorService threadPool; //线程池,用来管理与客户端交互的线程(重用线程)
private List allOut; //定义一个集合List,存放所有客户端输出流的共享集合
/** java.net.ServerSocket运行在服务端的Socket。主要工作是申请服务端的端口,
* 并且监听该端口, 等待客户端的连接。一旦一个客户端与服务端建立连接,
* ServerSocket会返回与该客户端沟通的Socket。*/
privateServerSocket server;public Server() //服务端构造方法,用来初始化服务端的相关属性
{try{//初始化
server = new ServerSocket(8088);
allOut= new ArrayList();
threadPool= Executors.newFixedThreadPool(50);
}catch(Exception e) {
e.printStackTrace();
}
}private synchronized void addOut(PrintWriter out) //向共享集合中添加给定的输出流
{
allOut.add(out);
}private synchronized void removeOut(PrintWriter out) //删除输出流
{
allOut.remove(out);
}private synchronized void sendMessageToAllClient(String message) //给所有的客户端发送消息
{for(PrintWriter out : allOut)
{
out.println(message);//把要发送的消息发送给这个客户端
}
}//服务端开始工作的方法
public voidstart() {try{//ServerSocket的accept方法:该方法用来监听服务端打开的8088端口//,等待客户端的连接//一旦一个客户端连接了就会返回与该客户端进行通信的Socket。
while(true)
{
System.out.println("等待客户端连接……");
Socket socket=server.accept();//accept是一个阻塞方法,直到一个客户端连接,否则会一直卡在这里
System.out.println("一个客户端连接了!");//启动一个线程,来完成与刚刚连接的客户端的交互工作
ClientHandler handler = newClientHandler(socket);//Thread t = new Thread(handler);//t.start();
threadPool.execute(handler); //把上面单独新建的一个线程,改成可以管理多个线程的线程池来使用
}
}catch(Exception e) {
e.printStackTrace();
}
}public static voidmain(String[] args) {
Server server= newServer();
server.start();
}//定义一个内部类,新建runnable新的线程
class ClientHandler implementsRunnable
{//当前线程用来交互的客户端的Socket
private Socket socket; //创建线程任务的同时指定要交互的客户端的Socket
private String host; //该客户端地址
publicClientHandler(Socket socket)
{//通过Socket我们是可以得知远端计算机的信息
InetAddress address= socket.getInetAddress(); //IP
host =address.getHostAddress();int port = socket.getPort();//端口
System.out.println(host+ "上线");this.socket =socket;
}public voidrun()
{
PrintWriter pw= null;try{
OutputStream out= socket.getOutputStream();//通过socket获取输出流,来将消息发送回给客户端。
OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
pw= new PrintWriter(osw, true);//(将该输出流存入共享集合中,以便可以广播消息)
addOut(pw); //在内部类用外部类的方法//通过Socket获取输入流,用方法:getInputStream()//InputStream getInputStream(),该方法用来获取一个输入流,//可以读取来自远端计算机发送过来的数据。
InputStream in=socket.getInputStream();
InputStreamReader isr= new InputStreamReader(in,"UTF-8");
BufferedReader br= newBufferedReader(isr);//转换为缓冲字符输入流的目的在于,可以以“行”为单位,读取字符串
String message= null;//由于br.readLine()方法在操作系统的不同,导致客户端和幅度按断开连接后,这个方法的效果是不同的。//linux:当linux的客户端与服务端断开连接后,br.readLine()方法会返回null//windows:当windows的客户端与服务端断开连接后,br.readLine()方法会抛出异常
while((message = br.readLine()) != null) //读取客户端发送过来的一行字符串
{//System.out.println(host + "说:" + message);//pw.println(host + "说:" + message);//将该客户端发送过来的消息,转发给所有客户端,达到广播的目的。
sendMessageToAllClient(host+"说"+message);
}
}catch(Exception e) {
e.printStackTrace();
}finally{//无论是linux的客户端还是windows的客户端,当与我们断开连接后,都应当将Socket关闭,来释放资源
System.out.println(host + "下线了!");//(将该客户端的输出流从共享集合中删除)
removeOut(pw);try{
socket.close();
}catch(Exception e) {
e.printStackTrace();
}
}
}
}
}