【Java从入门到大牛】网络编程

🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Java从入门到大牛
🌠 首发时间:2023年11月23日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾

目录

  • 网络编程
    • 基本的通信架构
  • 网络通信三要素
    • IP地址
    • 端口号
    • 协议
  • UDP通信-快速入门
  • UDP通信-多发多收
  • TCP通信-快速入门
  • TCP通信-多发多收
  • TCP通信-支持与多个客户端同时通信
  • TCP通信-综合案例
    • 即时通信-群聊
    • 实现一个简易版的BS架构
    • 改进

网络编程

什么是网络编程?

可以让设备中的程序与网络上的其他设备中的程序进行数据交互,也就是实现网络通信

Java的 java.net.* 包下提供了网络编程的解决方案

基本的通信架构

基本的通信架构有两种形式:CS架构(Client客户端/Server服务端)、BS架构(Brower浏览器/Server服务端)

在这里插入图片描述
在这里插入图片描述

无论是CS架构,还是BS架构的软件都必须依赖网络编程

网络通信三要素

在这里插入图片描述

IP地址

  • IP(Internet Protocol):全称 “互联网协议地址”, 是分配给上网设备的唯一标志
  • IP地址有两种形式:IPv4、IPv6

IPv4
在这里插入图片描述

IPv6

  • IPv6:共128位,号称可以为地球每一粒沙子编号
  • IPv6分成8段表示,每段每四位编码成一个十六进制位表示,数之间用冒号分开

在这里插入图片描述

IP域名

由于IP地址比较难记住,所以有了IP域名,例如 www.baidu.com 等等

公网IP,内网IP

  • 公网IP,是可以连接互联网的IP地址;内网IP,也叫局域网IP,只能组织机构内部使用
  • 192.168. 开头的就是常见的局域网地址,范围即为 192.168.0.0-192.168.255.255,专门为组织机构内部使用

特殊IP地址

  • 127.0.0.1、localhost:代表本机IP,只会寻找当前所在的主机

IP常用命令

  • ipconfig:查看本机IP地址
  • ping IP地址:检查网络是否连通

InetAddress

  • 代表IP地址

InetAddress的常用方法如下
在这里插入图片描述

应用

import java.net.InetAddress;

/**
 * 目标:掌握InetAddress类的使用
 */
public class InetAddressTest {
    public static void main(String[] args) throws Exception {
        // 1、获取本机IP地址对象的
        InetAddress ip1 = InetAddress.getLocalHost();
        System.out.println(ip1.getHostName());
        System.out.println(ip1.getHostAddress());

        // 2、获取指定IP或者域名的IP地址对象。
        InetAddress ip2 = InetAddress.getByName("www.baidu.com");
        System.out.println(ip2.getHostName());
        System.out.println(ip2.getHostAddress());

        // ping www.baidu.com
        System.out.println(ip2.isReachable(6000));
    }
}

端口号

端口

  • 标记正在计算机设备上运行的应用程序的,被规定为一个16位的二进制,范围是0~65535

分类

  • 周知端口:0~1023,被预先定义的知名应用占用,如HTTP占用80,FTP占用21
  • 注册端口:1024~49151,分配给用户进程或某些应用程序
  • 动态端口:49152~65535,之所以被称为动态端口,是因为它一般不固定分配某种进程,而是动态分配

注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则会出错

协议

通信协议

  • 网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议

开放式网络互联标准:OSI网络参考模型

  • OSI网络参考模型:全球网络互联标准
  • TCP/IP网络模型:事实上的国际标准

在这里插入图片描述

传输层的两个通信协议

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

UDP协议

  • 特点:无连接、不可靠通信,通信效率高,通常用于语音通话、视频直播等
  • 不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内)等
  • 发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的

TCP协议

  • 特点:面向连接、可靠通信,通信效率相对不高,通常用于网页、文件下载、支付等
  • TCP的最终目的:要保证在不可靠的信道上实现可靠的传输
  • TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接

TCP协议:三次握手建立可靠连接

  • 可靠连接:确定通信双方,收发消息都是正常无问题的!(全双工)

在这里插入图片描述

TCP协议:四次挥手断开连接

  • 目的:确保双方数据的收发都已经完成
    在这里插入图片描述

UDP通信-快速入门

Java提供了一个 java.net.DatagramSocket 类来实现UDP通信

DatagramSocket: 用于创建客户端、服务端

在这里插入图片描述

DatagramPacket:创建数据包
在这里插入图片描述

实战:使用UDP通信实现发送消息、接收消息

Client.java

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

/**
 * 目标:完成UDP通信快速入门:实现1发1收。
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建客户端对象
        DatagramSocket socket = new DatagramSocket();

        // 2、创建数据包对象封装要发出去的数据
        byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length
                , InetAddress.getLocalHost(),  6666);

        // 3、开始正式发送这个数据包的数据出去了
        socket.send(packet);

        System.out.println("客户端数据发送完毕~~~");
        socket.close(); // 释放资源!
    }
}

Server.java

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * 目标:完成UDP通信快速入门-服务端开发
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("----服务端启动----");
        // 1、创建一个服务端对象,注册端口
        DatagramSocket socket = new DatagramSocket(6666);

        // 2、创建一个数据包对象,用于接收数据的
        byte[] buffer = new byte[1024 * 64]; // 64KB.
        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);

        System.out.println(packet.getAddress().getHostAddress());
        System.out.println(packet.getPort());

        socket.close(); // 释放资源
    }
}

先启动服务端,再启动客户端

在这里插入图片描述

UDP通信-多发多收

客户端可以反复发送数据

  • 创建DatagramSocket对象(发送端对象)
  • 使用while死循环不断的接收用户的数据输入,如果用户输入的exit则退出程序
  • 如果用户输入的不是exit, 把数据封装成DatagramPacket
  • 使用DatagramSocket对象的send方法将数据包对象进行发送
  • 释放资源

接收端可以反复接收数据

  • 创建DatagramSocket对象并指定端口(接收端对象)
  • 创建DatagramPacket对象接收数据(数据包对象)
  • 使用DatagramSocket对象的receive方法传入DatagramPacket对象
  • 使用while死循环不断的进行第3步

Client.java

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

/**
 * 目标:完成UDP通信快速入门:实现客户端反复的发
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建客户端对象
        DatagramSocket socket = new DatagramSocket();

        // 2、创建数据包对象封装要发出去的数据
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦发现用户输入的exit命令,就退出客户端
            if("exit".equals(msg)){
                System.out.println("欢迎下次光临!退出成功!");
                socket.close(); // 释放资源
                break; // 跳出死循环
            }

            byte[] bytes = msg.getBytes();
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length
                    , InetAddress.getLocalHost(),  6666);

            // 3、开始正式发送这个数据包的数据出去了
            socket.send(packet);
        }
    }
}

Server.java

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * 目标:完成UDP通信快速入门-服务端反复的收
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("----服务端启动----");
        // 1、创建一个服务端对象
        DatagramSocket socket = new DatagramSocket(6666);

        // 2、创建一个数据包对象,用于接收数据的
        byte[] buffer = new byte[1024 * 64]; // 64KB.
        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(rs);

            System.out.println(packet.getAddress().getHostAddress());
            System.out.println(packet.getPort());
            System.out.println("--------------------------------------");
        }
    }
}

在这里插入图片描述

在这里插入图片描述

在这个基础上,我们还可以实现多个客户端同时向服务端发送消息

首先,我们需要在IDEA里面修改一下客户端这个程序的配置,让它可以同时运行多个客户端,具体步骤如下

1、在IDEA右上角找到这个,点击进入

在这里插入图片描述

2、如图操作,注意是客户端

在这里插入图片描述

3、多次执行 Client.java 这个程序,我们就可以得到多个客户端

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

TCP通信-快速入门

Java提供了一个 java.net.Socket 类来实现TCP通信

在这里插入图片描述

TCP通信之客户端开发

  • 客户端程序就是通过java.net包下的Socket类来实现的
    在这里插入图片描述

客户端发送消息

  • 创建客户端的Socket对象,请求与服务端的连接
  • 使用socket对象调用getOutputStream()方法得到字节输出流
  • 使用字节输出流完成数据的发送
  • 释放资源:关闭socket管道

Client.java

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 *  目标:完成TCP通信快速入门-客户端开发:实现1发1收
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接
        Socket socket = new Socket("127.0.0.1", 8888);

        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream os = socket.getOutputStream();

        // 3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        // 4、开始写数据出去了
        dos.writeUTF("在一起,好吗?");
        dos.close();

        socket.close(); // 释放连接资源
    }
}

TCP通信之服务端程序的开发

  • 服务端是通过java.net包下的ServerSocket类来实现的

在这里插入图片描述

服务端接收消息

  • 创建ServerSocket对象,注册服务端端口
  • 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
  • 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收
  • 释放资源:关闭socket管道

Server.java

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 *  目标:完成TCP通信快速入门-服务端开发:实现1发1收
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

        // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
        Socket socket = serverSocket.accept();

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

        // 4、把原始的字节输入流包装成数据输入流
        DataInputStream dis = new DataInputStream(is);

        // 5、使用数据输入流读取客户端发送过来的消息
        String rs = dis.readUTF();
        System.out.println(rs);
        // 其实我们也可以获取客户端的IP地址
        System.out.println(socket.getRemoteSocketAddress());

        dis.close();
        socket.close();
    }
}

在这里插入图片描述

TCP通信-多发多收

使用TCP通信实现:多发多收消息

  • 客户端使用死循环,让用户不断输入消息
  • 服务端也使用死循环,控制服务端收完消息,继续等待接收下一个消息

Client.java

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

/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。
        Socket socket = new Socket("127.0.0.1", 8888);

        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream os = socket.getOutputStream();

        // 3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦用户输入了exit,就退出客户端程序
            if("exit".equals(msg)){
                System.out.println("欢迎您下次光临!退出成功!");
                dos.close();
                socket.close();
                break;
            }

            // 4、开始写数据出去了
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

Server.java

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 *  目标:完成TCP通信快速入门-服务端开发:实现服务端反复发消息
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

        // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
        Socket socket = serverSocket.accept();

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

        // 4、把原始的字节输入流包装成数据输入流
        DataInputStream dis = new DataInputStream(is);

        while (true) {
            try {
                // 5、使用数据输入流读取客户端发送过来的消息
                String rs = dis.readUTF();
                System.out.println(rs);
            } catch (Exception e) {
                System.out.println(socket.getRemoteSocketAddress() + "离线了!");
                dis.close();
                socket.close();
                break;
            }
        }
    }
}

在这里插入图片描述

在这里插入图片描述

TCP通信-支持与多个客户端同时通信

如果我们像刚才UDP通信那样,同时启动多个客户端,你会发现服务端只能接收到一个客户端发送的消息,那么我们该如何实现服务端与多个客户端同时通信呢

目前我们开发的服务端程序,是不可以与多个客户端同时通信的,因为服务端只有一个主线程,只能处理一个客户端的消息

Client.java 不变

添加一个 ServerReaderThread 线程类,每个线程处理一个客户端

import java.io.DataInputStream;
import java.io.InputStream;
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();
            DataInputStream dis = new DataInputStream(is);

            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                } catch (Exception e) {
                    System.out.println("有人下线了:" + socket.getRemoteSocketAddress());
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Server.java

import java.net.ServerSocket;
import java.net.Socket;

/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

        while (true) {
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();

            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理
            new ServerReaderThread(socket).start();
        }
    }
}

在这里插入图片描述

TCP通信-综合案例

即时通信-群聊

什么是群聊?怎么实现?

  • 是指一个客户端把消息发出去,其他在线的全部客户端都可以收到消息
  • 需要用到端口转发的设计思想
  • 服务端需要把在线的Socket管道存起来,一旦收到一个消息要推送给其他管道

TCP通信-端口转发

在这里插入图片描述

代码实现

改进 Client.java

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

/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。
        Socket socket = new Socket("127.0.0.1", 8888);

        // 改进:创建一个独立的线程,负责随机从socket中接收服务端发送过来的消息
        new ClentReaderThread(socket).start();

        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream os = socket.getOutputStream();
        // 3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦用户输入了exit,就退出客户端程序
            if("exit".equals(msg)){
                System.out.println("欢迎您下次光临!退出成功!");
                dos.close();
                socket.close();
                break;
            }
            // 4、开始写数据出去了
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

添加 ClientReaderThread.java

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.Socket;

// 该线程不停接收服务端转发过来的消息并打印,相当于收到群聊里别人的信息
// 思路和服务端接收消息差不多
public class ClentReaderThread extends Thread{
    private Socket socket;
    public ClentReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                } catch (Exception e) {
                    System.out.println("自己下线了:" + socket.getRemoteSocketAddress());
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

改进 Server.java

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

/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信
 */
public class Server {
    // 改进:添加一个集合保存在线的所有客户端,便于转发消息
    public static List<Socket> onLineSockets = new ArrayList<>();

    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

        while (true) {
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();

            onLineSockets.add(socket);      // 有客户端上线就添加

            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());
            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            new ServerReaderThread(socket).start();
        }
    }
}

改进 ServerReaderThread.java

import java.io.*;
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();
            DataInputStream dis = new DataInputStream(is);
            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                    // 把这个消息分发给全部客户端进行接收。
                    sendMsgToAll(msg);
                } catch (Exception e) {
                    System.out.println("有人下线了:" + socket.getRemoteSocketAddress());

                    Server.onLineSockets.remove(socket);    // 客户端下线就删除

                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

	// 添加消息转发方法
    private void sendMsgToAll(String msg) throws IOException {
        // 发送给全部在线的socket管道接收。
        for (Socket onLineSocket : Server.onLineSockets) {
            OutputStream os = onLineSocket.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

实现一个简易版的BS架构

需求描述

要求从浏览器中访问服务器,并立即让服务器响应一个很简单的网页给浏览器展示

BS架构的基本原理

BS架构的客户端就是浏览器,客户从浏览器访问服务器的方法就是通过网址来访问,一般的格式是 http://服务器IP:服务器端口

在这里插入图片描述

注意:服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不会识别返回的数据,也就是说,服务端返回数据不能还是用以前那种打印语句直接打印

HTTP协议规定:响应给浏览器的数据格式必须满足如下格式

在这里插入图片描述

需求实现

Server.java,和前面的一样

import java.net.ServerSocket;
import java.net.Socket;

/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8080);

        while (true) {
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();

            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            new ServerReaderThread(socket).start();
        }
    }
}

改进 ServerReaderThread.java

import java.io.OutputStream;
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() {
        //  立即响应一个网页内容给浏览器展示。
        try {
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println(); // 必须换行
            ps.println("<div style='color:blue;font-size:120px;text-align:center'>66666666666<div>");

			// 访问完就要关闭
            ps.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

效果

随便打开一个浏览器,输入网址 127.0.0.1:8080 回车即可,其中 127.0.0.1 代表本机IP,8080 是我们在服务端指定的端口

在这里插入图片描述

改进

在上面的程序中,客户端的每次请求都会开一个新的线程,这样做好不好呢?

其实是不好的,当访问人数特别多时,也就是高并发时,就会很容易宕机

很明显,我们都知道要使用线程池来进行优化

在这里插入图片描述

优化后的代码

Server.java

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口
        ServerSocket serverSocket = new ServerSocket(8080);

        // 创建出一个线程池,负责处理通信管道的任务
        ThreadPoolExecutor pool = new ThreadPoolExecutor(16 * 2, 16 * 2, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        while (true) {
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();

            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理
            pool.execute(new ServerReaderRunnable(socket));
        }
    }
}

ServerReaderRunnable.java

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

public class ServerReaderRunnable implements Runnable{
    private Socket socket;
    public ServerReaderRunnable(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        //  立即响应一个网页内容给浏览器展示
        try {
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println(); // 必须换行
            ps.println("<div style='color:red;font-size:120px;text-align:center'>88888<div>");
            ps.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/180915.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis plus 简介

简介 MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window)的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 官网:https://baomidou.com/pages/24112f/ 特性 无侵入&…

Mybatis一级缓存和二级缓存原理剖析与源码详解

Mybatis一级缓存和二级缓存原理剖析与源码详解 在本篇文章中&#xff0c;将结合示例与源码&#xff0c;对MyBatis中的一级缓存和二级缓存进行说明。 MyBatis版本&#xff1a;3.5.2 文章目录 Mybatis一级缓存和二级缓存原理剖析与源码详解⼀级缓存场景一场景二⼀级缓存原理探究…

安卓手机好用的清单软件有哪些?

生活中每个人都有丢三落四的习惯&#xff0c;伴随着生活节奏的加快&#xff0c;人们常忘事的情况会更加频繁的出现&#xff0c;这时候很多人就开始选择手机上记录清单类的软件&#xff0c;安卓手机在手机市场中占有很大的分量&#xff0c;在安卓手机上好用的记录清单的软件有哪…

别熬夜了!人真的会变臭

身为当代社畜&#xff0c;你一定经历过如下瞬间——— 周一早高峰的地铁车厢&#xff0c;拥挤的人群里若有若无地飘荡出一股刺鼻臭味&#xff0c;即使戴着口罩也难以抵挡其穿透性&#xff1b; 深夜还灯火通明的办公室工位上&#xff0c;浑浊的空气裹挟着疲惫的身体&#xff0…

Shell 通配符与正则表达元字符

Author&#xff1a;rab 目录 前言一、通配符1.1 *1.2 ?1.3 []1.4 {} 二、正则表达元字符2.1 *2.2 .2.3 ^2.4 $2.5 []2.6 \2.7 \<\>2.8 \{\} 总结 前言 不管是学任何语言&#xff0c;几乎都会涉及到通配符与正则的使用。有时候对于 Linux 初学者来说&#xff0c;往往会将…

Rust使用iced构建UI时,如何在界面显示中文字符

注&#xff1a;此文适合于对rust有一些了解的朋友 iced是一个跨平台的GUI库&#xff0c;用于为rust语言程序构建UI界面。 iced的基本逻辑是&#xff1a; UI交互产生消息message&#xff0c;message传递给后台的update&#xff0c;在这个函数中编写逻辑&#xff0c;然后通过…

轻松记录收支明细,一键打印,财务无忧!

作为现代人&#xff0c;管理好个人财务是非常重要的。但是&#xff0c;如何记录收支明细并打印出来呢&#xff1f;今天&#xff0c;我们向您推荐一款财务软件&#xff0c;帮助您轻松解决这个问题。 首先第一步&#xff0c;我们要打开【晨曦记账本】&#xff0c;并登录账号。 第…

Delphi 12 Athens 发布了!

官方安装包 ☞ https://altd.embarcadero.com/download/radstudio/12.0/RADStudio_12_0_4915718.iso 安装辅助工具、控件可以戳这里 &#xff1a;Delphi 12 资源 RAD Stuido 12 Athens &#xff0c;这次更新的细节还是比较多的&#xff0c;但主要还是多端&#xff08;iOS、An…

BMS基础知识:BMS基本功能,铅酸和锂电池工作原理,电池系统的重要概念!

笔者有话说&#xff1a; 作为BMS从业者来讲&#xff0c;目前接触的BMS系统并不是很高大尚&#xff0c;但基础功能都是有的。 关于BMS的基本功能&#xff0c;工作原理&#xff0c;运行逻辑等&#xff0c;在此做一个梳理&#xff0c;讲一些最基础的扫盲知识&#xff0c;可以作为…

微服务 Spring Cloud 9,RPC框架,客户端和服务端如何建立网络连接?

目录 一、客户端和服务端如何建立网络连接&#xff1f;1、HTTP通信2、Socket通信 二、服务端如何处理请求&#xff1f;1、通常来说&#xff0c;有三种处理方式&#xff1a;2、不同的处理方式对应着不同的业务场景&#xff1a; 三、HTTP协议传输流程四、数据该如何序列化和反序列…

浅谈安科瑞智能照明系统在马来西亚国家石油公司项目的应用

摘要&#xff1a;随着社会经济的发展及网络技术、通信技术的提高&#xff0c;人们对照明设计提出了新的要求&#xff0c;它不仅要控制照明光源的发光时间、 亮度&#xff0c;而且与其它系统来配合不同的应用场合做出相应的灯光场景。本文介绍了马亚西亚石油公司智能照明项目的应…

2023.11.22 IDEA Spring Boot 项目热部署

目录 引言 操作步骤 1. 在 pom.xml 中添加热部署框架支持 2. Setting 开启项目自动编译 3. 以后创建的新项目进行同步配置 4. 重复 配置 步骤2 的内容 5. 开启运行中的热部署 引言 Spring Boot 的热部署是一种在项目正在运行的时候修改代码&#xff0c;却不需要重新启动…

无线电通信常见的12种天线

天线&#xff0c;作为无线通信系统的重要组成部分&#xff0c;有着不可替代的作用。 在我们的生活中&#xff0c;有很多类型的通信需求&#xff0c;例如长距离通信、短距离通信、卫星通信、微波通信、手机通信、点对点通信、点对面通信等等。 使用卫星电话进行通信 不同的需求&…

【高级程序设计】django与网页设计

django安装与配置 使用pip进行Django的安装: pip install djangoconda install django验证django安装成功 import django django.__version__ ## django.get_version()新建一个名称为blog的django项目(project), D:\>cd web-project D:\web-project>django-admi…

亚马逊防关联要注意什么?看这一篇,防关联有技巧!

亚马逊账号关联的问题&#xff0c;对于跨境电商来说都不陌生&#xff0c;店铺的安全问题往往和账号关联有关&#xff0c;一旦亚马逊账号被关联就很可能导致我们的店铺被封&#xff0c;对于被亚马逊封店的卖家都会有申诉机会&#xff0c;如果无法成功申诉&#xff0c;那将永久被…

政务大数据与资源平台建设解决方案:PPT全文75页,附下载

关键词&#xff1a;智慧政务解决方案&#xff0c;大数据解决方案&#xff0c;数据中心解决方案&#xff0c;数据治理解决方案 一、政务大数据与资源平台建设背景 1、政务大数据已成为智慧城市建设的必要基础 为响应国家不断加快5G基建、大数据、人工智能等新型基础设施建设布…

生命周期评估(LCA)与SimaPro碳足迹分析

SimaPro提供最新的科学方法和数据库以及丰富的数据&#xff0c;使您可以收集和评估产品和流程的环境绩效。通过这种方式&#xff0c;您可以将改变公司产品生命周期的想法提交给您的同事&#xff0c;以便阐明您的业务未来。 SimaPro软件的特点和功能&#xff1a; 完全控制产品生…

oracle数据库巡检常见脚本-系列三

简介 作为数据库管理员&#xff08;DBA&#xff09;&#xff0c;定期进行数据库的日常巡检是非常重要的。以下是一些原因&#xff1a; 保证系统的稳定性&#xff1a;通过定期巡检&#xff0c;DBA可以发现并及时解决可能导致系统不稳定的问题&#xff0c;如性能瓶颈、资源利用率…

时间复杂度 空间复杂度 ---java

目录 一. 算法效率 二.时间复杂度 2.1 时间复杂度的概念 2.2 大O的渐进表示法 2.3常见时间复杂度计算举例 三. 空间复杂度 一. 算法效率 算法效率分析分为两种&#xff1a;第一种是时间效率&#xff0c;第二种是空间效率 。 时间效率被称为时间复杂度&#xff0c;而空间…

基于 Flink CDC 打造企业级实时数据集成方案

本文整理自Flink数据通道的Flink负责人、Flink CDC开源社区的负责人、Apache Flink社区的PMC成员徐榜江在云栖大会开源大数据专场的分享。本篇内容主要分为四部分&#xff1a; CDC 数据实时集成的挑战Flink CDC 核心技术解读基于 Flink CDC 的企业级实时数据集成方案实时数据集…