当前位置: 首页 > article >正文

Java 基础知识九(网络编程)

UDP

DatagramSocket:通讯的数据管道

-send 和receive方法

-(可选,多网卡)绑定一个IP和Port
DatagramPacket
-集装箱:封装数据
-地址标签:目的地IP+Port

package org.example.net;

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

public class UdpRecv {

    public static void main(String[] args) throws Exception {
        DatagramSocket ds = new DatagramSocket(3000);
        byte[] buf = new byte[1024];
        DatagramPacket dp = new DatagramPacket(buf, 1024);
        System.out.println("UdpRecv:我在等待信息");
        ds.receive(dp);
        System.out.println("UdpRecv:我接收到信息");
        String strRecv = new String(dp.getData(), 0, dp.getLength()) + " from " + dp.getAddress().getHostAddress() + ":" + dp.getPort();
        System.out.println(strRecv);
        Thread.sleep(1000);
        System.out.println("UdpRecv:我要发送信息");
        String str = "hello world 222";
        DatagramPacket dp2 = new DatagramPacket(str.getBytes(), str.length(), InetAddress.getByName("127.0.0.1"), dp.getPort());
        ds.send(dp2);
        System.out.println("UdpRecv:我发送信息结束");
        ds.close();
    }
}
package org.example.net;

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

public class UdpSend {

    public static void main(String[] args) throws Exception {
        DatagramSocket ds = new DatagramSocket();
        String str = "hello world";
        DatagramPacket dp = new DatagramPacket(str.getBytes(), str.length(), InetAddress.getByName("127.0.0.1"), 3000);
        System.out.println("UdpSend:我要发送信息");
        ds.send(dp);
        System.out.println("UdpSend:我发送信息结束");


        Thread.sleep(1000);
        byte[] buf = new byte[1024];
        DatagramPacket dp2 = new DatagramPacket(buf, 1024);
        System.out.println("UdpSend:我在等待信息");
        ds.receive(dp2);
        System.out.println("UdpSend:我接收到信息");
        String str2 = new String(dp2.getData(), 0, dp2.getLength()) + " from " + dp2.getAddress().getHostAddress() + ":" + dp2.getPort();
        System.out.println(str2);
        ds.close();
    }
}
TCP

TCP协议:有链接、保证可靠的无误差通讯

-1)服务器:创建一个ServerSocket,等待连接
-2)客户机:创建一个Socket,连接到服务器-

3)服务器:ServerSocket接收到连接,创建一个Socket和客户的Socket建立专线连接,后续服务器和客户机的对话(这一对Sock会在一个单独的线程(服务器端)上运行

4)服务器的ServerSocket继续等待连接,返回1

ServerSocket: 服务器码头
需要绑定port
如果有多块网卡,需要绑定一个IP地址
Socket: 运输通道
-客户端需要绑定服务器的地址和Port
-客户端往Socket输入流写入数据,送到服务端-客户端从Socket输出流取服务器端过来的数据
-服务端反之亦然

服务端等待响应时,处于阻塞状态
服务端可以同时响应多个客户端
服务端每接受一个客户端,就启动一个独立的线程与之对应
客户端或者服务端都可以选择关闭这对Socket的通道
实例
-服务端先启动,且一直保留
-客户端后启动,可以先退出

package org.example.net;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

public class TcpClient {

    public static void main(String[] args) {
        try {
            Socket s = new Socket(InetAddress.getByName("127.0.0.1"),8001);//需要服务端先开启
            //同一个通道,服务端的输出流就是客户端的输入流;服务端的输入流就是客户端的输出流
            // 开启通道的输入流
            InputStream ips =s.getInputStream();
            BufferedReader brNet = new BufferedReader(new InputStreamReader(ips));
            OutputStream ops = s.getOutputStream();//开启通道的输出流
            DataOutputStream dos = new DataOutputStream(ops);
            BufferedReader brKey = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                String strWord = brKey.readLine();
                if (strWord.equalsIgnoreCase("quit")) {
                    break;
                }else{
                    System.out.println("I want to send:" + strWord);
                    dos.writeBytes(strWord + System.getProperty("line.separator") );
                    System.out.println("Server said :" + brNet.readLine());
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
package org.example.net;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer {
    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(8001);//驻守在8001端口
            Socket s = ss.accept();//阻寒,等到有客户端连接上来
            System.out.println("welcome to the java world");
            InputStream ips = s.getInputStream();//有人连上来,打开输入流
            OutputStream ops = s.getOutputStream();//打开输出流
            // 同一个通道,服务端的输出流就是客户端的输入流;服务端的输入流就是客户端的输出流
            ops.write("Hello,client!".getBytes()); //输出一句话给客户端
            BufferedReader br = new BufferedReader(new InputStreamReader(ips));
            //从客户端读取一句话
            System.out.println("client said:" + br.readLine());
            ips.close();
            ops.close();
            s.close();
            ss.close();

        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

server2 处理多客户端

package org.example.net;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer2 {
    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(8001);//驻守在8001端口
            while (true){

                Socket s = ss.accept();//阻寒,等到有客户端连接上来
                System.out.println("a new client coming");
                new Thread(new Worker(s)).start();
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
package org.example.net;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Worker implements Runnable {

    Socket s;

    public Worker(Socket s) {
        this.s = s;
    }

    @Override
    public void run() {
        try {
            System.out.println("server has started");
            InputStream ips = s.getInputStream();//有人连上来,打开输入流
            OutputStream ops = s.getOutputStream();//打开输出流

            BufferedReader br = new BufferedReader(new InputStreamReader(ips));
            DataOutputStream dos = new DataOutputStream(ops);
            while (true) {
                String strWord = br.readLine();
                System.out.println("client said:" + strWord + ":" + strWord.length());
                if (strWord.equalsIgnoreCase("quit")) {
                    break;
                }
                String strEcho = strWord + " echo";
                // dos.writeBytes(strWord + System.getProperty("line.separator"));
                System.out.println("Server said :" + strWord + "---->" + strEcho);
                dos.writeBytes(strWord + "---->" + strEcho + System.getProperty("line.separator"));
            }
            br.close();
            //关闭包装类,会自动关闭包装类中所包装过的底层类。所以不用调用ips.close()
            dos.close();
            s.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
HTTP

Java HTTP编程(java.net包)

-支持模拟成浏览器的方式去访问网页

-URL,Uniform Resource Locator,代表一个资源http://www.ecnu.edu.cn/index.html?a=1&b=2&c=3

-URLConnection
获取资源的连接器
根据URL的openConnection(方法获得URLConnection

connect方法,建立和资源的联系通道

getInputStream方法,获取资源的内容

package org.example.net;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;

public class URLConnectionGetTest {
    public static void main(String[] args) {
        try {
            String urlName = "https://www.baidu.com";
            URL url = new URL(urlName);
            URLConnection connection = url.openConnection();
            connection.connect();
            //打印http的头部信息
            Map<String, List<String>> headers = connection.getHeaderFields();
            for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
                String key = entry.getKey();
                for (String value : entry.getValue()) {
                    System.out.println(key + ":" + value);
                }
            }
            //输出将要收到的内容属性信息
            System.out.println("-----------------------");
            System.out.println("getcontentType:"+connection.getContentType());
            System.out.println("getcontentLength:"+ connection.getContentLength());
            System.out.println("getcontentEncoding:?"+ connection.getContentEncoding());
            System.out.println("getDate:"+ connection.getDate());
            System.out.println("getExpiration:"+ connection.getExpiration());
            System.out.println("getLastModifed:"+ connection.getLastModified());
            System.out.println("----------");

            BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"));
            //输出收到的内容
            String line ="";
            while ((line= br.readLine())!= null){
                System.out.println(line);
            }


        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
package org.example.net;

import java.io.*;
import java.net.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class URLConnectionPostTest {
    public static void main(String[] args) throws IOException {
        String urlstring = "https://tools.usps.com/go/zipLookupAction.action";
        Object userAgent = "HTTPie/0.9.2";
        Object redirects = "1";
        CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
        Map<String, String> params = new HashMap<String, String>();
        params.put("tAddress", "1 Market street");
        params.put("tcity", "San Francisco");
        params.put("sstate", "CA");
        String result = doPost(new URL(urlstring), params,
                userAgent == null ? null : userAgent.toString(),
                redirects == null ? -1 : Integer.parseInt(redirects.toString()));
        System.out.println(result);
    }


    public static String doPost(URL url, Map<String, String> nameValuePairs, String userAgent, int redirects) throws IOException {
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        if (userAgent != null)
            connection.setRequestProperty("User-Agent", userAgent);
        if (redirects >= 0)
            connection.setInstanceFollowRedirects(false);

        connection.setDoOutput(true);


        //输出请求的参数
        try (PrintWriter out = new PrintWriter(connection.getOutputStream())) {
            boolean first = true;
            for (Map.Entry<String, String> pair : nameValuePairs.entrySet()) {
                //参数必须这样拼接a = 1 & b = 2 & c = 3
                if (first) {
                    first = false;
                } else {
                    out.print('&');
                }
                String name = pair.getKey();
                String value = pair.getValue();
                out.print(name);
                out.print('=');
                out.print(URLEncoder.encode(value, "UTF-8"));
            }
        }
        String encoding = connection.getContentEncoding();
        if (encoding == null)
        {
            encoding = "UTF-8";
        }

        if (redirects > 0) {
            int responseCode = connection.getResponseCode();
            System.out.println("responsecode:" + responseCode);
            if (responseCode == HttpURLConnection.HTTP_MOVED_PERM
                    || responseCode == HttpURLConnection.HTTP_MOVED_TEMP
                    || responseCode == HttpURLConnection.HTTP_SEE_OTHER) {
                String location = connection.getHeaderField("Location");
                if (location != null) {
                    URL base = connection.getURL();
                    connection.disconnect();
                    return doPost(new URL(base, location), nameValuePairs, userAgent, redirects - 1);
                }
            }

        } else if (redirects == 0) {
            throw new IOException("Too many redirects");
        }
        StringBuilder response = new StringBuilder();
        try (Scanner in = new Scanner(connection.getInputStream(),encoding))
        {
            while(in.hasNextLine()){
                response.append(in.nextLine());
                response.append("\n");
            }
        } catch (IOException e) {

            InputStream err = connection.getErrorStream();
            if (err == null) throw e;
            try (Scanner in = new Scanner(err)) {
                response.append(in.nextLine());
                response.append("\n");
            }
        }
        return response.toString();
    }
}

HTTPClient

java.net.http包

取代URLConnection 支持HTTP/1.1和HTTP/2 实现大部分HTTP方法主要类
-HttpClient -HttpRequest- HttpResponse

package org.example.net;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class JDKHttpClientGetTest {
    public static void main(String[] args) throws IOException, InterruptedException {
        doGet();
    }

    public static void doGet() {
        try {
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build();
            HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.body());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Httpcomponents
hc.apache.org,Apache出品从HttpClient进化而来,是一个集成的Java HTIP工具包
-实现所有HTTP方法:get/post/put/delete
-支持自动转向
-支持https协议
-支持代理服务器等

NIO

·Buffer 缓冲区,一个可以读写的内存区域-ByteBuffer, CharBuffer, DoubleBuffer, IntBuffer, LongBufferShortBuffer (StringBuffer 不是Buffer缓冲区)
四个主要属性
capacity 容量,position 读写位置
limit 界限,mark标记,用于重复一个读/写操作

Channel 通道
全双工的,支持读/写(而Stream流是单向的)
-支持异步读写
-和Buffer配合,提高效率
-ServerSocketChannel 服务器TCP Socket 接入通道,接收客户端-SocketChannel TCP Socket通道,可支持阻寒/非阻塞通讯-DatagramChannel UDp 通道
-FileChannel 文件通道

elector多路选择器
-每隔一段时间,不断轮询注册在其上的Channel-如果有一个Channel有接入、读、写操作,就会被轮询出来进行后续IO操作

-根据SelectionKey可以获取相应的Channel,

-避免过多的线程

-SelectionKey四种类型
·OP_CONNECT
·OP_ACCEPT
·OP READ
·OP WRITE

package org.example.NIO;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

public class NioClient {

    public static void main(String[] args) {
        String host = "127.0.0.1";
        int port = 8001;
        Selector selector = null;
        SocketChannel socketchannel = null;
        try {
            selector = Selector.open();
            socketchannel = SocketChannel.open();
            socketchannel.configureBlocking(false);//非阻塞
            //如果直接连接成功,则注册到多路复用器上,发送请求消息,读应答
            if (socketchannel.connect(new InetSocketAddress(host, port))) {
                socketchannel.register(selector, SelectionKey.OP_READ);
                doWrite(socketchannel);
            } else {
                socketchannel.register(selector, SelectionKey.OP_CONNECT);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        while (true) {
            try {
                selector.select(1000);
                Set<SelectionKey> selectedKeys = selector.selectedKeys();

                Iterator<SelectionKey> it = selectedKeys.iterator();
                SelectionKey key = null;
                while (it.hasNext()) {
                    key = it.next();
                    it.remove();
                    try {
                        handleInput(selector, key);
                    } catch (Exception e) {
                        if (key != null) {
                            key.cancel();
                            if (key.channel() != null)
                                key.channel().close();
                        }
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            
        }
    }

    public static void doWrite(SocketChannel sc)throws IOException {
        byte[] str = UUID.randomUUID().toString().getBytes();
        ByteBuffer writeBuffer = ByteBuffer.allocate(str.length);
        writeBuffer.put(str);
        writeBuffer.flip();
        sc.write(writeBuffer);
    }

    public static void handleInput(Selector selector, SelectionKey key) throws IOException, InterruptedException {

        if(key.isValid()) {//判断是否连接成功
            SocketChannel sc = (SocketChannel) key.channel();
            if (key.isConnectable()) {
                if (sc.finishConnect()) {
                    sc.register(selector, SelectionKey.OP_READ);
                }
            }

            if (key.isReadable()) {
                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                int readBytes = sc.read(readBuffer);
                if (readBytes > 0) {
                    readBuffer.flip();
                    byte[] bytes = new byte[readBuffer.remaining()];
                    readBuffer.get(bytes);
                    String body = new String(bytes, "UTF-8");
                    System.out.println("server said :" + body);
                } else if (readBytes < 0) {
                    //对端链路关闭

                    key.cancel();
                    sc.close();
                } else
                    ; // 读到字节,忽略
            }
            Thread.sleep(5000);
            doWrite(sc);
        }
    }
}
package org.example.NIO;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;


public class NioServer {

    public static void main(String[] args) throws IOException {
        int port = 8001;
        Selector selector = null;
        ServerSocketChannel servChannel = null;
        try {
            selector = selector.open();
            servChannel = ServerSocketChannel.open();
            servChannel.configureBlocking(false);
            servChannel.socket().bind(new InetSocketAddress(port), 1024);
            servChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务器在8001端口守候");
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }

        while (true) {
            try {
                selector.select(1000);
                Set<SelectionKey> selectedKeys = selector.selectedKeys();

                Iterator<SelectionKey> it = selectedKeys.iterator();
                SelectionKey key = null;
                while (it.hasNext()) {
                    key = it.next();
                    it.remove();
                    try {
                        handleInput(selector, key);
                    } catch (Exception e) {
                        if (key != null) {
                            key.cancel();
                            if (key.channel() != null)
                                key.channel().close();
                        }
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }

            try {
                Thread.sleep(500);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static void handleInput(Selector selector, SelectionKey key) throws IOException {
        if (key.isValid()) {//判断是否连接成功
            if (key.isAcceptable()) {
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                SocketChannel sc = ssc.accept();
                sc.configureBlocking(false);
                sc.register(selector, SelectionKey.OP_READ);

            }

            if (key.isReadable()) {
                SocketChannel sc = (SocketChannel) key.channel();
                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                int readBytes = sc.read(readBuffer);
                if (readBytes > 0) {
                    readBuffer.flip();
                    byte[] bytes = new byte[readBuffer.remaining()];
                    readBuffer.get(bytes);
                    String request = new String(bytes, "UTF-8");
                    System.out.println("server said :" + request);

                    String response = request + " 666";
                    doWrite(sc,response);
                } else if (readBytes < 0) {
                    //对端链路关闭

                    key.cancel();
                    sc.close();
                } else
                    ; // 读到字节,忽略
            }

        }
    }
    public static void doWrite(SocketChannel channel,String response) throws IOException {
        if (response != null && response.trim().length() > 0) {
            byte[] bytes = response.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            channel.write(writeBuffer);
        }
    }
}




AIO

package org.example.Aio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.UUID;

public class AioClient {
    public static void main(String[] a){
        try {
            AsynchronousSocketChannel channel =  AsynchronousSocketChannel.open();
            channel.connect(new InetSocketAddress("localhost", 8001), null, new CompletionHandler<Void, Void>() {
                public void completed(Void result, Void attachment){
                    String str = UUID.randomUUID().toString();
                    channel.write(ByteBuffer.wrap(str.getBytes()), null, new CompletionHandler<Integer, Object>() {
                        @Override
                        public void completed(Integer result, Object attachment){
                            try {
                                System.out.println("write " +str + " , and wait response");
                                ByteBuffer buffer = ByteBuffer.allocate(1024);
                                channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                                    @Override
                                    public void completed(Integer result_num, ByteBuffer attachment) {
                                        attachment.flip();
                                        CharBuffer charBuffer = CharBuffer.allocate(1024);
                                        CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
                                        decoder.decode(attachment, charBuffer, false);
                                        charBuffer.flip();
                                        String data = new String(charBuffer.array(), 0, charBuffer.limit());
                                        System.out.println("server said: " + data);
                                        try {
                                            channel.close();
                                        }catch (Exception e){
                                            e.printStackTrace();
                                        }

                                    }

                                    @Override
                                    public void failed(Throwable exc, ByteBuffer attachment) {
                                        System.out.println("read error "+ exc.getMessage());
                                    }
                                });
                                channel.close();
                            }catch (Exception e){
                                e.printStackTrace();
                            }
                        }
                        public void failed(Throwable exc, Object attachment){
                            System.out.println("write error ");
                        }
                    });
                }
                public void failed(Throwable exc, Void attachment){
                    System.out.println("faild ");
                }
            });
            Thread.sleep(10000);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
package org.example.Aio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.AsynchronousByteChannel;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

public class AioServer {
    public static void main(String[] args) throws IOException {
        AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open();
        server.bind(new InetSocketAddress("localhost", 8001));
        System.out.println(" server is witing at port 8001");

        server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel channel, Object attchment){
                server.accept(null, this);
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                    @Override
                    public void completed(Integer result_num, ByteBuffer attachment) {
                        attachment.flip();
                        CharBuffer charBuffer = CharBuffer.allocate(1024);
                        CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
                        decoder.decode(attachment, charBuffer, false);
                        charBuffer.flip();
                        String data = new String(charBuffer.array(),0,charBuffer.limit());
                        System.out.println("client said: "+data);
                        channel.write(ByteBuffer.wrap((data + " 666").getBytes()));
                        try {
                            channel.close();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    };

                    @Override
                    public void failed(Throwable exc, ByteBuffer attachment) {
                        System.out.println("read error "+ exc.getMessage());
                    }
                });
            }
           public void failed(Throwable exc, Object attachment){
               System.out.println("failed "+ exc.getMessage());
           }
        });
        while (true){
            try {
                Thread.sleep(5000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }

    }

}


http://www.kler.cn/a/311772.html

相关文章:

  • 扫雷游戏代码分享(c基础)
  • 【设计模式】结构型模式(四):组合模式、享元模式
  • 【LeetCode】【算法】23. 合并K个升序链表
  • 【LLM】3:从零开始训练大语言模型(预训练、微调、RLHF)
  • 第二十周学习周报
  • Unicode字符集(万国码)
  • 二叉树(下)
  • Conda Config修改
  • 深度学习-18-深入理解BERT实战使用预训练的DistilBERT模型
  • 【Vue嵌套数据中,实现动态表头和内容】
  • 不会JS逆向也能高效结合Scrapy与Selenium实现爬虫抓取
  • 前端框架对比和选择?
  • [学习笔记]树链剖分(简易版) 及其LCA
  • Redis实践之缓存:设置缓存过期策略
  • 计算机网络33——文件系统
  • sqli-labs靶场自动化利用工具——第13关
  • RabbitMQ 和 Kafka 的详细对比表格
  • 消息队列:如何确保消息不会丢失?
  • 自然语言处理实战项目全解析
  • 阻止冒泡事件
  • Python中的异步编程:从基础知识到高级应用
  • vi | vim基本使用
  • 视频相关处理
  • 基于Delphi的题库生成系统
  • spark读mongodb
  • HTB-Jerry(tomcat war文件、msfvenom)