客户端:
创建Socket对象,指定服务端的IP和端口,与服务端建立连接。
创建客户端读取消息的线程任务类:
a. 定义socket变量,用于存储接收到的客户端的socket管道连接请求
b. 定义有参数构造器,用于接收客户端的socket请求,初始化到socket变量
c. 重写run方法:
创建一个独立的线程对象专门负责这个客户端的读取消息任务(服务端随机可能转发其他客户端的消息过来!)
从socket通信管道中得到字节输出流管道,并包装成打印流管道
客户端开始发送数据给服务端
package com.app.d10_tcp_socket5_sms;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
/**
需求:
1、客户端可以发送消息
2、客户端可以接收到其他客户端和自己发出的消息
*/
public class Client {
public static void main(String[] args) throws Exception {
System.out.println("-=-=-=-客户端启动成功-=-=-=-");
// 1、创建Socket对象,指定服务端的IP和端口,与服务端建立连接
Socket socket = new Socket("127.0.0.1", 7878);
// 3、创建一个独立的线程对象专门负责这个客户端的读取消息任务(服务端随时可能转发其他客户端的消息过来!)
new ClientReaderThread(socket).start();
// 4、从socket通信管道中得到字节输出流管道,并包装成打印流管道
PrintStream ps = new PrintStream(socket.getOutputStream());
// 5、客户端开始发送数据给服务端
Scanner sc = new Scanner(System.in);
while (true) {
String msg = sc.nextLine();
ps.println(msg);
ps.flush(); // 刷新数据
}
}
}
package com.app.d10_tcp_socket5_sms;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
/**
2、创建客户端读取消息的线程任务类
*/
public class ClientReaderThread extends Thread{
// a. 定义socket变量,用于存储接收到的客户端的socket请求
private Socket socket;
// b. 定义有参数构造器,用于接收客户端的socket请求,初始化到socket变量
public ClientReaderThread(Socket socket) {
this.socket = socket;
}
// c. 重写run方法:
@Override
public void run() {
try {
// 1. 从socket通信管道中得到字节输入流管道
InputStream is = socket.getInputStream();
// 2. 将字节输入流管道转换成字符输入流管道,并包装成缓冲字符输入流管道,负责接收服务端转发其他客户端发送的消息过来
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 3. 使用死循环开始按照行读取服务端转发过来的数据
String msg;
while ( (msg = br.readLine()) != null ) {
// 4. 展示读取到的数据
System.out.println("收到了:" + msg);
}
} catch (Exception e) {
// 5. 如果出现异常,说明群主已将你踢出群聊(服务端把你踢出去了~)
System.out.println("服务端已将你踢出~~");
}
}
}
服务端:
创建静态的List集合,用于存储当前全部在线的客户端的socket管道
创建ServerSocket对象,注册端口
使用死循环接收客户端的socket管道连接请求
开始接收每个客户端的socket管道连接请求(注意:在这里等待到客户端的socket管道连接)
跟踪客户端上线,将上线的客户端的socket管道添加到List集合中,完成上线
创建服务端接收客户端socket管道连接请求的线程任务类:
a. 定义socket变量,用于存储接收到的客户端的socket请求
b. 定义有参数构造器,用于接收客户端的socket请求,初始化到socket变量
c. 重写run方法:
从socket通信管道中得到字节输入流管道
将字节输入流管道转换成字符输入流管道,并包装成缓冲字符输入流管道:负责接收服务端转发其他客户端发送的消息过来
使用死循环开始按照行读取客户端发送过来的数据
展示读取到的数据
创建独立的方法sendMsgToAllSockets:将该客户端发送的消息进行端口转发给全部在线的客户端的socket管道
a. 遍历全部在线的客户端的socket管道的集合allOnlineSockets:
1. 从socket管道中得到字节输出流管道,并包装成打印流管道:负责转发消息给全部在线的客户端的socket管道
package com.app.d10_tcp_socket5_sms;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
需求:
1、服务端接收多个客户端发送的消息
2、服务端可以将多个客户端发送的消息转发给全部在线的客户端
*/
public class Server {
// 1、创建静态的List集合,用于存储当前全部在线的客户端的socket管道
public static List<Socket> allOnlineSockets = new ArrayList<>();
public static void main(String[] args) throws Exception {
System.out.println("-=-=-=-服务端启动成功-=-=-=-");
// 2、创建ServerSocket对象,注册端口
ServerSocket serverSocket = new ServerSocket(7878);
// 3、使用死循环接收客户端的socket管道连接请求
while (true) {
// 4、开始接收每个客户端的socket管道连接请求
// 注意:在这里等到客户端的socket管道连接
Socket socket = serverSocket.accept();
// 5、跟踪客户端上线,将上线的客户端的socket管道添加到List集合中,完成上线
System.out.println(socket.getRemoteSocketAddress() + "上线了~~");
allOnlineSockets.add(socket);
// 7、每接收到一个客户端的socket管道连接请求,创建一个独立的线程对象专门负责这个客户端的socket管道连接请求的任务
new ServerReaderThread(socket).start();
}
}
}
package com.app.d10_tcp_socket5_sms;
import java.io.*;
import java.net.Socket;
/**
6、创建服务端接收客户端socket管道连接请求的线程任务类
*/
public class ServerReaderThread extends Thread {
// a. 定义socket变量,用于存储接收到的客户端的socket请求
private Socket socket;
// b. 定义有参数构造器,用于接收客户端的socket请求,初始化到socket变量
public ServerReaderThread(Socket socket) {
this.socket = socket;
}
// c. 重写run方法:
@Override
public void run() {
try {
// 1. 从socket通信管道中得到字节输入流管道
InputStream is = socket.getInputStream();
// 2. 将字节输入流管道转换成字符输入流管道,并包装成缓冲字符输入流管道,负责接收服务端转发其他客户端发送的消息过来
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 3. 使用死循环开始按照行读取客户端发送过来的数据
String msg;
while ( (msg = br.readLine()) != null ) {
// 4. 展示读取到的数据
System.out.println(socket.getRemoteSocketAddress() + "说了:" + msg);
// 5、创建独立的方法sendMsgToAllSockets:将该客户端发送的消息进行端口转发给全部在线的客户端的socket管道
sendMsgToAllSockets(msg);
}
} catch (Exception e) {
// 6. 跟踪客户端下线,将下线的客户端的socket管道从List集合中删除
System.out.println(socket.getRemoteSocketAddress() + "离线了~~");
Server.allOnlineSockets.remove(socket);
}
}
/**
将消息进行端口转发给所有在线的客户端的方法
*/
private void sendMsgToAllSockets(String msg) throws Exception {
// a.遍历全部在线的客户端的socket管道的集合allOlineSockets
for (Socket onlineSocket : Server.allOnlineSockets) {
// 1. 从socket管道中得到字节输出流管道,并包装成打印流管道:负责转发消息给全部在线的客户端socket管道
PrintStream ps = new PrintStream(onlineSocket.getOutputStream());
ps.println(msg);
ps.flush(); // 刷新数据
}
}
}
1、即时通信是什么含义,要实现怎么样设计?
注意:服务器必须给浏览器响应HTTP协议格式的数据,否则浏览器不识别。
HTTP响应数据的协议格式:就是给浏览器显示的网页信息:
package com.app.d11_tcp_socket6_thread_bs;
import java.net.ServerSocket;
import java.net.Socket;
/**
目标:理解BS结构的基本原理
*/
public class BSServerDemo {
public static void main(String[] args) {
System.out.println("---------服务端启动成功----------");
try {
// 1、创建ServerSocket对象,注册端口
ServerSocket serverSocket = new ServerSocket(8080);
// 3、使用死循环接收多个客户端的socket管道连接请求
while (true) {
// a. 开始接收多个客户端的socket管道连接请求
Socket socket = serverSocket.accept();
// b. 将socket管道连接请求交给一个独立的线程来处理
new ServerReaderThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.app.d11_tcp_socket6_thread_bs;
import java.io.PrintStream;
import java.net.Socket;
/**
2、创建线程任务类处理多个客户端的Socket管道连接请求
*/
public class ServerReaderThread extends Thread{
// a. 定义socket变量,用于存储接收到的客户端的socket管道连接请求
private Socket socket;
// b. 定义有参数构造器,用于接收客户端的socket管道连接请求,并初始化给socket变量
public ServerReaderThread(Socket socket) {
this.socket = socket;
}
/**
c. 重写run方法
*/
@Override
public void run() {
try {
// 浏览器已经与本线程建立了Socket管道
// 1、响应消息给浏览器显示
PrintStream ps = new PrintStream(socket.getOutputStream());
// 2、必须响应HTTP协议格式数据,否则浏览器不认识消息
ps.println("HTTP/1.1 200 OK"); // 协议类型/版本 状态码 响应成功的消息
ps.println("Content-Type:text/html;charset=UTF-8"); // 响应的数据类型:文本/网页;字符编码
ps.println(); // 必须发送一个空行
// 3、响应数据回去给浏览器
ps.println(" 《我是歌手》 ");
ps.close(); // 释放资源!
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.app.d12_tcp_socket7_threadpool_bs;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
/**
目标:理解BS结构的基本原理,使用线程池优化BS
*/
public class BSServerDemo {
// 定义静态的线程池,用于处理多个客户端的socket管道连接请求
public static final ExecutorService pools = new ThreadPoolExecutor(3,
5, 6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
System.out.println("---------服务端启动成功----------");
try {
// 1、创建ServerSocket对象,注册端口
ServerSocket serverSocket = new ServerSocket(8080);
// 3、使用死循环接收多个客户端的socket管道连接请求
while (true) {
// a. 开始接收多个客户端的socket管道连接请求
Socket socket = serverSocket.accept();
// b. 将socket管道连接请求交给线程池来处理
pools.execute(new ServerReaderRunnable(socket));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.app.d12_tcp_socket7_threadpool_bs;
import java.io.PrintStream;
import java.net.Socket;
/**
2、创建线程任务类处理多个客户端的Socket管道连接请求
*/
public class ServerReaderRunnable implements Runnable{
// a. 定义socket变量,用于存储接收到的客户端的socket管道连接请求
private Socket socket;
// b. 定义有参数构造器,用于接收客户端的socket管道连接请求,并初始化给socket变量
public ServerReaderRunnable(Socket socket) {
this.socket = socket;
}
/**
c. 重写run方法
*/
@Override
public void run() {
try {
// 浏览器已经与本线程建立了Socket管道
// 1、响应消息给浏览器显示
PrintStream ps = new PrintStream(socket.getOutputStream());
// 2、必须响应HTTP协议格式数据,否则浏览器不认识消息
ps.println("HTTP/1.1 200 OK"); // 协议类型/版本 状态码 响应成功的消息
ps.println("Content-Type:text/html;charset=UTF-8"); // 响应的数据类型:文本/网页;字符编码
ps.println(); // 必须发送一个空行
// 3、响应数据回去给浏览器
ps.println(" 《我是歌手》 ");
ps.close(); // 释放资源!
} catch (Exception e) {
e.printStackTrace();
}
}
}
1、TCP通信如何实现BS请求网页信息回来?