java socket tcpservice_java 网络编程Socket

1 packagecom.etc;2

3 importjava.io.BufferedReader;4 importjava.io.IOException;5 importjava.io.InputStream;6 importjava.io.InputStreamReader;7 importjava.io.OutputStream;8 importjava.io.OutputStreamWriter;9 importjava.io.PrintWriter;10 importjava.net.ServerSocket;11 importjava.net.Socket;12 importjava.util.HashMap;13 importjava.util.Map;14 importjava.util.concurrent.ExecutorService;15 importjava.util.concurrent.Executors;16

17 /**

18 * 控制台聊天程序 服务端应用程序19 *20 *21 */

22 public classchatServer {23 /**

24 * ServerSocket 是运行在服务端的Socket 用来监听端口,等待客户端的连接, 一旦连接成功就会返回与该客户端通信的Socket25 */

26 privateServerSocket serverSocket;27 /**

28 * 创建线程池来管理客户端的连接线程 避免系统资源过度浪费29 */

30 privateExecutorService threadPool;31 /**

32 * 该属性用来存放客户端之间私聊的信息33 */

34 private MapallOut;35

36 /**

37 * 构造方法,服务端初始化38 */

39 publicchatServer() {40 try{41 /*

42 * 创建ServerSocket,并申请服务端口 将来客户端就是通过该端口连接服务端程序的43 */

44 serverSocket = new ServerSocket(12580);45 /*

46 * 初始化Map集合,存放客户端信息47 */

48 allOut = new HashMap();49 /*

50 * 初始化线程池,设置线程的数量51 */

52 threadPool = Executors.newFixedThreadPool(10);53 /*

54 * 初始化用来存放客户端输出流的集合, 每当一个客户端连接,就会将该客户端的输出流存入该集合; 每当一个客户端断开连接,就会将集合中该客户端的输出流删除;55 * 每当转发一条信息,就要遍历集合中的所有输出流(元素) 因此转发的频率高于客户端登入登出的频率,56 * 还是应该使用ArrayList来存储元素,仅限群聊,私聊不行 allOut = new ArrayList();57 */

58 } catch(Exception e) {59 e.printStackTrace();60 }61 }62

63 /*

64 * 将客户端的信息以Map形式存入集合中65 */

66 private voidaddOut(String key, PrintWriter value) {67 synchronized (this) {68 allOut.put(key, value);69 }70 }71

72 /*

73 * 将给定的输出流从共享集合中删除 参数为客户端nickName,作为Map的key键74 */

75 private synchronized voidremoveOut(String key) {76 allOut.remove(key);77 System.out.println("当前在线人数为:" +allOut.size());78 }79

80 /*

81 * 将给定的消息转发给所有客户端82 */

83 private synchronized voidsendMsgToAll(String message) {84 for(PrintWriter out : allOut.values()) {85 out.println(message);86 System.out.println("当前在线人数为:" +allOut.size());87 }88 }89

90 /*

91 * 将给定的消息转发给私聊的客户端92 */

93 private synchronized voidsendMsgToPrivate(String nickname, String message) {94 PrintWriter pw = allOut.get(nickname); //将对应客户端的聊天信息取出作为私聊内容发送出去

95 if (pw != null) {96 pw.println(message);97 System.out.println("当前在线私聊人数为:" +allOut.size());98 }99 }100

101 /**

102 * 服务端启动的方法103 */

104 public voidstart() {105 try{106 while (true) {107 /*

108 * 监听10086端口109 */

110 System.out.println("等待客户端连接... ... ");111 /*

112 * Socket accept() 这是一个阻塞方法,会一直在10086端口进行监听113 * 直到一个客户端连接上,此时该方法会将与这个客户端进行通信的Socket返回114 */

115 Socket socket =serverSocket.accept();116 System.out.println("客户端连接成功! ");117 /*

118 * 启动一个线程,由线程来处理客户端的请求,这样可以再次监听 下一个客户端的连接了119 */

120 Runnable run = newGetClientMsgHandler(socket);121 threadPool.execute(run); //通过线程池来分配线程

122 }123 } catch(Exception e) {124 e.printStackTrace();125 }126 }127

128 /**

129 * 该线程体用来处理给定的某一个客户端的消息,循环接收客户端发送 的每一个字符串,并输出到控制台130 *131 *@authorJacob132 *133 */

134 class GetClientMsgHandler implementsRunnable {135 /*

136 * 该属性是当前线程处理的具体的客户端的Socket137 *138 * @see java.lang.Runnable#run()139 */

140 privateSocket socket;141 /*

142 * 获取客户端的地址信息 private String hostIP;143 */

144 /*

145 * 获取客户端的昵称146 */

147 privateString nickName;148

149 /*

150 * 创建构造方法151 */

152 publicGetClientMsgHandler(Socket socket) {153 this.socket =socket;154 /*

155 * 获取远端客户的Ip地址信息 保存客户端的IP地址字符串 InetAddress address = socket.getInetAddress();156 * hostIP = address.getHostAddress();157 */

158 }159

160 /*

161 * 创建内部类来获取昵称162 */

163 private String getNickName() throwsException {164 try{165 //服务端的输入流读取客户端发送来的昵称输出流

166 InputStream iin =socket.getInputStream();167 InputStreamReader isr = new InputStreamReader(iin, "UTF-8");168 BufferedReader bReader = newBufferedReader(isr);169 //服务端将昵称验证结果通过自身的输出流发送给客户端

170 OutputStream out =socket.getOutputStream();171 OutputStreamWriter iosw = new OutputStreamWriter(out, "UTF-8");172 PrintWriter ipw = new PrintWriter(iosw, true);173 //读取客户端发来的昵称

174 String nameString =bReader.readLine();175 while (true) {176 if (nameString.trim().length() == 0) {177 ipw.println("FAIL");178 }179 if(allOut.containsKey(nameString)) {180 ipw.println("FAIL");181 } else{182 ipw.println("OK");183 returnnameString;184 }185 nameString =bReader.readLine();186 }187 } catch(Exception e) {188 throwe;189 }190 }191

192 @Override193 public voidrun() {194 PrintWriter pw = null;195 try{196 /*

197 * 通过客户端的Socket获取客户端的输出流 用来将消息发送给客户端198 */

199 OutputStream os =socket.getOutputStream();200 OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");201 pw = new PrintWriter(osw, true);202 /*

203 * 将客户昵称和其所说的话作为元素存入共享集合HashMap中204 */

205 nickName =getNickName();206 addOut(nickName, pw);207 Thread.sleep(100);208 /*

209 * 服务端通知所有客户端,某用户登录210 */

211 sendMsgToAll("[系统通知]:欢迎**" + nickName + "**登陆聊天室!");212 /*

213 * 通过客户端的Socket获取输入流 读取客户端发送来的信息214 */

215 InputStream is =socket.getInputStream();216 InputStreamReader isr = new InputStreamReader(is, "UTF-8");217 BufferedReader br = newBufferedReader(isr);218 String msgString = null;219 while ((msgString = br.readLine()) != null) {220 //验证是否是私聊

221 if (msgString.startsWith("@")) {222 /*

223 * 私聊格式:@昵称:内容224 */

225 int index = msgString.indexOf(":");226 if (index >= 0) {227 //获取昵称

228 String name = msgString.substring(1, index);229 String info = msgString.substring(index + 1, msgString.length());230 info = nickName + "对你说:" +info;231 //将私聊信息发送出去

232 sendMsgToPrivate(name, info);233 //服务端不在广播私聊的信息

234 continue;235 }236 }237 /*

238 * 遍历所有输出流,将该客户端发送的信息转发给所有客户端239 */

240 System.out.println(nickName + "说:" +msgString);241 sendMsgToAll(nickName + "说:" +msgString);242 }243 } catch(Exception e) {244 /*

245 * 因为Win系统用户的客户端断开连接后,br.readLine()方法读取 不到信息就会抛出异常,而Linux系统会持续发送null;246 * 因此这里就不在将捕获的异常抛出了。247 */

248 } finally{249 /*

250 * 当执行到此处时,说明客户端已经与服务端断开连接 则将该客户端存在共享集合中的输出流删除251 */

252 removeOut(nickName);253 /*

254 * 通知所有客户端,某某客户已经下线255 */

256 sendMsgToAll("[系统通知]:" + nickName + "已经下线了。");257 /*

258 * 关闭socket,则通过Socket获取的输入输出流也一同关闭了259 */

260 if (socket != null) {261 try{262 socket.close();263 } catch(IOException e) {264 e.printStackTrace();265 }266 }267 }268 }269 }270

271 public static voidmain(String[] args) {272 chatServer server = newchatServer();273 server.start();274 }275 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值