线程池、网络编程——day21

2021/7/3 9:51:24

本文主要是介绍线程池、网络编程——day21,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

在jdk1.5之后引入了了线程池的概念

生产线程池的工厂类

/*
    线程池:JDK1.5之后提供的
    java.util.concurrent.Executors:线程池的工厂类,用来生成线程池
    Executors类中的静态方法:
        static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池
        参数:
            int nThreads:创建线程池中包含的线程数量
        返回值:
            ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程)
    java.util.concurrent.ExecutorService:线程池接口
        用来从线程池中获取线程,调用start方法,执行线程任务
            submit(Runnable task) 提交一个 Runnable 任务用于执行
        关闭/销毁线程池的方法
            void shutdown()
    线程池的使用步骤:
        1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
        2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
        3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
        4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
 */

代码实现

public class Demo01ThreadPool {
    public static void main(String[] args) {
        //1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
        ExecutorService es = Executors.newFixedThreadPool(2);
        //3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
        es.submit(new RunnableImpl());//pool-1-thread-1创建了一个新的线程执行
        //线程池会一直开启,使用完了线程,会自动把线程归还给线程池,线程可以继续使用
        es.submit(new RunnableImpl());//pool-1-thread-1创建了一个新的线程执行
        es.submit(new RunnableImpl());//pool-1-thread-2创建了一个新的线程执行

        //4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
        es.shutdown();

        es.submit(new RunnableImpl());//抛异常,线程池都没有了,就不能获取线程了
    }

}
/*
    2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
 */
public class RunnableImpl implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"创建了一个新的线程执行");
    }
}

网络编程

网络编程前文:

import java.net.InetAddress;
import java.net.UnknownHostException;

//static InetAddress getByName​(String host)
//                          确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址
//String getHostName​()     获取此IP地址的主机名
//String getHostAddress​()  返回文本显示中的IP地址字符串
public class InetadressDemo1 {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress address = InetAddress.getByName("沉迷代码");

        String hostName = address.getHostName();
        System.out.println("主机名为" + hostName);

        String ip = address.getHostAddress();
        System.out.println("IP为" + ip);
    }
}

一个端口号只能被一个应用程序使用

端口号是由两个字节组成 取值在0—65535之间,但是1024之前的端口号不能用,已经被系统分配给已知的网络软件了,网络软件的端口号不能重复

通信概述

UDP编程

Client端代码实现

import java.io.IOException;
import java.net.*;

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        //1.找码头
        DatagramSocket ds = new DatagramSocket();//空参表示会随机绑定一个动态端口把包发送出去
        //2.打包
        //DatagramPacket​(byte[] buf, int length, InetAddress address, int port)
        String s = "这是我要发送的数据";
        //把一个字符串变成字节数组
        byte[] bytes = s.getBytes();
        InetAddress address = InetAddress.getByName("127.0.0.1");
        int port = 10000;
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
        //3.由码头发送包裹
        ds.send(dp);
        //4.关闭资源
        ds.close();
    }
}

Server端代码实现

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class ServerDemo {
    //注意点:
        //1.要先运行接收端,再运行发送端;因为UDP是面向无连接的,
             // 所以发送端直接把数据已经发出来了 在运行接收端就收不到数据了。。
        //2.如果接收端再启动之后,没有接收到数据,那么会死等(阻塞).
        //3.在接收数据的时候,需要调用一个getLength方法,表示接收到了多少字节
    public static void main(String[] args) throws IOException {
        //1.找码头     ---- 表示接收端从10000端口接收数据的.
        DatagramSocket ds = new DatagramSocket(10000);
        //2,创建一个新的箱子
        byte [] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
        //3.接收礼物,把礼物放到新的箱子中
        System.out.println("-----------接收前----------");
        ds.receive(dp);
        System.out.println("------------接收后---------");
        //4.从新的箱子里面获取礼物
       // byte[] data = dp.getData();
        //因为字节数组定义1024会输出很多空格,所以在这里把有效数据转换成字符串
        int length = dp.getLength();
        //把数组变成字符串打印输出
        System.out.println(new String(bytes,0,length));
        //5.关掉资源
        ds.close();
    }
}

UDP通讯练习题

发送端代码实现

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
        DatagramSocket ds = new DatagramSocket();
        while (true) {//选中后ctrl+alt+t即可
            //传输的数据就来自键盘录入
            String s = sc.nextLine();
            if("886".equals(s)){
                break;
            }
            //要把字符串转换为字节数组才能传输
            byte[] bytes = s.getBytes();
            //告知要把数据发送到哪台电脑上
            InetAddress address = InetAddress.getByName("127.0.0.1");
            int port = 10000;
            DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);

            ds.send(dp);
        }

        ds.close();
    }
}

接收端代码实现

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds = new DatagramSocket(10000);

        while (true) {
            byte [] bytes = new byte[1024];
            DatagramPacket dp = new DatagramPacket(bytes,bytes.length);

            ds.receive(dp);

            //把数据放到数组中
            byte[] data = dp.getData();
            //然后设置有效字符 把字符转换成字符串输出
            int length = dp.getLength();
            System.out.println(new String(data,0,length));
        }

       // ds.close();
    }
}

UDP拓展——实现聊天功能

实现客户机和服务器之间互动,一发一回;这种缺陷是只能一发一回,因为接收端的receive方法是个阻塞的,它是需要等客户端发过来消息才进行下一步

先做一个工具类,代码实现

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

/**
 * @Author: kerwin
 * @Description:
 */
public class NetworkUtils {
    // 获取发送端数据报包的方法
    public static DatagramPacket getSendPacket(String msg, String ip, int port) throws UnknownHostException {
        // 把需要发送的数据转化为字节数组
        byte[] bytes = msg.getBytes();
        InetAddress targetIp = InetAddress.getByName(ip);
        // 创建用于发送的数据报包
        DatagramPacket sendPacket =
                new DatagramPacket(bytes, 0, bytes.length, targetIp, port);
        return sendPacket;
    }

    // 获取用于接收的数据报包的方法
    public static DatagramPacket getReceivePacket() {
        byte[] bytes = new byte[1024];
        DatagramPacket receivePacket = new DatagramPacket(bytes, 0, bytes.length);
        return receivePacket;
    }

    // 解析数据报包的方法
    public static String parsePacket(DatagramPacket packet){
        byte[] data = packet.getData();
        int offset = packet.getOffset();
        int length = packet.getLength();
        String s = new String(data, offset, length);
        return s;
    }

}

发送端代码实现

import com.kerwin.lianxi.util.NetworkUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;

/**
 * @Author: kerwin
 * @Description:
 */
public class Sender {
    public static void main(String[] args) throws IOException {
        // 创建用于发送的socket对象
        DatagramSocket datagramSocket = new DatagramSocket(8888);
        // 键盘接收数据
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String msg;
        while ((msg = br.readLine()) != null) {

            // 把键盘输入的数据封装到数据报包里面
            DatagramPacket sendPacket = NetworkUtils.getSendPacket(msg, "127.0.0.1", 9999);
            // send方法发送出去
            datagramSocket.send(sendPacket);

            // 以上是发送的接收
            // 下面是接收数据
            // 获取一个用于接收的数据报包
            DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
            // receive方法接收
            // receive方法是一个阻塞方法
            datagramSocket.receive(receivePacket);
            // 解析数据报包
            String s = NetworkUtils.parsePacket(receivePacket);
            // 获取数据 报包的ip地址
            SocketAddress socketAddress = receivePacket.getSocketAddress();
            System.out.println("接收到了来自"+socketAddress+"的消息: " + s);
        }
    }
}

接收端代码实现

import com.kerwin.lianxi.util.NetworkUtils;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.util.Scanner;

/**
 * @Author: kerwin
 * @Description:
 */
public class Receiver {
    public static void main(String[] args) throws IOException {
        // 创建用于接收的socket对象
        DatagramSocket datagramSocket = new DatagramSocket(9999);
        // 接盘接收数据
        Scanner scanner = new Scanner(System.in);
        while (true) {
            // 创建用于接收的数据报包
            DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
            // receive
            datagramSocket.receive(receivePacket);
            // 解析数据报包
            String msg = NetworkUtils.parsePacket(receivePacket);
            SocketAddress socketAddress = receivePacket.getSocketAddress();
            System.out.println("Receiver接收到了来自"+socketAddress+"的消息: " + msg);
            // 下面是给Sender发送消息
            // 创建用于发送的数据报包 数据来源就是键盘输入
            String s = scanner.nextLine();
            DatagramPacket sendPacket = NetworkUtils.getSendPacket(s, "127.0.0.1", 8888);
            // send方法发送出去
            datagramSocket.send(sendPacket);
        }

    }
}

UDP拓展——把上面的案例优化

 实现可以重复发送消息,这里使用多线程的方式,一个线程是发送,一个线程接收;还需要两个类,一个人和另一个人

两个类代码实现

import java.io.IOException;
import java.net.DatagramSocket;

/**
 * @Author: kerwin
 * @Description:
 */
public class OnePerson {
    public static void main(String[] args) throws IOException {
        // 创建UDP的socket对象
        DatagramSocket datagramSocket = new DatagramSocket(8888);
        // 创建线程并启动
        new Thread(new SendTask(datagramSocket,"127.0.0.1",9999)).start();
        new Thread(new ReceiveTask(datagramSocket)).start();
    }
}
import java.io.IOException;
import java.net.DatagramSocket;

/**
 * @Author: kerwin
 * @Description:
 */
public class AnotherPerson {
    public static void main(String[] args) throws IOException {
        // 创建UDP的socket对象
        DatagramSocket datagramSocket = new DatagramSocket(9999);
        // 创建线程并启动
        new Thread(new SendTask(datagramSocket,"127.0.0.1",8888)).start();
        new Thread(new ReceiveTask(datagramSocket)).start();
    }
}

 两个线程代码实现

import com.kerwin.lianxi.util.NetworkUtils;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.UnknownHostException;
import java.util.Scanner;

/**
 * @Author: kerwin
 * @Description:
 */
public class SendTask implements Runnable{
    // 定义成员变量
    DatagramSocket datagramSocket;
    // 目标ip  port
    String ip;
    int port;

    public SendTask(DatagramSocket datagramSocket, String ip, int port) {
        this.datagramSocket = datagramSocket;
        this.ip = ip;
        this.port = port;
    }

    @Override
    public void run() {
        // 只干一件事情  发送消息
        Scanner scanner = new Scanner(System.in);
        while (true) {
            // 从键盘接收数据
            String sendMsg = scanner.nextLine();
            // 创建用于发送的数据报包
            try {
                DatagramPacket sendPacket = NetworkUtils.getSendPacket(sendMsg, ip, port);
                // 发送出去
                datagramSocket.send(sendPacket);
            } catch (UnknownHostException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
import com.kerwin.lianxi.util.NetworkUtils;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * @Author: kerwin
 * @Description:
 */
public class ReceiveTask implements Runnable{
    // 成员变量
    DatagramSocket datagramSocket;

    public ReceiveTask(DatagramSocket datagramSocket) {
        this.datagramSocket = datagramSocket;
    }

    @Override
    public void run() {
        // 只干一件事情 接收数据 并打印
        while (true) {
            // 创建接收的数据报包
            DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
            // receive方法接收数据
            try {
                datagramSocket.receive(receivePacket);
                // parse数据报包
                String receiveMsg = NetworkUtils.parsePacket(receivePacket);
                System.out.println(receiveMsg);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

TCP编程

客户端代码实现

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

/*
    TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回写的数据
    表示客户端的类:
        java.net.Socket:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
        套接字:包含了IP地址和端口号的网络单位

    构造方法:
        Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
        参数:
            String host:服务器主机的名称/服务器的IP地址
            int port:服务器的端口号

    成员方法:
        OutputStream getOutputStream() 返回此套接字的输出流。
        InputStream getInputStream() 返回此套接字的输入流。
        void close() 关闭此套接字。

    实现步骤:
        1.创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
        2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
        3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
        4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        5.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
        6.释放资源(Socket)
     注意:
        1.客户端和服务器端进行交互,必须使用Socket中提供的网络流,不能使用自己创建的流对象
        2.当我们创建客户端对象Socket的时候,就会去请求服务器和服务器经过3次握手建立连接通路
            这时如果服务器没有启动,那么就会抛出异常ConnectException: Connection refused: connect
            如果服务器已经启动,那么就可以进行交互了
 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1.创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
        Socket socket = new Socket("127.0.0.1",8888);
        //2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
        os.write("你好服务器".getBytes());

        //4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();

        //5.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));

        //6.释放资源(Socket)
        socket.close();

    }

}

服务器代码实现

未完持续...



这篇关于线程池、网络编程——day21的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程