TCP实战案例之即时通信、BS架构模拟
即时通信是什么含义,要实现怎么样的设计?
1、即时通信,是指一个客户端的消息发出去,其他客户端可以接收到
2、之前我们的消息都是发给服务端的
3、即时通信需要进行端口转发的设计思想
4、服务端需要把在线的Socket管道存储起来
5、一旦收到一个消息要推送给其他管道
客户端:
import java.io.*;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;import java.util.List;import java.util.Scanner;public class TCP_实战案例_即时通信 { public static void main(String[] args) throws Exception { System.out.println("-----客户端启动-----"); //创建Socket通信管道请求与服务器的连接 /* public Socket(String host, int port) 参数一、服务器的IP地址 参数二、服务器的端口 */ Socket socket = new Socket("127.0.0.1", 7777); //创建一个独立的线程专门负责这个客户端的读消息(服务端随时可能转发!) new ClientReaderThread(socket).start(); //从Socket通信管道中得到一个字节输出流,负责发送数据 OutputStream out = socket.getOutputStream(); //把低级的字节流包装成打印流 PrintStream ps = new PrintStream(out); Scanner sc = new Scanner(System.in); while (true) { System.out.println("请输入你要发送的消息:"); String line = sc.nextLine(); //发送消息 ps.println(line); //刷新 ps.flush(); } }}//客户端读消息线程class ClientReaderThread extends Thread{ private Socket socket; public ClientReaderThread(Socket socket) { this.socket = socket; } @Override public void run() { try { //从Socket通信管道中得到一个字节输入流 InputStream is = socket.getInputStream(); //把字节输入流包装成缓冲字节输入流进行消息的接收 BufferedReader br = new BufferedReader(new InputStreamReader(is)); //按照行读取消息 String line; while ((line = br.readLine()) != null) { System.out.println("收到消息:" + line); } } catch (Exception e) { System.out.println( "服务端已把你踢出!"); } }}
服务端:
import java.io.*;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;import java.util.List;import java.util.Scanner;class TCP_即时通信_服务端 { //定义一个静态的List集合存储当前全部在线的socket管道 public static List allonlineSockets=new ArrayList(); public static void main(String[] args) throws Exception { System.out.println("-----服务端启动-----"); //注册端口 ServerSocket serverSocket = new ServerSocket(7777); //定义一个死循环由主线程负责不断的接收客户端的Socket通信管道连接 while (true) { //每接收一个客户端的Socket通信管道,交给一个独立的子线程负责读取信息 Socket socket = serverSocket.accept(); System.out.println(socket.getRemoteSocketAddress() + "上线了"); allonlineSockets.add(socket);//上线完成 //创建一个独立的线程单独处理这个socket管道 new ServerReaderThread(socket).start(); } }}class ServerReaderThread extends Thread { private Socket socket; public ServerReaderThread(Socket socket) { this.socket = socket; } @Override public void run() { try { //从Socket通信管道中得到一个字节输入流 InputStream is = socket.getInputStream(); //把字节输入流包装成缓冲字节输入流进行消息的接收 BufferedReader br = new BufferedReader(new InputStreamReader(is)); //按照行读取消息 String line; while ((line = br.readLine()) != null) { System.out.println("收到了消息:" + line); //把这个消息进行端口转发给全部客户端socket管道 sendMsgToAll(line); } } catch (Exception e) { System.out.println(socket.getRemoteSocketAddress() + "下线了"); TCP_即时通信_服务端.allonlineSockets.remove(socket);//删除 } }//独立功能独立成方法 private void sendMsgToAll(String msg) throws Exception { for (Socket socket : TCP_即时通信_服务端.allonlineSockets) { PrintStream ps=new PrintStream(socket.getOutputStream()); ps.println(msg); ps.flush(); } }}
运行效果:
这里有个缺陷就是发的消息没有屏蔽自己,两个一样的是因为开了并发。
下面是服务端关闭后的效果:
BS架构模拟:
1、之前的客户端都是什么样的?
其实就是CS架构,客户端是需要我们自己开发实现的。
2、BS结构是什么样的,需要开发客户端吗?
浏览器访问服务端,不需要开发客户端。
简单模拟:
import java.io.*;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.*;public class TCP_BS架构 { public static void main(String[] args) throws Exception { //注册端口 ServerSocket serverSocket = new ServerSocket(7070); //定义一个死循环由主线程负责不断的接收客户端的Socket通信管道连接 while (true) { //每接收一个客户端的Socket通信管道,交给一个独立的子线程负责读取信息 Socket socket = serverSocket.accept(); //创建一个独立的线程单独处理这个socket管道 new ServerReaderThread2(socket).start(); } }}class ServerReaderThread2 extends Thread { private Socket socket; public ServerReaderThread2(Socket socket) { this.socket = socket; } @Override public void run() { //浏览器已经与本线程建立socket管道 //响应消息给浏览器显示 try { PrintStream ps=new PrintStream(socket.getOutputStream()); //必须响应HTTP协议格式数据,否则浏览器不认识消息 ps.println("HTTP/1.1 200 OK");//协议类型和版本 响应成功的消息 ps.println("Content-Type:text/html;charset=UTF-8");//响应的数据类型:文本/网页 ps.println();//必须发送一个空格 //才可以响应数据回去给浏览器 ps.println("《遇安.》 "); ps.close(); } catch (Exception e) { e.printStackTrace(); } }}
线程池优化:
import java.io.*;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.*;public class TCP_BS架构 { private static ExecutorService pools = new ThreadPoolExecutor(3, 5, 6, TimeUnit.SECONDS, new ArrayBlockingQueue(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); public static void main(String[] args) throws Exception { //注册端口 ServerSocket serverSocket = new ServerSocket(7070); //定义一个死循环由主线程负责不断的接收客户端的Socket通信管道连接 while (true) { //每接收一个客户端的Socket通信管道,交给一个独立的子线程负责读取信息 Socket socket = serverSocket.accept(); pools.execute(new ServerRunnable2(socket)); } }}class ServerRunnable2 implements Runnable { private Socket socket; public ServerRunnable2(Socket socket) { this.socket = socket; } @Override public void run() { //浏览器已经与本线程建立socket管道 //响应消息给浏览器显示 try { PrintStream ps=new PrintStream(socket.getOutputStream()); //必须响应HTTP协议格式数据,否则浏览器不认识消息 ps.println("HTTP/1.1 200 OK");//协议类型和版本 响应成功的消息 ps.println("Content-Type:text/html;charset=UTF-8");//响应的数据类型:文本/网页 ps.println();//必须发送一个空格 //才可以响应数据回去给浏览器 ps.println("《遇安.》 "); ps.close(); } catch (Exception e) { System.out.println(socket.getRemoteSocketAddress() + "下线了"); } }}
在浏览器的效果图:
开发者涨薪指南
48位大咖的思考法则、工作方式、逻辑体系