Java学习笔记@网络编程

笔者:unirithe

日期:11/11/2021

网络编程

  • 网络编程可以让程序与网络上的其他设备中的程序进行数据交互

网络通信基本模式:

  • Client/Server (CS)

    Client

    • 需程序员开发实现
    • 用户需安装客户端

    Server

    • 需程序员开发实现
  • Browser/Server(BS)

    Browser

    • 无需程序员开发实现
    • 用户需要安装浏览器

    Server

    • 需程序员开发实现(JavaWeb)

相关知识:

  • 网络通信3要素IP、端口号、协议:一个消息发送给对方需要哪些关键因素

  • UDP通信: 消息直接发送给对方,不确认对方是否在线,不做消息确认

  • TCP 通信: 基于可靠传输的方式进行的通信模式。解决不同场景的通信需求

  • 即时通信:如何实现即时通信,具体如何实现

  • 模拟BS系统:WEB系统是如何支持访问到网页的,具体是如何与服务器通信的

网络通信三要素

网络通信三要素主要如下:

  • IP地址:设备在网络中的地址,是唯一的标识

  • 端口:应用程序在设备中唯一的标识

  • 协议:数据在网络中传输的规则,常见的协议有UDP协议和TCP协议

IP地址

  • IP(Internet Protocol):全称"互联网协议地址",是分配给上网设备的唯一标志
  • 常见的IP分类为:IPv4和IPv6

IPv4

32bit(4字节)点分十进制表示法,比如:192.168.1.1

IPv6

128bit(16字节),号称可以为地球每一粒沙子编号

分为8个整数,每个整数用4个十六进制位表示,数之间用冒号(:)分开

比如:ABCD:EF01:2345:6789:ABCD:EF01:2345:6789

IP地址基本寻路

image

IP地址形式

  • 公网地址和私有地址(局域网使用)

  • 192.168.开头的是常见的局域网地址,范围为192.168.0.0 - 192.168.255.255,专门为组织机构内部使用

Windows常用命令

  • ipconfig :查看本机IP地址

image

  • ping IP/域名地址 : 检查两个网络之间是否连通

image

特殊IP地址:

本机IP:127.0.0.1 或者 localhost : 称为回送地址也可称为本地回环地址,只会寻找当前所在本机

IP地址操作类 —— InetAddress

java.net.InetAddress类表示Internet协议(IP)地址

常用API

方法 描述
public static InetAddress getLocalHost() 返回本主机的地址对象
public static InetAddress getByName(String host) 得到指定主机的IP地址对象,参数是域名或IP地址
public String getHostName() 获取此IP地址的主机名
public String getHostAddress() 返回IP地址字符串
public boolean isReachable(int timeout) 在指定毫秒内连通该IP地址对应的主机,连通返回true

范例:查询本机IP地址

InetAddress ip1 = InetAddress.getLocalHost();
System.out.println(ip1.getHostName());
System.out.println(ip1.getHostAddress());

范例:获取域名IP对象,根据域名查询百度IP地址

InetAddress baidu = InetAddress.getByName("www.baidu.com");
System.out.println(baidu.getHostAddress());

范例:获取公网IP对象,根据百度IP地址创建

InetAddress baidu = InetAddress.getByName("36.152.44.95");
System.out.println(baidu.getHostName());

范例:判断3秒内是否可连通百度IP地址

InetAddress baidu = InetAddress.getByName("www.baidu.com");
System.out.println(baidu.isReachable(3000));

端口号

端口号:标识正在计算机设备上运行的进程(程序),被规定为一个16的二进制,范围是0~65535 (0 ~ \(2^{16} - 1\))

端口类型

  • 周知端口: 0 ~ 1023, 被预先定义的知名应用占用(如:HTTP占用80, FTP占用21, DNS占用53)
  • 注册端口:1024 ~ 49151, 分配给用户进程或某些应用程序。(如:Tomcat占用8080,Mysql占用3306)
  • 动态端口:49152 ~ 65535, 一般不固定分配某种进程,而是动态分配

PS:自己开发的程序选择注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错

协议

网络通信协议:指连接和通信数据的规则

两种参考模型

  • OSI 参考模型: 世界互联协议标准,全球通信规范,由于过于理想化,故未能在因特网上广泛推广
  • TCP/IP 参考模型:事实上的国际标准

image

传输层的两个常见协议

  • TCP(Transmission Control Protocol): 传输控制协议
  • UDP(User Datagram Protocol):用户数据报协议

TCP协议

TCP协议特点:

  • 使用TCP协议,必须双方先建立连接,是一种面向连接可靠通信协议
  • 具有可靠性,传输前,采用“三次握手” 方式建立连接
  • 连接中可进行大数据量的传输
  • 连接、发送数据都需要确认,且传输完毕后还需释放已建立的连接,通信效率较

TCP协议通信场景

  • 对信息安全要求较高的场景,例如:文件下载、金融等数据通信

TCP 三次握手 建立连接

image

TCP 四次挥手 断开连接

image

UDP协议

  • UDP是一种无连接、不可靠传输的协议
  • 将数据源IP、目的地IP和端口封装成数据包,无需建立连接
  • 每个数据包的大小限制在64KB内
  • 不可靠性,发送不管对方是否准备,接收方收到也无需确认
  • 可广播发送,发送数据结束时无需释放资源,开销小,速度快

UDP协议通信场景: 语音通话、视频会话等

UDP 通信

DatagramPacket 数据包对象

创建发送端数据包对象的构造器

public DatagramPacket(byte buf[], int offset, int length,
                      InetAddress address, int port) {
    setData(buf, offset, length);
    setAddress(address);
    setPort(port);
}
  • buf:要发送的内容,字节数组
  • length:要发送内容的字节长度
  • address:接收端的IP地址对象
  • port:接收端的端口号

创建接收端的数据包对象的构造器

public DatagramPacket(byte buf[], int length) {
    this (buf, 0, length);
}
  • buff:存储接收的内容
  • length:能够接收内容的长度

常用方法

public int getLength() 得到实际接收到的字节个数

DatagramSocket 发送端和接收端对象

构造器 描述
public DatagramPacket() 创建发送端的Socket对象,系统会随机分配一个端口号
public DatagramPacket(int port) 创建接收端的Socket对象并指定端口号

DatagramSocket类成员方法

public void send(DatagramPacket dp) ——发送数据包

public void receive(DatagramPacket p) ——接收数据包

UDP案例

一发一收

ServerDemo.java

import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ServerDemo {
    public static void main(String[] args) throws Exception{
        System.out.println("======服务端启动======");
        // 1. 创建接收端对象 , 注册端口
        DatagramSocket socket = new DatagramSocket(8888);
        // 2. 创建数据包对象,接受数据
        byte[] data = new byte[1024 * 64];
        DatagramPacket packet = new DatagramPacket(
                data,
                data.length);
        // 3. 等待接收数据
        socket.receive(packet);

        String res = new String(data, 0, packet.getLength());
        System.out.println("接受数据: " + res);
        System.out.println("对方IP: " +packet.getSocketAddress().toString());
        System.out.println("对方端口: " + packet.getPort());
        socket.close();
    }
}

ClientDemo.java

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class ClientDemo{
    public static void main(String[] args) throws Exception {
        System.out.println("======客户端启动======");
        // 1. 发送端自带默认的端口号
        DatagramSocket socket = new DatagramSocket();
        // 2. 创建数据包封装数据,发送数据
        byte[] data = "测试".getBytes();
        DatagramPacket packet = new DatagramPacket(data,
                data.length,
                InetAddress.getLocalHost(),
                8888);
        // 发送数据
        socket.send(packet);
        socket.close();
    }
}

多发多收

需求:使用UDP通信方式开发接收端和发送端

分析:

  • 发送端可以一直发送消息
  • 接收端可以不断的接收多个发送端的消息展示
  • 发送端输入exit则结束程序

ClientDemo.java

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Scanner;

public class ClientDemo{
    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.print("发送数据: ");
            String msg = sc.nextLine();
            if("exit".equals(msg)){
                System.out.println("离线成功");
                socket.close();
                break;
            }
            // 2. 创建数据包封装数据,发送数据
            byte[] data = msg.getBytes();
            DatagramPacket packet = new DatagramPacket(data,
                    data.length,
                    InetAddress.getLocalHost(),
                    8888);
            // 发送数据
            socket.send(packet);
        }
    }
}

ServerDemo.java

import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ServerDemo {
    public static void main(String[] args) throws Exception{
        System.out.println("======服务端启动======");
        // 1. 创建接收端对象 , 注册端口
        DatagramSocket socket = new DatagramSocket(8888);
        // 2. 创建数据包对象,接受数据
        byte[] data = new byte[1024 * 64];
        DatagramPacket packet = new DatagramPacket(
                data,
                data.length);
        // 3. 等待接收数据
        while (true) {
            socket.receive(packet);
            String res = new String(data, 0, packet.getLength());
            System.out.println("受到了来自:  " +
                    packet.getAddress() + "对方端口为:" +
                    packet.getPort() + "的消息: " +
                    res);
        }
    }
}

通信方式之广播、组播

UDP的三种通信方式

  • 单播:单台主机与单台主机之间的通信
  • 广播:当前主机与所在网络中的所有主机通信
  • 组播:当前主机与选定的一组主机的通信

广播

UDP如何实现广播?

  • 使用广播地址:255.255.255.255
  • 具体操作
    • 发送端发送的数据包的目的地为广播地址且指定端口,如(255.255.255.255, 9999)
    • 本机所在网段的其他主机的程序只要匹配端口成功就可以收到信息(9999)

ClientDemo.java

DatagramSocket socket = new DatagramSocket();

byte[] data = "测试".getBytes();

DatagramPacket packet = new DatagramPacket(data,
data.length,InetAddress.getByName("255.255.255.255"),9999);

socket.send(packet);

ServerDemo.java

DatagramSocket socket = new DatagramSocket(9999);

byte[] data = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(
                data,
                data.length);
socket.receive(packet);

String res = new String(data, 0,packet.getLength());

System.out.println("受到了来自:  " +packet.getAddress() + "对方端口为:" +packet.getPort() + "的消息: " +res);

组播

UDP如何实现组播?

  • 使用组播地址:224.0.0.0 ~ 239.255.255.255
  • 具体操作:
    • 发送端的数据包的目的是组播IP(例如:224.0.1.1,端口: 9999)
    • 接收端必须绑定该组播IP(224.0.1.1),端口还要对应发送端的目的端口9999
    • DatagramSocket的子类 MulticastSocket可以在接收端绑定组播IP

ClientDemo.java

DatagramSocket socket = new DatagramSocket();
byte[] data = "测试".getBytes();

DatagramPacket packet = new DatagramPacket(data,data.length,InetAddress.getByName("224.0.1.1"),9999);

socket.send(packet);

ServerDemo.java

MulticastSocket socket = new MulticastSocket(9999);   //socket.joinGroup(InetAddress.getByName("224.0.1.1")); JDK 14 开始摒弃

socket.joinGroup(new InetSocketAddress(InetAddress.getByName("224.0.1.1"),9999),NetworkInterface.getByInetAddress(InetAddress.getLocalHost()));

byte[] data = new byte[1024 * 64];

DatagramPacket packet = new DatagramPacket(
                data,
                data.length);

socket.receive(packet);
String res = new String(data, 0,packet.getLength());

System.out.println("受到了来自:  " +packet.getAddress() + "对方端口为:" +packet.getPort() + "的消息: " +res);

TCP通信

在Java中只要使用java.net.Socket类实现通信,其底层则使用了TCP协议

客户端 Socket

  • 创建客户端的Socket对象,请求与服务器的连接

    public Socket(String host, int port)

  • 使用socket对象调用getOutputStream()方法得到字节输出流

  • 使用字节输出流完成数据的发送

  • 释放资源,关闭socket管道

服务端 ServerSocket

构造器

public ServerSocket(int port) 注册服务端端口

方法

public Socket accept() 阻塞等待接收客户端的 Socket通信连接,得到Socket对象

基本原理:

  • 客户端怎么发,服务端就怎么收
  • 若客户端没有消息,服务端会进入阻塞等待
  • Socket一方关闭或出现异常、对方Socket也会失效或出错

TCP案例

一发一收

ClientDemo.java

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;

public class ClientDemo {
    public static void main(String[] args){
        try {
            Socket socket = new Socket("127.0.0.1", 1235);
            OutputStream os  = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            //ps.print("TCP客户端连接邀请"); 发送失败,因为未带换行符
            ps.println("TCP客户端连接邀请");
            ps.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

ServerDemo.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo{
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(1235);

            // 等待接收客户端socket连接请求,建立Socket通信管道
            Socket socket = serverSocket.accept();

            // 从socket通信管道中得到一个字节输入流
            InputStream is = socket.getInputStream();

            // 字节输入流包装成缓冲字符输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            // 按行读取
            String msg;
            if((msg = br.readLine()) != null){
                System.out.println(socket.getRemoteSocketAddress() + "say : " + msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

多发多收

范例:无断开状态的一发一收

ClientDemo.java

Socket socket = new Socket("127.0.0.1", 1235);

OutputStream os  = socket.getOutputStream();

PrintStream ps = new PrintStream(os);

Scanner sc = new Scanner(System.in);

while(true){
    System.out.print("发送 :");
    String msg = sc.nextLine();
    ps.println(msg);
    ps.flush();
}

ServerDemo.java

ServerSocket serverSocket = new ServerSocket(1235);

Socket socket = serverSocket.accept();

InputStream is = socket.getInputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(is));
String msg;

while((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "发送了 : " + msg);
}

当前案例是否可以同时接收多个客户端的消息 ? 不可以

因为服务端只有一个线程,只能与一个客户端进行通信

范例:多线程实现多发多收

ServerReaderThread.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;


public class ServerReaderThread extends Thread{
    private Socket socket;

    public ServerReaderThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            // 字节输入流包装成缓冲字符输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            // 按行读取
            String msg;
            while((msg = br.readLine()) != null){
                System.out.println(socket.getRemoteSocketAddress() + "发送了 : " + msg);
            }
        } catch (IOException e) {
            System.out.println(socket.getRemoteSocketAddress() + "已下线.");
        }
    }
}

ClientDemo.java

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientDemo {
    public static void main(String[] args){
        try {
            Socket socket = new Socket("127.0.0.1", 1235);
            OutputStream os  = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            Scanner sc = new Scanner(System.in);
            //ps.print("TCP客户端连接邀请"); 发送失败,因为未带换行符
            while(true){
                System.out.print("发送 :");
                String msg = sc.nextLine();
                // 发送信息
                ps.println(msg);
                ps.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ServerDemo.java

import com.sun.security.ntlm.Server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo{
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(1235);

            // 等待接收客户端socket连接请求,建立Socket通信管道
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println(socket.getRemoteSocketAddress() + "已上线!");
                new ServerReaderThread(socket).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

总结:如何实现服务端接收多个客户端消息

  • 主线程定义循环负责接收客户端Socket管道连接
  • 每接收到一个Socket通信管道后分配一个独立的线程负责处理

TCP通信 线程池优化

未使用线程池的通信架构存在的问题:

  • 客户端与服务端的线程模型是 N - N 关系
  • 客户端并发越多,系统瘫痪越快

引入线程池处理多个客户端消息

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * @author Uni
 * @create 2021/11/11 17:48
 */
public class ServerReaderRunnableImpl implements Runnable{
    private Socket socket;
    public ServerReaderRunnableImpl(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            // 字节输入流包装成缓冲字符输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            // 按行读取
            String msg;
            while((msg = br.readLine()) != null){
                System.out.println(socket.getRemoteSocketAddress() + "发送了 : " + msg);
            }
        } catch (IOException e) {
            System.out.println(socket.getRemoteSocketAddress() + "已下线.");
        }
    }
}

ServerDemo.java

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;

public class ServerDemo{
    private static ExecutorService pool = new ThreadPoolExecutor(3, 5, 6,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(1235);
            // 等待接收客户端socket连接请求,建立Socket通信管道
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println(socket.getRemoteSocketAddress() + "已上线!");
                Runnable target = new ServerReaderRunnableImpl(socket);
                pool.execute(target);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ClientDemo.java

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientDemo {
    public static void main(String[] args){
        try {
            Socket socket = new Socket("127.0.0.1", 1235);
            OutputStream os  = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            Scanner sc = new Scanner(System.in);
            //ps.print("TCP客户端连接邀请"); 发送失败,因为未带换行符
            while(true){
                System.out.print("发送 :");
                String msg = sc.nextLine();
                // 发送信息
                ps.println(msg);
                ps.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

线程池优势:

  • 服务端可复用线程处理多个客户端,避免系统瘫痪
  • 适合客户端通信时长较短的场景

TCP 通信实战案例

即时通信

指一个客户端的消息发出去,其他客户端可接收到,之前的案例的消息都是发给服务端的

即时通信需要进行端口转发的设计思想:

  • 服务端把在线的Socket管道存储起来(可使用List)
  • 服务端一旦收到一个消息要推送给其他管道(遍历List)

ServerDemo.java

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class ServerDemo {
    public static List<Socket> allOnlineSockets = new ArrayList<>();
    public static void main(String[] args) throws Exception{
        ServerSocket serverSocket = new ServerSocket(9999);

        while(true){
            Socket socket = serverSocket.accept();
            System.out.println(socket.getRemoteSocketAddress() + "上线了");
            allOnlineSockets.add(socket);
            new ServerReaderThread(socket).start();
        }
    }
}

ServerReaderThread.java

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

public class ServerReaderThread extends Thread{
    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        InputStream is = null;
        try {
            is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String line;
            while((line = br.readLine()) != null) {
                System.out.println(socket.getRemoteSocketAddress() + "发送了: " + line);
                // 把消息进行端口转发给全部客户端 socket 管道
                sendMsgToAll(line);
            }
        } catch (Exception e) {
            System.out.println(socket.getRemoteSocketAddress() + " 下线了");
            ServerDemo.allOnlineSockets.remove(socket);
        }
    }
    private void sendMsgToAll(String msg) throws Exception {
        for (Socket allOnlineSocket : ServerDemo.allOnlineSockets) {
            PrintStream ps = new PrintStream(socket.getOutputStream());
            ps.println(msg);
            ps.flush();
        }
    }
}

ClientDemo.java

import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientDemo {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("127.0.0.1", 9999);
        new ClientReaderThread(socket).start();
        OutputStream os = socket.getOutputStream();
        PrintStream ps = new PrintStream(os);
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.print("发送: ");
            String msg = sc.nextLine();
            ps.println(msg);
            ps.flush();
        }
    }
}

ClienReaderThread.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class ClientReaderThread extends Thread{
    private Socket socket;

    ClientReaderThread(Socket socket){this.socket = socket;}
    @Override
    public void run() {
        try{
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String line;
            while((line = br.readLine()) != null){
                System.out.println(" 收到消息:  " + line);
            }
        } catch (IOException e) {
            System.out.println("服务端强制将你中断.");
        }
        super.run();
    }
}

模拟BS系统

image

import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class BSserverDemo {
    public static void main(String[] args) {
        try{
            ServerSocket ss = new ServerSocket(8080);
            while(true){
                Socket socket = ss.accept();
                new ServerReaderThread(socket).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class ServerReaderThread extends Thread{
    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try{
            PrintStream ps = new PrintStream(socket.getOutputStream());
            ps.println("HTTP/1.1  200  OK");      // 协议类型和版本, 响应成功信息
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println();
            ps.println("<h1 style='color:red'> 《测试》</h1>");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用线程池实现

ServerReaderThread.java

public class ServerReaderRunnable implements Runnable{
    private Socket socket;
    public ServerReaderRunnable(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try{
            PrintStream ps = new PrintStream(socket.getOutputStream());
            ps.println("HTTP/1.1  200  OK");      // 协议类型和版本, 响应成功信息
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println();
            ps.println("<h1 style='color:red'> 《测试》</h1>");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

BSserverDemo.java

public class BSserverDemo {
    private static ExecutorService pool = new ThreadPoolExecutor(3, 5, 60,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {
        try{
            ServerSocket ss = new ServerSocket(8080);
            while(true){
                Socket socket = ss.accept();
                pool.execute(new ServerReaderRunnable(socket));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
posted @ 2021-11-12 00:12  Unirithe  阅读(52)  评论(0)    收藏  举报