socket,线程池(TCP通信)
2015-09-15 10:43
471 查看
Server 1
Client 1
Server(2)
Client(2)
package day20150914socket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /** * 服务端应用程序 * (MINA的本质也是使用server) */ public class Server { //服务端的Socket private ServerSocket server; //构造方法,用于初始化服务端 public Server() throws IOException{ try { System.out.println("初始化服务端"); /* * 创建ServerSocket时需要指定的服务端口 */ server = new ServerSocket(8088); System.out.println("服务端初始化完毕"); } catch (IOException e) { throw e; } } public void start(){ try { System.out.println("等待客户端连接。。。"); /* * ServerSocket的accept()方法: * 用于监听8088端口,等待客户连接, 否则该方法阻塞。 * 若一个客户端连接了,会返回给客户端的Socket */ Socket socket = server.accept(); //获取远端(客户端)地址 InetAddress address = socket.getInetAddress(); //获取远端IP地址 String ip = address.getHostAddress(); //获取远端端口号 int port = socket.getPort(); System.out.println(ip+":"+port+"客户端连接上了"); /* * 通过刚刚连接上来的客户端的Socket获取输入流 * 来读取客户端发过来的信息 */ InputStream in = socket.getInputStream(); //将字节输入流包装为字符输入流,这样就可指定编码集 InputStreamReader isr = new InputStreamReader(in,"utf-8"); //将字符流转为缓冲字符输入流,这样就可以以行为单位来读取字符串了 BufferedReader br = new BufferedReader(isr); String message = null; while((message=br.readLine())!=null){ System.out.println("客户端说:"+message); } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { try { Server server = new Server(); server.start(); } catch (IOException e) { e.printStackTrace(); System.out.println("服务器初始化失败"); } } }
Client 1
package day20150914socket; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class Client { //用于连接服务器端的Socket private Socket socket; public Client() throws Exception{ try { System.out.println("正在连接服务端。。。"); /* * 创建Socket对象, * 就会尝试根据给定的地址与端口连接服务器 * 所以,若该对象创建成功,说明与服务器端连接正常 */ //localhost:本机。连接其他计算机可写IP socket = new Socket("localhost",8088); System.out.println("成功连接服务端。"); } catch (Exception e) { throw e; } } public void start(){ try{ /* * 可以通过Socket的getOutputStream()方法获取一条输出流 * 用于将信息发送至服务器 */ OutputStream out = socket.getOutputStream(); /* *使用字符流指定编码集将字符串转为字节后, *再通过out发送给服务器 */ OutputStreamWriter osr = new OutputStreamWriter(out,"utf-8"); /* * 将字符流包装为缓冲字符流 * 就可以以行为单位写出字符串了。 */ PrintWriter pw = new PrintWriter(osr); Scanner sc = new Scanner(System.in); while(true){ String str = sc.nextLine(); pw.println(str); pw.flush(); } }catch(Exception e){ e.printStackTrace(); } } public static void main(String[] args) { try { Client client = new Client(); client.start(); } catch (Exception e) { e.printStackTrace(); System.out.println("客户端初始化失败"); } } }
Server(2)
package day20150914socket2; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 服务端应用程序 * (MINA的本质也是使用server) */ public class Server2 { //服务端的Socket private ServerSocket server; //线程池,用于管理客户端连接的交互线程 private ExecutorService threadPool; //保存所有客户端输出流的集合 private List<PrintWriter> allOut; //构造方法,用于初始化服务端 public Server2() throws IOException{ try { System.out.println("初始化服务端"); /* * 创建ServerSocket时需要指定的服务端口 */ server = new ServerSocket(8088); //初始化线程池 threadPool = Executors.newFixedThreadPool(50); /* * 初始化存放所有客户端输出流的集合 * 使用ArrayList而不是linkedList的原因: * 增删元素不频繁,而是使用遍历频繁 */ allOut = new ArrayList<PrintWriter>(); System.out.println("服务端初始化完毕"); } catch (IOException e) { throw e; } } public void start(){ try { /* * ServerSocket的accept()方法: * 用于监听8088端口,等待客户连接, 否则该方法阻塞。 * 若一个客户端连接了,会返回给客户端的Socket */ while(true){ System.out.println("等待客户端连接。。。"); Socket socket = server.accept(); /* * 当一个客户端连接后,启动一个线程ClientHandler * 将客户端的socket传入,使得该线程处理与该客户端的交互 * 这样,可再次进入循环,接收下一个客户端的连接 */ Runnable handler = new ClientHandler(socket); //Thread t = new Thread(handler); //t.start(); /* * 使用线程池分配空闲线程来处理当前连接的客户端 */ threadPool.execute(handler); } } catch (IOException e) { e.printStackTrace(); } } /** * 将给定的输出流存入共享集合 * * 加synchronized关键字,锁定Server对象, * 下面3个方法锁定后,互斥(3个方法锁定同一个对象) * 即一个线程访问其中一个方法后, * 其他线程不可访问这3个方法中的任何一个 */ public synchronized void addOut(PrintWriter pw){ allOut.add(pw); } /** * 将给定的输出流从共享集合中删除 */ public synchronized void removeOut(PrintWriter pw){ allOut.remove(pw); } /** * 将给定的消息转发给所有客户端 */ public synchronized void sendMessage(String message){ for(PrintWriter pw : allOut){ pw.println(message); } } public static void main(String[] args) { try { Server2 server = new Server2(); server.start(); } catch (IOException e) { e.printStackTrace(); System.out.println("服务器初始化失败"); } } /** * 服务器端的一个线程,用于与某个客户端交互, * 使用线程的目的是使得服务器可以处理多个客户端了 */ class ClientHandler implements Runnable{ //当前线程处理的客户端的socket private Socket socket; //当前客户端的IP private String ip; //当前客户端的昵称 private String nickname; /** * 根据给定的客户端的Socket,创建线程体 */ public ClientHandler(Socket socket){ this.socket = socket; //获取远端(客户端)地址 InetAddress address = socket.getInetAddress(); //获取远端IP地址 ip = address.getHostAddress(); //获取远端端口号 int port = socket.getPort(); //改为使用昵称,所以不在这里通知了 //System.out.println(ip+":"+port+"客户端连接上了"); } /** * 该线程会将当前socket中的输入流获取 * 用来循环读取客户端发送过来的消息 */ @Override public void run() { PrintWriter pw = null; try{ /* * 为了让服务端向客户端发送信息 * 通过socket获取输出流 */ OutputStream out = socket.getOutputStream(); //转为字符流,用于指定编码集 OutputStreamWriter osr = new OutputStreamWriter(out,"utf-8"); //创建缓冲字符输出流,true自动行刷新 pw = new PrintWriter(osr,true); /* * 将该客户端的输出流存入共享集合 * 以便使得该客户端也能接收服务器转发的消息 */ //allOut.add(pw); addOut(pw); System.out.println("当前在线人数:"+allOut.size()); /* * 通过刚刚连接上来的客户端的Socket获取输入流 * 来读取客户端发过来的信息 */ InputStream in = socket.getInputStream(); //将字节输入流包装为字符输入流,这样就可指定编码集 InputStreamReader isr = new InputStreamReader(in,"utf-8"); //将字符流转为缓冲字符输入流,这样就可以以行为单位来读取字符串了 BufferedReader br = new BufferedReader(isr); //当创建好当前客户端的输入流后,读取的第一个字符串应当是昵称 nickname = br.readLine(); //通知所有客户端,当前用户上线了 sendMessage("["+nickname+"]上线了"); sendMessage("当前在线人数为:"+allOut.size());//告知所有客户端在线人数 String message = null; /* * 读取客户端发来的一行字符串 * windows与linux的差异: * linux:当客户端断开连接后,通过输入流会读取到null, * 这是合乎逻辑的, * 因为缓冲流的readLine方法若返回null就无法通过该流读取到信息 * * windows:当客户端与服务器端断开接连后 * readLine()方法会抛出异常 */ while((message=br.readLine())!=null){ //System.out.println("客户端说:"+message); //pw.println(message);//把客户端发来的消息回给客户端 //当前客户端说话内容告诉给所有客户端 sendMessage(nickname+"说:"+message); } }catch(Exception e){ /* * 在windows中的客户端 * 报错通常是因为客户端断开了连接 * * 不用关流,可直接关Socket */ }finally{ /* * 首先将该客户端的输出流从共享集合中删除 */ //allOut.remove(pw); removeOut(pw); //控制台显示该用户下线了 System.out.println("["+nickname+"]下线了"); //通知其他用户该用户下线了 sendMessage("["+nickname+"]下线了"); //输出当前在线人数(输出流的个数) System.out.println("当前在线人数为:"+allOut.size()); sendMessage("当前在线人数为:"+allOut.size());//告知所有客户端在线人数 /* * 无论是linux用户还是windows用户, * 当客户与服务端断开连接后, * 我们都应当在服务器端与客户端断开连接 */ try { socket.close(); //关闭之后,catch处理意义也不大,故catch块里的内容可为空 } catch (IOException e) { } //System.out.println("一个客户端下线了"); } } } }
Client(2)
package day20150914socket2; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class Client2 { //用于连接服务器端的Socket private Socket socket; public Client2() throws Exception{ try { System.out.println("正在连接服务端。。。"); /* * 创建Socket对象, * 就会尝试根据给定的地址与端口连接服务器 * 所以,若该对象创建成功,说明与服务器端连接正常 */ //localhost:本机。连接其他计算机可写IP socket = new Socket("localhost",8088); System.out.println("成功连接服务端。"); } catch (Exception e) { throw e; } } /** * 客户端启动方法 */ public void start(){ try{ //创建并启动线程,来接收服务器端发送过来的消息 Runnable runn = new GetServerInfoHandler(); Thread t = new Thread(runn); t.start(); /* * 可以通过Socket的getOutputStream()方法获取一条输出流 * 用于将信息发送至服务器 */ OutputStream out = socket.getOutputStream(); /* *使用字符流指定编码集将字符串转为字节后, *再通过out发送给服务器 */ OutputStreamWriter osr = new OutputStreamWriter(out,"utf-8"); /* * 将字符流包装为缓冲字符流 * 就可以以行为单位写出字符串了。 */ PrintWriter pw = new PrintWriter(osr,true);//true,自动行刷新 //创建一个Scanner,用于接收用户输入的字符串 Scanner sc = new Scanner(System.in); //输出欢迎语 System.out.println("欢迎来到传奇的聊天室"); while(true){ System.out.println("请输入昵称"); String nickname = sc.nextLine(); if(nickname.trim().length()>0){ pw.println(nickname); break; } System.out.println("昵称不能为空"); } while(true){ String str = sc.nextLine(); pw.println(str); //pw.flush();//PrintWriter自动行刷新就不要此句了 } }catch(Exception e){ e.printStackTrace(); } } public static void main(String[] args) { try { Client2 client = new Client2(); client.start(); } catch (Exception e) { e.printStackTrace(); System.out.println("客户端初始化失败"); } } /** * 该线程的作用是循环接收服务器端发送过来的信息, * 并输出到控制台 */ class GetServerInfoHandler implements Runnable{ @Override public void run() { try{ //通过socket获取输入流 InputStream in = socket.getInputStream(); //将字节输入流转为字符输入流,指定编码集 InputStreamReader isr = new InputStreamReader(in,"utf-8"); //将字符流转为缓冲字符输入流,这样就可以以行为单位来读取字符串了 BufferedReader br = new BufferedReader(isr); String message = null; //循环读取服务端发送过来的每个字符串 while((message=br.readLine())!=null){ //将服务端发送的字符串输出到控制台 System.out.println(message); } }catch(Exception e){ } } } }
相关文章推荐
- Andorid之网络通信框架Volley使用和总结
- ios 上传多张图片总结——IOS网络访问之使用AFNetworking
- Qt 学习之路 :访问网络(4)
- 【网络通信:Volley】请求的发送与响应之Image
- HTTP 错误码备查
- Android HttpUtils工具类
- IOS网络第七天WebView-04仿网易新闻详情
- IOS网络第七天WebView-03js中调用webView中的代码
- IOS网络第七天WebView-02WebView和网页的交互2,删除大众点评多余文字,加上蒙版进度
- 整个网络可能最完善的 Android 自定义键盘 问题汇总以及解决方案
- IOS网络第七天WebView-01WebView和网页的交互1
- IOS网络第六天 ASI (略)
- IOS网络第五天 AFN-03-监控网络状态
- caffe架构学习之(一)--基于google protocol buffer开源项目的深度网络定义
- IOS网络第五天 AFN-02-文件上传,底部弹出窗体,拍照和相册获取图片上传
- AFN 上传文件的方法,AFN 监测网络状态,Reachability 监测网络状态
- HTTP状态码
- [原]fclose(stdout)和close(1)的区别 http://m.blog.csdn.net/blog/wangzuxi_11109/43445599
- 使用HttpURLConnection向服务器发送post和get请求(转)
- 使用ListView和AsyncTask、fastjson解析Json以及适配器BaseAdapter来实现下载网络的图片以及文字并显示出来