黑马全套Java教程(九):网络编程(四)

简介: 黑马全套Java教程(九):网络编程

黑马全套Java教程(九):网络编程(三)+https://developer.aliyun.com/article/1556507

37.4 线程池优化

ServerDemo2.java

package d8_socket4;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
//目标:实现服务端可以同时处理多个客户端的消息
public class ServerDemo2 {
    //使用静态变量记住一个线程池对象
    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 {
            System.out.println("=====服务端启动成功=====");
            //1. 注册端口
            ServerSocket serverSocket = new ServerSocket(6666);
            //定义一个死循环由主线程负责不断的接收客户端的Socket管道连接
            while (true) {
                //2. 每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息
                Socket socket = serverSocket.accept();
                System.out.println(socket.getRemoteSocketAddress() + ":它来了,上线了!");
                //任务对象负责读取消息
                Runnable target = new ServerReaderRunnable(socket);
                pool.execute(target);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ClientDemo1.java

package d8_socket4;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
//目标:使用线程池优化,实现通信
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) {
                //4. 发送消息
                System.out.println("请说:");
                String msg = sc.nextLine();
                ps.println(msg);
                ps.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ServerReaderRunnable.java

package d8_socket4;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
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 (IOException e) {
            System.out.println(socket.getRemoteSocketAddress() + ":下线了!");
        }
    }
}


案例:即时通信

即时通信的含义,要怎么设计:

  • 即时通信,是指一个客户端的消息发出去,其他客户端可以接收到
  • 即时通信需要进行端口转发的设计思想
  • 服务端需要把在线的Socket管道存储起来
  • 一旦收到一个消息要推送给其他管道

ServerReaderThread.java

package d9_tcp_sms;
import d8_socket4.ServerReaderRunnable;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
//目标:实现服务端可以同时处理多个客户端的消息
public class ServerDemo2 {
    //定义一个静态的List集合存储当前全部在线的socket管道
    public static List<Socket> allOnlineSockets = new ArrayList<>();
    public static void main(String[] args) throws Exception{
        System.out.println("=====服务端启动成功=====");
        //1. 注册端口
        ServerSocket serverSocket = new ServerSocket(6666);
        //定义一个死循环由主线程负责不断的接收客户端的Socket管道连接
        while (true) {
            //2. 每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息
            Socket socket = serverSocket.accept();
            System.out.println(socket.getRemoteSocketAddress() + ":上线了!");
            //任务对象负责读取消息
            Runnable target = new ServerReaderRunnable(socket);
            allOnlineSockets.add(socket);   //上线完成
            //3. 创建一个独立的线程来单独处理这个socket管道
            new ServerReaderThread(socket).start();
        }
    }
}
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);
                //把这个消息进行端口转发给全部客户端socket管道
                sendMsgToAll(msg);
            }
        } catch (Exception e) {
            System.out.println(socket.getRemoteSocketAddress() + ":下线了!");
            ServerDemo2.allOnlineSockets.remove(socket);
        }
    }
    private void sendMsgToAll(String msg) throws Exception{
        for(Socket socket : ServerDemo2.allOnlineSockets){
            PrintStream ps = new PrintStream(socket.getOutputStream());
            ps.println(msg);
            ps.flush();
        }
    }
}

ClientDemo1.java

package d9_tcp_sms;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class ClientDemo1 {
    public static void main(String[] args) throws Exception {
        System.out.println("=====客户端启动成功=====");
        //1. 创建Socket通信管道请求有服务端的连接
        Socket socket = new Socket("127.0.0.1", 6666);
        //创建一个独立的线程专门负责这个客户端的读消息(服务端随时可能转发消息过来)
        new ClientReaderThread(socket).start();
        //2. 从socket通信管道中得到一个字节输出流,负责发送数据
        OutputStream os = socket.getOutputStream();
        //3. 把低级的字节流包装成打印流
        PrintStream ps = new PrintStream(os);
        //4. 发送消息
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();
            ps.println(msg);
            ps.flush();
        }
    }
}
class ClientReaderThread extends Thread {
    private Socket socket;
    public ClientReaderThread(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("收到消息:" + msg);
            }
        } catch (Exception e) {
            System.out.println(socket.getRemoteSocketAddress() + ":服务端把你踢出去了!");
        }
    }
}


案例:模拟BS系统

package d10_bs;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
//目标:实现服务端可以同时处理多个客户端的消息
public class BSserverDemo {
    public static void main(String[] args) throws Exception{
        //1. 注册端口
        ServerSocket serverSocket = new ServerSocket(8080);
        //2. 创建一个循环接收多个客户端的请求
        while (true) {
            Socket socket = serverSocket.accept();
            //3. 创建一个独立的线程来单独处理这个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管道
            //响应消息给浏览器显示
            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'>《Java大法好》 </span>");
            ps.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

例2:利用线程池优化

package d10_bs;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
//目标:实现服务端可以同时处理多个客户端的消息
public class BSserverDemo {
    //使用静态变量记住一个线程池对象
    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) throws Exception{
        //1. 注册端口
        ServerSocket serverSocket = new ServerSocket(8080);
        //2. 创建一个循环接收多个客户端的请求
        while (true) {
            Socket socket = serverSocket.accept();
            //3. 创建一个独立的线程来单独处理这个socket管道
            pool.execute(new ServerReaderThread(socket));
        }
    }
}
class ServerReaderThread extends Thread {
    private Socket socket;
    public ServerReaderThread(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            //浏览器  已经与本线程建立了Socket管道
            //响应消息给浏览器显示
            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'>《Java大法好》 </span>");
            ps.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
目录
相关文章
|
1天前
|
XML 测试技术 数据格式
《手把手教你》系列基础篇(八十五)-java+ selenium自动化测试-框架设计基础-TestNG自定义日志-下篇(详解教程)
【7月更文挑战第3天】TestNG教程展示了如何自定义日志记录。首先创建一个名为`TestLog`的测试类,包含3个测试方法,其中一个故意失败以展示日志。使用`Assert.assertTrue`和`Reporter.log`来记录信息。接着创建`CustomReporter`类,继承`TestListenerAdapter`,覆盖`onTestFailure`, `onTestSkipped`, 和 `onTestSuccess`,在这些方法中自定义日志输出。
17 6
|
17小时前
|
Java 测试技术 Apache
《手把手教你》系列基础篇(八十六)-java+ selenium自动化测试-框架设计基础-Log4j实现日志输出(详解教程)
【7月更文挑战第4天】Apache Log4j 是一个广泛使用的 Java 日志框架,它允许开发者控制日志信息的输出目的地、格式和级别。Log4j 包含三个主要组件:Loggers(记录器)负责生成日志信息,Appenders(输出源)确定日志输出的位置(如控制台、文件、数据库等),而 Layouts(布局)则控制日志信息的格式。通过配置 Log4j,可以灵活地定制日志记录行为。
13 4
|
1天前
|
网络协议 安全 Java
Java中的网络编程:Socket编程详解
Java中的网络编程:Socket编程详解
|
1天前
|
安全 网络协议 Java
Java中的网络通信:HTTP详解
Java中的网络通信:HTTP详解
|
1天前
|
网络协议 Java 网络安全
Java中的网络编程:TCP详解
Java中的网络编程:TCP详解
|
17小时前
|
网络协议 安全 Java
深入了解Java中的网络编程与Socket通信
深入了解Java中的网络编程与Socket通信
|
1天前
|
Linux 网络安全 数据安全/隐私保护
网络安全教程-------渗透工具Kali,官网链接,ARM的介绍,Mobil,华为小米,oppe手机,是无法刷入第三方的操作系统的,E+手机,谷歌的picksoul,或者三星手机,系统盘是WSL的
网络安全教程-------渗透工具Kali,官网链接,ARM的介绍,Mobil,华为小米,oppe手机,是无法刷入第三方的操作系统的,E+手机,谷歌的picksoul,或者三星手机,系统盘是WSL的
|
2天前
|
Java 测试技术 Android开发
《手把手教你》系列基础篇(八十四)-java+ selenium自动化测试-框架设计基础-TestNG日志-上篇(详解教程
【7月更文挑战第2天】TestNG是一个用于自动化测试的Java框架,提供日志记录功能。日志有两种模式:底层级详细记录每个步骤,高层级仅记录关键事件。示例代码展示了如何在测试方法中使用`Reporter.log()`记录信息,这些信息会显示在TestNG HTML报告中。文章还提及了日志显示时可能出现的编码问题及解决办法。
|
2天前
|
网络协议 Java
Java网络编程基础与Socket实现技术
Java网络编程基础与Socket实现技术
|
Java 数据库 容器

热门文章

最新文章