网络编程-轮询和长轮询

轮询(Polling):是指不管服务器端有没有更新,客户端(通常是指浏览器)都定时的发送请求进行查询,轮询的结果可能是服务器端有新的更新过来,也可能什么也没有,只是返回个空的信息。不管结果如何,客户端处理完后到下一个定时时间点将继续下一轮的轮询。

长轮询(Long Polling):长轮询的服务其客户端是不做轮询的,客户端在发起一次请求后立即挂起,一直到服务器端有更新的时候,服务器才会主动推送信息到客户端。 在服务器端有更新并推送信息过来之前这个周期内,客户端不会有新的多余的请求发生,服务器端对此客户端也啥都不用干,只保留最基本的连接信息,一旦服务器有更新将推送给客户端,客户端将相应的做出处理,处理完后再重新发起下一轮请求。

可见,长轮询的特点:

  • 服务器端会阻塞请求直到有数据传递或超时才返回.
  • 客户端响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接.
  • 当客户端处理接收的数据、重新建立连接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端重新建立连接,客户端会一次把当前服务器端所有的信息取回。

Java-长轮询(Long polling)实现

  • 服务端
package _20200418.example;

import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 长轮询服务端
 * <p>
 * Created by zfh on 2020/04/18
 */
public class server {

    private final AtomicLong value = new AtomicLong();

    private void start() throws IOException {
        HttpServer httpServer = HttpServer.create(new InetSocketAddress(8080), 0);
        httpServer.setExecutor(Executors.newCachedThreadPool());
        httpServer.createContext("/long-polling", httpExchange -> {

            byte[] data = fetchData();
            httpExchange.sendResponseHeaders(200, data.length);

            OutputStream outputStream = httpExchange.getResponseBody();
            outputStream.write(data);
            outputStream.close();
            httpExchange.close();
        });
        httpServer.start();
    }

    private byte[] fetchData() {
        try {
            // 由于客户端设置的超时时间是50s,
            // 为了更好的展示长轮询,这边random 100,模拟服务端hold住大于50和小于50的情况。
            Random random = new Random();
            TimeUnit.SECONDS.sleep(random.nextInt(100));
        } catch (InterruptedException ignored) {
        }
        return Long.toString(value.getAndIncrement()).getBytes(StandardCharsets.UTF_8);
    }

    public static void main(String[] args) throws IOException {
        server server = new server();
        server.start();
    }
}
  • 客户端
package _20200418.example;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 长轮询-客户端
 * <p>
 * Created by zfh on 2020/04/18
 */
public class client {

    private static final String SYNC_URL = "http://localhost:8080/long-polling";

    private final AtomicLong sequence = new AtomicLong();

    void poll() {
        // 循环执行,保证每次longpolling结束,再次发起longpolling
        // 结束条件,超时或者拿到数据
        while (!Thread.interrupted()) {
            doPoll();
        }
    }

    private void doPoll() {
        System.out.println("第" + (sequence.incrementAndGet()) + "次 longpolling");

        long startMillis = System.currentTimeMillis();

        HttpURLConnection connection = null;
        try {
            URL getUrl = new URL(SYNC_URL);
            connection = (HttpURLConnection) getUrl.openConnection();

            // 50s作为长轮询超时时间
            connection.setReadTimeout(50000);
            connection.setConnectTimeout(3000);
            connection.setRequestMethod("GET");
            connection.setUseCaches(false);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8");
            connection.connect();

            if (200 == connection.getResponseCode()) {
                BufferedReader reader = null;
                try {
                    reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
                    StringBuilder result = new StringBuilder(256);
                    String line;
                    while ((line = reader.readLine()) != null) {
                        result.append(line);
                    }
                    System.out.println("结果 " + result);
                } finally {
                    if (reader != null) {
                        reader.close();
                    }
                }
            }
        } catch (IOException e) {
            System.out.println("request failed");
        } finally {
            long elapsed = (System.currentTimeMillis() - startMillis) / 1000;
            System.out.println("connection close" + "     " + "elapse " + elapsed + "s");
            if (connection != null) {
                connection.disconnect();
            }
            System.out.println();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        client bootstrap = new client();
        bootstrap.poll();

        Thread.sleep(Integer.MAX_VALUE);
    }
}
posted @ 2020-06-01 14:20  MarsZuo  阅读(591)  评论(0编辑  收藏  举报