即时通信是什么含义,要实现怎么样的设计?
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<Socket> 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("<span style='color:red;font-size:90px'>《遇安.》 </span>");
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("<span style='color:red;font-size:90px'>《遇安.》 </span>");
ps.close();
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了");
}
}
}
在浏览器的效果图: