聊天室服务端源码

  1 import java.io.BufferedReader;
  2 import java.io.IOException;
  3 import java.io.InputStream;
  4 import java.io.InputStreamReader;
  5 import java.io.OutputStream;
  6 import java.io.OutputStreamWriter;
  7 import java.io.PrintWriter;
  8 import java.net.InetAddress;
  9 import java.net.ServerSocket;
 10 import java.net.Socket;
 11 import java.util.ArrayList;
 12 import java.util.List;
 13 
 14 /**
 15  * 聊天室服务端
 16  *
 17  * @author HeFahai
 18  *
 19  */
 20 public class Server {
 21     /*
 22      * java.net.ServerSocket 运行在服务端的ServerSocket主要有两个作用 1:申请服务端口
 23      * 2:监听服务端口,一旦一个客户端通过该端口建立链接,则创建一个Socket用于与该客户端通讯
 24      */
 25     private ServerSocket server;
 26     /*
 27      * 该集合用来存放所有客户端的输出流,用于将信息广播给所有客户端。
 28      */
 29     private List<PrintWriter> allOut;
 30 
 31     public Server() throws IOException {
 32         /*
 33          * 初始化ServerSocket的同事需要指定服务端口 该端口号不能与系统其他应用程序已申请的端口 号重复,否则会抛出异常。
 34          */
 35         server = new ServerSocket(8088);
 36         // 初始化链表
 37         allOut = new ArrayList<PrintWriter>();
 38     }
 39 
 40     public void start() {
 41         try {
 42             /*
 43              * ServerSocket提供方法: Socket accept() 该方法会监听ServerSocket申请的服务端口。
 44              * 是一个阻塞方法,直到一个客户端通过该端口链接 才会返回一个Socket。这个返回的Scoket是用于 链接客户端进行通讯的。
 45              */
 46             while (true) {
 47                 System.out.println("等待客户端连接...");
 48                 Socket socket = server.accept();
 49                 System.out.println("客户端已连接!");
 50                 /*
 51                  * 启动一个线程与该客户端交互
 52                  */
 53                 ClientHander clientHander = new ClientHander(socket);
 54                 Thread t = new Thread(clientHander);
 55                 t.start();
 56             }
 57         } catch (Exception e) {
 58             e.printStackTrace();
 59         }
 60     }
 61 
 62     /**
 63      * 将给定的消息广播给所有客户端
 64      *
 65      * @param message
 66      */
 67     private void sendMessage(String message) {
 68         synchronized (allOut) {
 69             // 转发给所有客户端
 70             for (PrintWriter o : allOut) {
 71                 o.println(message);
 72             }
 73         }
 74     }
 75 
 76     public static void main(String[] args) {
 77         try {
 78             Server server = new Server();
 79             server.start();
 80         } catch (Exception e) {
 81             e.printStackTrace();
 82         }
 83     }
 84 
 85     /**
 86      * 该线程用来完成与指定客户端加护的工作
 87      *
 88      */
 89     private class ClientHander implements Runnable {
 90         /*
 91          * 当前线程通过这个Socket与指定客户端交互
 92          */
 93         private Socket socket;
 94         /*
 95          * 远程计算机地址信息,这里是客户端的地址
 96          */
 97         private String host;
 98 
 99         public ClientHander(Socket socket) {
100             this.socket = socket;
101             /*
102              * 通过Socket可以远程获取计算机的地址信息
103              */
104             InetAddress address = socket.getInetAddress();
105             /*
106              * 获取远端计算机IP地址的字符串格式
107              */
108             host = address.getHostAddress();
109         }
110 
111         public void run() {
112             PrintWriter pw = null;
113             try {
114 
115                 /*
116                  * InputStream getInputStream() Socket提供的该方法可以获取一个输入流,
117                  * 通过该流,服务端读取到远端计算机发送过 来的数据。
118                  */
119                 InputStream in = socket.getInputStream();
120 
121                 InputStreamReader isr = new InputStreamReader(in, "UTF-8");
122 
123                 BufferedReader br = new BufferedReader(isr);
124 
125                 /*
126                  * 通过Socket获取输出流,用于将数据发送给客户端
127                  */
128                 OutputStream os = socket.getOutputStream();
129 
130                 OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
131 
132                 pw = new PrintWriter(osw, true);
133 
134                 /*
135                  * 将该客户端的输出流存入到共享集合中
136                  *
137                  *
138                  * 由于多个线程都会调用该集合的add方法向其中添加输出流,所以 为了保证线程安全,可以将该集合上锁。
139                  */
140                 synchronized (allOut) {
141                     allOut.add(pw);
142                 }
143 
144                 System.out.println(host + "上线了!");
145                 
146                 sendMessage(host + "上线了!当前在线" + allOut.size() + "人");
147 
148                 String message = null;
149                 while ((message = br.readLine()) != null) {
150                     // System.out.println(host + "说:" + message);
151                     // 回复给当前客户端
152                     // pw.println(host + "说:" + message);
153 
154                     sendMessage(host + "说:" + message);
155                 }
156             } catch (Exception e) {
157 
158             } finally {
159                 // 处理客户端断开连接以后的工作
160 
161                 synchronized (allOut) {
162                     // 将该客户端的输出流从共享集合中删除
163                     allOut.remove(pw);
164                 }
165                 System.out.println(host + "下线了!");
166                 
167                 sendMessage(host + "下线了!当前在线" + allOut.size() + "人");
168                 if (socket != null) {
169                     try {
170                         socket.close();
171                     } catch (IOException e) {
172                         e.printStackTrace();
173                     }
174                 }
175 
176             }
177         }
178     }
179 }

 

posted @ 2017-06-08 19:00  大头何  阅读(11)  评论(0)    收藏  举报