黑马全套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();
        }
    }
}
目录
相关文章
|
22天前
|
Java
【思维导图】JAVA网络编程思维升级:URL与URLConnection的逻辑梳理,助你一臂之力!
【思维导图】JAVA网络编程思维升级:URL与URLConnection的逻辑梳理,助你一臂之力!
33 1
|
22天前
|
XML JSON 搜索推荐
【高手过招】JAVA网络编程对决:URL与URLConnection的高级玩法,你敢挑战吗?
【高手过招】JAVA网络编程对决:URL与URLConnection的高级玩法,你敢挑战吗?
43 0
|
2天前
|
Java API
Java时间戳教程
本文详细介绍Java中时间戳的处理方法,包括获取当前时间戳、使用`java.time`包、时间戳与日期的相互转换及格式化等。示例代码展示了如何利用`System.currentTimeMillis()`和`java.time.Instant`获取时间戳,以及如何通过`Date`和`ZonedDateTime`进行日期转换和时区处理。随着Java 8引入的`java.time`包,日期时间操作变得更加强大和便捷,推荐在新项目中优先采用。
|
22天前
|
Java
【实战演练】JAVA网络编程高手养成记:URL与URLConnection的实战技巧,一学就会!
【实战演练】JAVA网络编程高手养成记:URL与URLConnection的实战技巧,一学就会!
29 3
|
22天前
|
安全 Java 网络安全
【认知革命】JAVA网络编程新视角:重新定义URL与URLConnection,让网络资源触手可及!
【认知革命】JAVA网络编程新视角:重新定义URL与URLConnection,让网络资源触手可及!
31 2
|
23天前
|
存储 算法 Java
Java中的集合框架深度解析云上守护:云计算与网络安全的协同进化
【8月更文挑战第29天】在Java的世界中,集合框架是数据结构的代言人。它不仅让数据存储变得优雅而高效,还为程序员提供了一套丰富的工具箱。本文将带你深入理解集合框架的设计哲学,探索其背后的原理,并分享一些实用的使用技巧。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇通往高效编程的大门。
|
21天前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
42 0
|
22天前
|
缓存 Java API
【技术前沿】JAVA网络编程黑科技:URL与URLConnection的创新应用,带你飞越极限!
【技术前沿】JAVA网络编程黑科技:URL与URLConnection的创新应用,带你飞越极限!
28 0
|
23天前
|
Java API
Java与Lua互相调用简单教程
【8月更文挑战第29天】在软件开发中,Java以其强大的稳定性和广泛的生态系统著称,而Lua则因其轻量级、灵活和嵌入式的特点在脚本编写、游戏开发等领域大放异彩。将两者结合使用,可以充分利用Java的底层能力和Lua的快速开发优势。本文将通过一个简单的教程,介绍如何在Java程序中嵌入并执行Lua脚本,以及如何在Lua中调用Java方法。
19 0
|
7天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)