标签:
服务端的代码如下:
package com; 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.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; @SuppressWarnings("unused") public class Server { private ExecutorService threadPool; //线程池,用来管理与客户端交互的线程(重用线程) private List<PrintWriter> allOut; //定义一个集合List,存放所有客户端输出流的共享集合 /* * java.net.ServerSocket运行在服务端的Socket。主要工作是申请服务端的端口, * 并且监听该端口, 等待客户端的连接。一旦一个客户端与服务端建立连接, * ServerSocket会返回与该客户端沟通的Socket。 */ private ServerSocket server; public Server() //服务端构造方法,用来初始化服务端的相关属性 { try { //初始化 server = new ServerSocket(8088); allOut = new ArrayList<PrintWriter>(); 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 void start() { try { //ServerSocket的accept方法:该方法用来监听服务端打开的8088端口 //,等待客户端的连接 //一旦一个客户端连接了就会返回与该客户端进行通信的Socket。 while(true) { System.out.println("等待客户端连接……"); Socket socket = server.accept(); //accept是一个阻塞方法,直到一个客户端连接,否则会一直卡在这里 System.out.println("一个客户端连接了!"); //启动一个线程,来完成与刚刚连接的客户端的交互工作 ClientHandler handler = new ClientHandler(socket); //Thread t = new Thread(handler); //t.start(); threadPool.execute(handler); //把上面单独新建的一个线程,改成可以管理多个线程的线程池来使用 } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { Server server = new Server(); server.start(); } //定义一个内部类,新建runnable新的线程 class ClientHandler implements Runnable { //当前线程用来交互的客户端的Socket private Socket socket; //创建线程任务的同时指定要交互的客户端的Socket private String host; //该客户端地址 public ClientHandler(Socket socket) { //通过Socket我们是可以得知远端计算机的信息 InetAddress address = socket.getInetAddress(); //IP host = address.getHostAddress(); int port = socket.getPort();//端口 System.out.println(host + "上线"); this.socket = socket; } public void run() { 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 = new BufferedReader(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(); } } } } }
客户端代码如下:
package com; import java.io.BufferedReader; import java.io.FileInputStream; 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.Properties; import java.util.Scanner; public class Client { //java.net.Socket封装了TCP协议通讯的Socket //使用它我们就可以与服务端的ServerSocket进行连接, //然后通过输入流、输出流与服务端进行数据交换 ,达到网络通信的目的 private Socket socket; public Client() { try { //初始化时通常传入两个参数: //1:远程计算机的地址(服务端IP地址) //2:服务端打开的端口号 //端口号0-65535 //前1000不要用,10000以后也很少用。 //端口是系统用来将外界数据转发给某个应用程序的,每个程序都如果想访问网络, //都需要申请一个对外的端口,这样当外界通过IP地址找到我们的计算机后, //通过该端口就可以将数据交给我们的程序来处理。 //读取配置文件,将Socket需要的两个参数读取回来,然后再创建Socket。 //java.util.Properties借助该类来读取配置文件config.properties Properties prop = new Properties(); prop.load(new FileInputStream("config.properties")); // void load(InputStream in) 通过给定的流来读取配置内容 //我们的配置内容写在了文件config.properties中,所以我们可以创建一个读取该文件的流。 //这样,load方法就可以通过这个流读取该文件上的配置内容了。 //当读取完毕后,Properties对象就可以表示配置文件中所有内容,我们可以类似Map的形式获取对应的配置内容, //例如:配置文件中的一行 serverhost=localhost //可以根据“=”左边的内容获取右边对应的值 //需要注意的是:key与value都是字符串 String serverHost = prop.getProperty("serverhost"); int serverPort = Integer.parseInt(prop.getProperty("serverport")); socket = new Socket(serverHost, serverPort); //创建连接,成功返回Socket实例,否则抛异常 //8088是服务端那边的程序申请的对外端口,可以通过8088连接服务端的程序 } catch (Exception e) { e.printStackTrace(); } } //客户端开始工作的方法 public void start() { //让客户端向服务器做一次会话,输出流 try { //启动线程,来接收服务端发送过来的消息 GetServerMessageHandler handler = new GetServerMessageHandler(); Thread t = new Thread(handler); t.start(); //Socket的getOutputStream方法,可以获取一个直接输出流, //通过该流写出去的数据将会发送到远端(发送给服务器) //可以通过这个流发送文本、图片等,输出流 OutputStream out = socket.getOutputStream(); //把字节流转成字符流,要考虑字符集的编码问题,要用到转换流 OutputStreamWriter osw = new OutputStreamWriter(out,"utf-8"); //通过PrintWriter写出字符流 PrintWriter pw = new PrintWriter(osw, true); //true自动行刷新 Scanner sc = new Scanner(System.in); while(true) { String message = sc.nextLine(); pw.println(message); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { Client client = new Client(); client.start(); } //新建一个内部类,作为新的runnable新线程,该线程的作用是接受服务端发送过来的每一行消息,并输出到客户端的控制台 class GetServerMessageHandler implements Runnable { public void run() { try { //1.通过Socket获取输入流 //2.转换为缓冲字符输入流 //3.循环读取服务端发送的每行消息 //4.将消息输出到控制台 //1 InputStream in = socket.getInputStream(); //2 InputStreamReader isr = new InputStreamReader(in,"UTF-8"); //3 BufferedReader br = new BufferedReader(isr); String message = null; while((message = br.readLine()) != null) { System.out.println(message); } } catch (Exception e) { e.printStackTrace(); } } } }
标签:
原文地址:http://www.cnblogs.com/cyjs1988/p/4497242.html