目录
一、UDP 通信
1. 简介
UDP 是 User Datagram Protocol 的简称, 中文名是用户数据报协议,是OSI(Open System
Interconnection,开放式系统互联)参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送
服务。Java 主要提供了两个类来实现基于 UDP 的 Socket 编程。
2. UDP 编程的两个实现类
DatagramSocket
此类表示用来发送和接收数据报包的套接字。
数据报套接字是包投递服务的发送或接收点,每个在数据报套接字上发送或接收的包都是单独编址和路由的。
从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。
在 DatagramSocket 上总是启用 UDP 广播发送。
DatagramPacket
DatagramPacket.class:此类表示数据报包,它用来实现无连接的包投递服务。
DatagramPacket 会根据该包中包含的地址和端口等信息,将报文从一台机器路由到另一台机器。
构造函数
- 一个用来接收数据 DatagramPacket(byte buf[], int length) ,用一个字节数组接收 UDP 包,buf 数组在传
递给构造函数时是空的,而 length 值用来设定要读取的字节数。
- 另一个用来发送数据 DatagramPacket(byte buf[], int length, InetAddress address, int port),建立将要
传输的 UDP 包,并指定 ip 地址和端口号。
3. 代码示例
示例一:一发/一收
发送端
/**
发送端 一发 一收
*/
public class ClientDemo1 {
public static void main(String[] args) throws Exception {
System.out.println("=====客户端启动======");
// 1、创建发送端对象:发送端自带默认的端口号(人)
DatagramSocket socket = new DatagramSocket(666);
// 2、创建一个数据包对象封装数据(韭菜盘子)
/**
public DatagramPacket(byte buf[], int length,
InetAddress address, int port)
参数一:封装要发送的数据(韭菜)
参数二:发送数据的大小
参数三:服务端的主机IP地址
参数四:服务端的端口
*/
byte[] buffer = "我是一颗快乐的韭菜,你愿意吃吗?".getBytes();
DatagramPacket packet = new DatagramPacket( buffer, buffer.length,
InetAddress.getLocalHost() , 8888);
// 3、发送数据出去
socket.send(packet);
socket.close();
}
}
接收端
public class ServerDemo2 {
public static void main(String[] args) throws Exception {
System.out.println("=====服务端启动======");
// 1、创建接收端对象:注册端口(人)
DatagramSocket socket = new DatagramSocket(8888);
// 2、创建一个数据包对象接收数据(韭菜盘子)
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 3、等待接收数据。
socket.receive(packet);
// 4、取出数据即可
// 读取多少倒出多少
int len = packet.getLength();
String rs = new String(buffer,0, len);
System.out.println("收到了:" + rs);
// 获取发送端的ip和端口
String ip =packet.getSocketAddress().toString();
System.out.println("对方地址:" + ip);
int port = packet.getPort();
System.out.println("对方端口:" + port);
socket.close();
}
}
示例二:多发/多收
发送端
/**
发送端 多发 多收
*/
public class ClientDemo1 {
public static void main(String[] args) throws Exception {
System.out.println("=====客户端启动======");
// 1、创建发送端对象:发送端自带默认的端口号(人)
DatagramSocket socket = new DatagramSocket(7777);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc.nextLine();
if("exit".equals(msg)){
System.out.println("离线成功!");
socket.close();
break;
}
// 2、创建一个数据包对象封装数据(韭菜盘子)
byte[] buffer = msg.getBytes();
DatagramPacket packet = new DatagramPacket( buffer, buffer.length,
InetAddress.getLocalHost() , 8888);
// 3、发送数据出去
socket.send(packet);
}
}
}
接收端
/**
接收端
*/
public class ServerDemo2 {
public static void main(String[] args) throws Exception {
System.out.println("=====服务端启动======");
// 1、创建接收端对象:注册端口(人)
DatagramSocket socket = new DatagramSocket(8888);
// 2、创建一个数据包对象接收数据(韭菜盘子)
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) {
// 3、等待接收数据。
socket.receive(packet);
// 4、取出数据即可
// 读取多少倒出多少
int len = packet.getLength();
String rs = new String(buffer,0, len);
System.out.println("收到了来自:" + packet.getAddress() +", 对方端口是" + packet.getPort() +"的消息:" + rs);
}
}
}
示例三:多发/多收
发送端
/**
发送端 多发 多收
*/
public class ClientDemo1 {
public static void main(String[] args) throws Exception {
System.out.println("=====客户端启动======");
// 1、创建发送端对象:发送端自带默认的端口号(人)
DatagramSocket socket = new DatagramSocket();
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc.nextLine();
if("exit".equals(msg)){
System.out.println("离线成功!");
socket.close();
break;
}
// 2、创建一个数据包对象封装数据(韭菜盘子)
byte[] buffer = msg.getBytes();
// 注意:只要目的地IP是 255.255.255.255 这个消息将以广播的形式对外发送
DatagramPacket packet = new DatagramPacket( buffer, buffer.length,
InetAddress.getByName("255.255.255.255") , 8888);
// DatagramPacket packet = new DatagramPacket( buffer, buffer.length,
// InetAddress.getByName("224.0.1.1") , 9898);
// 3、发送数据出去
socket.send(packet);
}
}
}
接收端一
/**
接收端
*/
public class ServerDemo2 {
public static void main(String[] args) throws Exception {
System.out.println("=====服务端启动======");
// 1、创建接收端对象:注册端口(人)
DatagramSocket socket = new DatagramSocket(8888);
// 2、创建一个数据包对象接收数据(韭菜盘子)
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) {
// 3、等待接收数据。
socket.receive(packet);
// 4、取出数据即可
// 读取多少倒出多少
int len = packet.getLength();
String rs = new String(buffer,0, len);
System.out.println("收到了来自:" + packet.getAddress() +", 对方端口是" + packet.getPort() +"的消息:" + rs);
}
}
}
接收端二
/**
接收端
*/
public class ServerDemo3 {
public static void main(String[] args) throws Exception {
System.out.println("=====服务端启动======");
// 1、创建接收端对象:注册端口(人)
MulticastSocket socket = new MulticastSocket(9898);
// 注意:绑定组播地址(加群)
socket.joinGroup(new InetSocketAddress(InetAddress.getByName("224.0.1.1") , 9898),
NetworkInterface.getByInetAddress(InetAddress.getLocalHost()));
// 2、创建一个数据包对象接收数据(韭菜盘子)
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) {
// 3、等待接收数据。
socket.receive(packet);
// 4、取出数据即可
// 读取多少倒出多少
int len = packet.getLength();
String rs = new String(buffer,0, len);
System.out.println("收到了来自:" + packet.getAddress() +", 对方端口是" + packet.getPort() +"的消息:" + rs);
}
}
}
示例四:模拟QQ
发送端
public class UdpSend {
public static final String QUIT = "quit";
public static void main(String[] args) throws Exception {
DatagramSocket ds = new DatagramSocket();
byte[] buf = new byte[1024];
while (true) {
System.out.println("请输入:");
Scanner sc = new Scanner(System.in);
String next = sc.next();
if (next.equals(QUIT)) {
// 发送通知退出消息
DatagramPacket send = new DatagramPacket(next.getBytes(), next.length(),
InetAddress.getByName("127.0.0.1"), 3000);
ds.send(send);
ds.close();
System.out.println("程序退出...");
break;
}
// 发送消息数据包
DatagramPacket send = new DatagramPacket(next.getBytes(), next.length(),
InetAddress.getByName("127.0.0.1"), 3000);
// 发送消息
ds.send(send);
System.out.println("消息已经发送...");
// 接收消息数据包
DatagramPacket recive = new DatagramPacket(buf, 1024);
// 接收消息
ds.receive(recive);
String strRecv = new String(recive.getData(), 0, recive.getLength()) +
" from " + send.getAddress().getHostAddress() + ":" + recive.getPort();
System.out.println(strRecv);
}
}
}
接收端
public class UdpRecv {
/**
* 中断标记
*/
public static final String QUIT = "quit";
public static void main(String[] args) throws Exception {
// 创建套接字
DatagramSocket ds = new DatagramSocket(3000);
byte[] buf = new byte[1024];
while (true) {
// 创建接收消息数据包
DatagramPacket recive = new DatagramPacket(buf, 1024);
// 接收消息,如果没有消息,进入阻塞状态
ds.receive(recive);
String strRecv = new String(recive.getData(), 0, recive.getLength()) +
" from " + recive.getAddress().getHostAddress() + ":" + recive.getPort();
// 打印接收到的消息
System.out.println(strRecv);
System.out.println("请输入:");
Scanner sc = new Scanner(System.in);
String next = sc.next();
if (next.equals(QUIT)) {
ds.close();
System.out.println("程序退出...");
break;
}
// 创建发送消息数据包
DatagramPacket send = new DatagramPacket(next.getBytes(), next.length(),
InetAddress.getByName("127.0.0.1"), recive.getPort());
// 发送消息
ds.send(send);
System.out.println("消息已经发送...");
}
}
}
二、TCP 通信
1. 简介
- TCP是一种面向连接、安全、可靠、的传输数据的协议
- 传输前,采用“三次握手”方式,点对点通信,是可靠的
- 在连接中可进行大数据量的传输
如果使用了 WireShark 工具,可以看到一次TCP连接建立时的整个过程。
2. TCP通信的基本原理
- 客户端怎么发,服务端就怎么收
- 客户端如果没有消息,服务端会进入阻塞等待
- Socket一方关闭或者出现异常,对方Socket也会失效或者出错
3. 代码示例
示例一:一发/一收
发送端
/**
目标:完成Socket网络编程入门案例的客户端开发,实现1发1收。
*/
public class ClientDemo1 {
public static void main(String[] args) {
try {
System.out.println("====客户端启动===");
// 1、创建Socket通信管道请求有服务端的连接
// public Socket(String host, int port)
// 参数一:服务端的IP地址
// 参数二:服务端的端口
Socket socket = new Socket("127.0.0.1", 7777);
// 2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
// 3、把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
// 4、发送消息
ps.println("我是TCP的客户端,我已经与你对接,并发出邀请:约吗?");
ps.flush();
// 关闭资源。
// socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
接收端
/**
目标:开发Socket网络编程入门代码的服务端,实现接收消息
*/
public class ServerDemo2 {
public static void main(String[] args) {
try {
System.out.println("===服务端启动成功===");
// 1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
// 2、必须调用accept方法:等待接收客户端的Socket连接请求,建立Socket通信管道
Socket socket = serverSocket.accept();
// 3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
// 4、把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 5、按照行读取消息
String msg;
if ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
示例二:多发/多收
发送端
/**
目标:实现多发和多收
*/
public class ClientDemo1 {
public static void main(String[] args) {
try {
System.out.println("====客户端启动===");
// 1、创建Socket通信管道请求有服务端的连接
// public Socket(String host, int port)
// 参数一:服务端的IP地址
// 参数二:服务端的端口
Socket socket = new Socket("127.0.0.1", 7777);
// 2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
// 3、把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc.nextLine();
// 4、发送消息
ps.println(msg);
ps.flush();
}
// 关闭资源。
// socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
接收端
/**
目标:开发Socket网络编程入门代码的服务端,实现接收消息
*/
public class ServerDemo2 {
public static void main(String[] args) {
try {
System.out.println("===服务端启动成功===");
// 1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
while (true) {
// 2、必须调用accept方法:等待接收客户端的Socket连接请求,建立Socket通信管道
Socket socket = serverSocket.accept();
// 3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
// 4、把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 5、按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
示例三:多发/一收
发送端
/**
目标:实现服务端可以同时处理多个客户端的消息。
*/
public class ClientDemo1 {
public static void main(String[] args) {
try {
System.out.println("====客户端启动===");
// 1、创建Socket通信管道请求有服务端的连接
// public Socket(String host, int port)
// 参数一:服务端的IP地址
// 参数二:服务端的端口
Socket socket = new Socket("127.0.0.1", 7777);
// 2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
// 3、把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc.nextLine();
// 4、发送消息
ps.println(msg);
ps.flush();
}
// 关闭资源。
// socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
// 3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
// 4、把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 5、按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了!!!");
}
}
}
接收端
/**
目标:实现服务端可以同时处理多个客户端的消息。
*/
public class ServerDemo2 {
public static void main(String[] args) {
try {
System.out.println("===服务端启动成功===");
// 1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
// a.定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。
while (true) {
// 2、每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress()+ "它来了,上线了!");
// 3、开始创建独立线程处理socket
new ServerReaderThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
示例四:线程池优化
发送端
/**
拓展:使用线程池优化:实现通信。
*/
public class ClientDemo1 {
public static void main(String[] args) {
try {
System.out.println("====客户端启动===");
// 1、创建Socket通信管道请求有服务端的连接
// public Socket(String host, int port)
// 参数一:服务端的IP地址
// 参数二:服务端的端口
Socket socket = new Socket("127.0.0.1", 6666);
// 2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
// 3、把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc.nextLine();
// 4、发送消息
ps.println(msg);
ps.flush();
}
// 关闭资源。
// socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class ServerReaderRunnable implements Runnable{
private Socket socket;
public ServerReaderRunnable(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
// 3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
// 4、把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 5、按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了!!!");
}
}
}
接收端
/**
目标:实现服务端可以同时处理多个客户端的消息。
*/
public class ServerDemo2 {
// 使用静态变量记住一个线程池对象
private static ExecutorService pool = new ThreadPoolExecutor(300,
1500, 6, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2)
, Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
try {
System.out.println("===服务端启动成功===");
// 1、注册端口
ServerSocket serverSocket = new ServerSocket(6666);
// a.定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。
while (true) {
// 2、每接收到一个客户端的Socket管道,
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress()+ "它来了,上线了!");
// 任务对象负责读取消息。
Runnable target = new ServerReaderRunnable(socket);
pool.execute(target);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
三、综合案例
1. UDP 通信
2. TCP 通信
2.1. TCP通信循环聊天案例
服务器
public class Server {
public static void main(String[] args) throws Exception{
// 服务器:
//1.创建ServerSocket对象,指定端口号 6666
ServerSocket ss = new ServerSocket(6666);
//2.调用accept方法接收客户端请求,建立连接,返回Socket对象
Socket socket = ss.accept(); // 会阻塞,直到客户端连接成功
// 死循环
while (true) {
//3.使用Socket对象获得输入流
InputStream is = socket.getInputStream();
//4.读客户端写过来的数据
byte[] bys = new byte[1024];
int len = is.read(bys);
System.out.println("服务器读到客户端的数据是:" + new String(bys, 0, len));
// 服务器回写数据给客户端
// 5.使用Socket对象获得输出流
OutputStream os = socket.getOutputStream();
// 6.写出数据
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String msg = sc.nextLine();
os.write(msg.getBytes());
// 7.关闭流,释放资源
// socket.close();
// ss.close();// 一般服务器是不关
}
}
}
客户端
public class Client {
public static void main(String[] args) throws Exception{
// 客户端:
//1.创建Socket对象,指定要连接的服务器的ip地址和端口号
Socket socket = new Socket("127.0.0.1",6666);
// 死循环
while (true) {
//2.通过Socket对象获得输出流
OutputStream os = socket.getOutputStream();
//3.写出字符串数据
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String msg = sc.nextLine();
os.write(msg.getBytes());
// 接收服务器回写的数据
//4.通过Socket对象获得输入流
InputStream is = socket.getInputStream();
//5.读服务器回写的字符串数据
byte[] bys = new byte[1024];
int len = is.read(bys);
System.out.println("客户端读到服务器端回写的数据:"+new String(bys,0,len));
//6.关闭流,释放资源
// os.close();
}
}
}
2.2. 文件上传综合案例
需求
使用TCP协议, 通过客户端向服务器上传一个文件
分析
- 【客户端】输入流,从硬盘读取文件数据到程序中。
- 【客户端】输出流,写出文件数据到服务端。
- 【服务端】输入流,读取文件数据到服务端程序。
- 【服务端】输出流,写出文件数据到服务器硬盘中。
- 【服务端】获取输出流,回写数据。
- 【客户端】获取输入流,解析回写数据。
实现
服务器
public class Server {
public static void main(String[] args) throws Exception {
//服务器:
//1.创建ServerSocket对象,指定端口号
ServerSocket ss = new ServerSocket(7777);
// 循环接收请求,建立连接
while (true){
//2.调用accept方法接收请求,建立连接,返回Socket对象
Socket socket = ss.accept();
// 建立连接之后,就开启线程上传文件
new Thread(new Runnable() {
@Override
public void run() {
try {
//4.通过Socket对象获得输入流,关联连接通道
InputStream is = socket.getInputStream();
//5.创建字节输出流,关联目的地文件路径
FileOutputStream fos = new FileOutputStream("file\\bbb\\"+System.currentTimeMillis()+".jpg");
//6.定义一个byte数组,用来存储读取到的字节数据
byte[] bys = new byte[8192];
//7.定义一个int变量,用来存储读取到的字节个数
int len;
System.out.println("服务开始接收文件...");
//8.循环读数据--->连接通道
while ((len = is.read(bys)) != -1) {// 卡住
//9.在循环中,写出数据
fos.write(bys, 0, len);
}
System.out.println("服务器接收文件完毕了!");
//10.通过Socket对象获得输出流
OutputStream os = socket.getOutputStream();
//11.回写数据给客户端
os.write("文件上传成功!".getBytes());
//12.关闭流,释放资源
fos.close();
is.close();
// ss.close();
}catch (Exception e){
}
}
}).start();
}
}
}
客户端
public class Client {
public static void main(String[] args) throws Exception{
//客户端:
//1.创建Socket对象,指定要连接的服务器的ip地址和端口号
Socket socket = new Socket("127.0.0.1",7777);
//2.创建字节输入流,关联数据源文件路径
FileInputStream fis = new FileInputStream("file\\aaa\\mm.jpg");
//3.通过Socket对象获得输出流,关联连接通道
OutputStream os = socket.getOutputStream();
//4.定义一个byte数组,用来存储读取到的字节数据
byte[] bys = new byte[8192];
//5.定义一个int变量,用来存储读取到的字节个数
int len;
System.out.println("客户端开始上传文件...");
//6.循环读数据-->源文件
while ((len = fis.read(bys)) != -1) {
//7.在循环中,写出数据
os.write(bys,0,len);// 最后文件中的字节数据
}
/*
结束不了的原因: 服务器不知道客户端不再写数据过来了,所以服务器会一直等到读取数据
解决办法: 客户端不再写数据了,就需要告诉服务器
*/
socket.shutdownOutput();// 终止写出数据,但流没有关闭
System.out.println("客户端上传文件完毕!");
//8.通过Socket对象获得输入流
InputStream is = socket.getInputStream();
//9.读取服务器回写的数据
int lens = is.read(bys);// 卡住
System.out.println("服务器回写的数据:"+new String(bys,0,lens));
//10.关闭流,释放资源
os.close();
fis.close();
}
}
2.3. 模拟B\S服务器
需求
模拟网站服务器,使用浏览器访问自己编写的服务端程序,查看网页效果。
分析
- 准备页面数据,web文件夹。
- 我们模拟服务器端,ServerSocket类监听端口,使用浏览器访问,查看网页效果
- 注意:
// 1.浏览器工作原理是遇到图片会开启一个线程进行单独的访问,因此在服务器端加入线程技术。
// 2. 响应页面的时候需要同时把以下信息响应过去给浏览器
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
os.write("\r\n".getBytes());
实现
public class Server {
public static void main(String[] args) throws Exception {
//1.创建ServerSocket对象,指定端口号为8888
ServerSocket ss = new ServerSocket(8888);
// 浏览器工作原理是遇到图片会开启一个线程进行单独的访问,因此在服务器端加入线程技术。
while (true){
//2.调用accept方法接收请求,建立连接,返回Socket对象
Socket socket = ss.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
//3.通过Socket对象获得输入流
InputStream is = socket.getInputStream();
//4.通过输入流读取连接通道中的请求数据(浏览器发过来)
// byte[] bys = new byte[8192];
// int len = is.read(bys);
// System.out.println(new String(bys,0,len));
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = br.readLine();
System.out.println("line:" + line);
//5.通过请求数据筛选出要访问的页面的路径(file\web\index.html)
String[] arr = line.split(" ");
String path = arr[1].substring(1);
System.out.println("path:"+path);
//6.创建字节输入流对象,关联需要访问的页面的路径
FileInputStream fis = new FileInputStream(path);
//7.通过Socket对象获得输出流,关联连接通道
OutputStream os = socket.getOutputStream();
//8.定义一个byte数组,用来存储读取到的字节数据
byte[] bys = new byte[8192];
//9.定义一个int变量,用来存储读取到的字节个数
int len;
//10.循环读取数据
// 响应页面的时候需要同时把以下信息响应过去给浏览器
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
os.write("\r\n".getBytes());
while ((len = fis.read(bys)) != -1) {
//11.在循环中,写数据给浏览器
os.write(bys,0,len);
}
//12.关闭流,释放资源
os.close();
fis.close();
//ss.close();
}catch (Exception e){
}
}
}).start();
}
}
}