精华内容
下载资源
问答
  • Android WebSocket长连接
    千次阅读
    2022-04-22 14:32:26

    1.build.gradle中添加依赖:

    implementation "org.java-websocket:Java-WebSocket:1.5.1"

    2.在清单文件中加入网络请求权限:

     <uses-permission android:name="android.permission.INTERNET" />

    3.需要在AndroidManifest.xml中开启一个服务:

    <!-- 后台服务-长连接 -->
    <service android:name=".service.MyService" />

    写一个类MyService继承Service,在onCreate()方法中开启一个线程。

    public class MyService extends Service {
    
        //WebSocket长连接
        public Application application;
    
        public Context context;
    
        boolean isSuccess;
        private long sendTime = 0L;
        
    
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        //
        private WebSocketThread thread;
                                                 
    
        @Override
        public void onCreate() {
            super.onCreate();
            if (mWebSocket != null) {
                mWebSocket.close(1000, null);
            }
    
            new WebSocketThread().start();
            application= AndroidApp.getInstance();//这个是application,需要在功能清单里面的--android:name=".AndroidApp"
            context=AndroidApp.getInstance();
            Log.e("TAG", "onCreate------------*************-------------");                               
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            return super.onStartCommand(intent, flags, startId);
    
        }
    
    
        /**
         * 心跳检测时间
         */
        private static final long HEART_BEAT_RATE = 10000;//每隔10秒进行一次对长连接的心跳检测
        private final String WEBSOCKET_HOST_AND_PORT = "ws://";//可替换为自己的主机名和端口号
        private static WebSocket mWebSocket;
    
        // 初始化WebSocket
        private void initSocket() throws UnknownHostException, IOException {
            OkHttpClient client = new OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS).build();
            Request request = new Request.Builder().url(WEBSOCKET_HOST_AND_PORT).build();
            client.newWebSocket(request, new WebSocketListener() {
                @Override
                public void onOpen(WebSocket webSocket, Response response) {//开启长连接成功的回调
                    super.onOpen(webSocket, response);
                    mWebSocket = webSocket;
                }
    
                @Override
                public void onMessage(WebSocket webSocket, String text) {//接收消息的回调
                    super.onMessage(webSocket, text);
                    //收到服务器端传过来的消息text             
                    Log.e("TAG", "接收消息的回调--------------" + text);
                }
    
                @Override
                public void onMessage(WebSocket webSocket, ByteString bytes) {
                    super.onMessage(webSocket, bytes);
    
                }
    
                @Override
                public void onClosing(WebSocket webSocket, int code, String reason) {
                    super.onClosing(webSocket, code, reason);
    
                }
    
                @Override
                public void onClosed(WebSocket webSocket, int code, String reason) {
                    super.onClosed(webSocket, code, reason);
                }
    
                @Override
                public void onFailure(WebSocket webSocket, Throwable t, @Nullable Response response) {//长连接连接失败的回调
                    super.onFailure(webSocket, t, response);
                }
            });
    
            client.dispatcher().executorService().shutdown();
            mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);//开启心跳检测
        }
    
    
        class WebSocketThread extends Thread {
            @Override
            public void run() {
                super.run();
                try {
                    initSocket();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
        // 发送心跳包
        private Handler mHandler = new Handler();
        private Runnable heartBeatRunnable = new Runnable() {
            @Override
            public void run() {
               if (System.currentTimeMillis() - sendTime >= HEART_BEAT_RATE) {
                    if (mWebSocket != null) {
                        boolean isSuccess = mWebSocket.send("自定义发给后台服务器的消息");//发送一个消息给服务器,通过发送消息的成功失败来判断长连接的连接状态
                        if (!isSuccess) {//长连接已断开,
                            mHandler.removeCallbacks(heartBeatRunnable);
                            mWebSocket.cancel();//取消掉以前的长连接
                            new WebSocketThread().start();//创建一个新的连接
                        } else {//长连接处于连接状态---
                            Log.e("TAG", "发送心跳包-------------长连接处于连接状态");
                        }
                    }
                    sendTime = System.currentTimeMillis();
                }
                mHandler.postDelayed(this, HEART_BEAT_RATE);//每隔一定的时间,对长连接进行一次心跳检测
            }
        };
    
        //关闭长连接
        @Override
        public void onDestroy() {
            super.onDestroy();
            if (mWebSocket != null) {
                mWebSocket.close(1000, null);
            }
        }
    }

    在你需要长连接的地方,开启长连接:

    public class MainActivity extends BaseActivity {
    
        @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.activity_main);
             //开启长连接
             startService();
             //关闭长连接
             //stopService();
         }
    
        private void startService() {
            Intent intent1 = new Intent(MainActivity.this, MyService.class);
    	startService(intent1);
        }
    
    
    //    private void stopService() {
    //        Intent intent1 = new Intent(MainActivity.this, MyService.class);
    //        stopService(intent1);
    //    }
    }
    更多相关内容
  • Mina长连接框架实现Android客户端与服务器端通信
  • Java实现Socket长连接和短连接

    千次下载 热门讨论 2014-09-21 11:32:43
    Java实现Socket长连接和短连接,实现原理可参见个人博客
  • Netty4长连接、断开重连、心跳检测、Msgpack编码解码 http://blog.csdn.net/giousa/article/details/72846303#t2
  • 使用MINA实现长连接

    2016-08-13 17:16:41
    使用MINA实现长连接
  • 用okhttp实现webSocket长连接

    热门讨论 2016-08-24 15:49:41
    用okhttp实现webSocket长连接,可以接收服务端消息,向服务端发送消息,心跳包,维护长连接
  • 短连接(short connnection),是相对于长连接而言的概念,指的是在数据传送过程中,只在需要发送数据时才去建立一个连接,数据发送完成后则断开此连接,即每次连接只完成一项业务的发送。 2.通俗一点 长连接:连接-...

    查阅了一些资料,站在前人的肩膀上看世界会很美好。

    什么是长连接和短连接

    1.官方解释

    长连接(long connnection),指在一个连接上可以连续发送多个数据包,在连接保持期间,如果没有数据包发送,需要双方发链路检测包。

    短连接(short connnection),是相对于长连接而言的概念,指的是在数据传送过程中,只在需要发送数据时才去建立一个连接,数据发送完成后则断开此连接,即每次连接只完成一项业务的发送。

    2.通俗一点

    长连接:连接->传输数据->保持连接 -> 传输数据-> …->直到一方关闭连接,客户端关闭连接。长连接指建立SOCKET连接后无论使用与否都要保持连接。

    短连接:连接->传输数据->关闭连接。下次一次需要传输数据需要再次连接。

    下图对比帮助理解(当然,连接和响应的具体流程需要看对应的通信协议):

    在这里插入图片描述

    3.应用场景

    长连接

    长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用新建立TCP连接。

    例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。

    短连接

    而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。

    所以并发量大,但每个用户无需频繁操作情况下需用短连好。

    4.TCP长短连接的优势

    TCP短连接

    模拟一下TCP短连接的情况,client向server发起连接请求,server接到请求,然后双方建立连接。client向server发送消息,server回应client,然后一次读写就完成了,这时候双方任何一个都可以发起close操作,不过一般都是client先发起close操作。

    从上面的描述看,短连接一般只会在client/server间传递一次读写操作。

    短连接优点:管理起来方便,存在的连接都是有效的连接,不需要额外的控制手段。

    TCP长连接

    接下来我们再模拟一下长连接的情况,client向server发起连接,server接受client连接,双方建立连接。Client与server完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。下面介绍一下TCP的保活功能。

    TCP的保活功能主要为服务器应用提供。如果客户端已经消失而连接未断开,则会使得服务器上保留一个半开放的连接,而服务器又在等待来自客户端的数据,此时服务器将永远等待客户端的数据。保活功能就是试图在服务端器端检测到这种半开放的连接。如下示例:Client2关闭,TCP的保活功能使得Client1上仍然保留有一个半开放的连接,仍然在等待Cilent2的数据。

    在这里插入图片描述

    如果一个给定的连接在两小时内没有任何的动作,则服务器就向客户发一个探测报文段,客户主机必须处于以下4个状态之一:

    1. 客户主机依然正常运行,并从服务器可达。客户的TCP响应正常,而服务器也知道对方是正常的,服务器在两小时后将保证定时器复位。
    2. 客户主机已经崩溃,并且关闭或者正在重新启动。在任何一种情况下,客户的TCP都没有响应。服务端将不能收到对探测的响应,并在75秒后超时。服务器总共发送10个这样的探测 ,每个间隔75秒。如果服务器没有收到一个响应,它就认为客户主机已经关闭并终止连接。
    3. 客户主机崩溃并已经重新启动。服务器将收到一个对其保证探测的响应,这个响应是一个复位,使得服务器终止这个连接。
    4. 客户机正常运行,但是服务器不可达,这种情况与2类似,TCP能发现的就是没有收到探查的响应。

    在长连接的应用场景下,client端一般不会主动关闭它们之间的连接,Client与server之间的连接如果一直不关闭的话,会存在一个问题,随着客户端连接越来越多,server早晚有扛不住的时候,这时候server端需要采取一些策略,如关闭一些长时间没有读写事件发生的连接,这样可以避免一些恶意连接导致server端服务受损。如果条件再允许就可以以客户端机器为颗粒度,限制每个客户端的最大长连接数,这样可以完全避免某个蛋疼的客户端连累后端服务。

    长连接和短连接的产生在于client和server采取的关闭策略,具体的应用场景采用具体的策略,没有十全十美的选择,只有合适的选择。

    HTTP与TCP/IP

    关于HTTP和TCP/IP各协议的详细介绍点击【计算机网络-学习总结篇】。

    1.HTTP协议与TCP/IP协议的关系

    HTTP协议的长连接和短连接,本质上是TCP协议的长连接和短连接。HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议。 IP协议主要解决网络路由和寻址问题,TCP协议主要解决如何在IP层之上可靠地传递数据包,使得网络上接收端收到发送端所发出的所有包,并且接受顺序与发送顺序一致。TCP协议是可靠的、面向连接的。TCP才负责连接,只有负责传输的这一层才需要建立连接!!!!!

    HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。

    而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码(但要服务器和客户端都设置):

    Connection:keep-alive
    

    在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的TCP连接。也就是说在长连接情况下,多个HTTP请求可以复用同一个TCP连接,这就节省了很多TCP连接建立和断开的消耗

    Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。

    2.如何理解HTTP协议是无状态的

    HTTP协议是无状态的,指的是HTTP协议对事务处理没有记忆能力,服务器不知道客户端是什么状态。即第一次和第二次发起对应的请求之间没有任何联系。

    长轮询与短轮询

    1.什么是长轮询与短轮询

    短轮询相信大家都不难理解,比如你现在要做一个电商中商品详情的页面,这个详情界面中有一个字段是库存量(相信这个大家都不陌生,随便打开淘宝或者京东都能找到这种页面)。而这个库存量需要实时的变化,保持和服务器里实际的库存一致。

    这个时候,你会怎么做?

    最简单的一种方式,就是你用JS写个死循环,不停的去请求服务器中的库存量是多少,然后刷新到这个页面当中,这其实就是所谓的短轮询

    这种方式有明显的坏处,那就是你很浪费服务器和客户端的资源。客户端还好点,现在PC机配置高了,你不停的请求还不至于把用户的电脑整死,但是服务器就很蛋疼了。如果有1000个人停留在某个商品详情页面,那就是说会有1000个客户端不停的去请求服务器获取库存量,这显然是不合理的。

    那怎么办呢?

    长轮询这个时候就出现了,其实长轮询和短轮询最大的区别是,短轮询去服务端查询的时候,不管库存量有没有变化,服务器就立即返回结果了。而长轮询则不是,在长轮询中,服务器如果检测到库存量没有变化的话,将会把当前请求挂起一段时间(这个时间也叫作超时时间,一般是几十秒)。在这个时间里,服务器会去检测库存量有没有变化,检测到变化就立即返回,否则就一直等到超时为止

    而对于客户端来说,不管是长轮询还是短轮询,客户端的动作都是一样的,就是不停的去请求,不同的是服务端,短轮询情况下服务端每次请求不管有没有变化都会立即返回结果,而长轮询情况下,如果有变化才会立即返回结果,而没有变化的话,则不会再立即给客户端返回结果,直到超时为止。

    这样一来,客户端的请求次数将会大量减少(这也就意味着节省了网络流量,毕竟每次发请求,都会占用客户端的上传流量和服务端的下载流量),而且也解决了服务端一直疲于接受请求的窘境。

    但是长轮询也是有坏处的,因为把请求挂起同样会导致资源的浪费,假设还是1000个人停留在某个商品详情页面,那就很有可能服务器这边挂着1000个线程,在不停检测库存量,这依然是有问题的。

    因此,从这里可以看出,不管是长轮询还是短轮询,都不太适用于客户端数量太多的情况,因为每个服务器所能承载的TCP连接数是有上限的,这种轮询很容易把连接数顶满。哪怕轮询解决不了获取库存这个问题,但只要大家明白了长短轮询的区别,这就足够了。

    2.长短轮询和长短连接的区别

    这里简单说一下它们的区别:

    • 第一个区别是决定的方式,一个TCP连接是否为长连接,是通过设置HTTP的Connection Header来决定的,而且是需要两边(服务器端与应用端)都设置才有效。而一种轮询方式是否为长轮询,是根据服务端的处理方式来决定的,与客户端没有关系。
    • 第二个区别就是实现的方式,连接的长短是通过协议来规定和实现的。而轮询的长短,是服务器通过编程的方式手动挂起请求来实现的。

    参考:

    https://cloud.tencent.com/developer/article/1640430

    https://www.cnblogs.com/gotodsp/p/6366163.html

    展开全文
  • TCP长连接实践与挑战

    千次阅读 2022-01-27 15:39:05
    本文介绍了tcp长连接在实际工程中的实践过程,并总结了tcp连接保活遇到的挑战以及对应的解决方案。

    本文介绍了tcp长连接在实际工程中的实践过程,并总结了tcp连接保活遇到的挑战以及对应的解决方案。

    作者:字节跳动终端技术 ——— 陈圣坤

    概述

    众所周知,作为传输层通信协议,TCP是面向连接设计的,所有请求之前需要先通过三次握手建立一个连接,请求结束后通过四次挥手关闭连接。通常我们使用TCP连接或者基于TCP连接之上的应用层协议例如HTTP 1.0等,都会为每次请求建立一次连接,请求结束即关闭连接。这样的好处是实现简单,不用维护连接状态。但对于大量请求的场景下,频繁创建、关闭连接可能会带来大量的开销。因此这种场景通常的做法是保持长连接,一次请求后连接不关闭,下次再对该端点发起的请求直接复用该连接,例如HTTP 1.1及HTTP 2.0都是这么做的。然而在工程实践中会发现,实现TCP长连接并不像想象的那么简单,本文总结了实现TCP长连接时遇到的挑战和解决方案。

    事实上TCP协议本身并没有规定请求完成时要关闭连接,也就是说TCP本身就是长连接的,直到有一方主动关闭连接为止。实现TCP连接遇到的挑战主要有两个:连接池和连接保活。

    连接池

    长连接意味着连接是复用的,每次请求完连接不关闭,下次请求继续使用该连接。如果请求是串行的,那完全没有问题。但在并发场景下,所有请求都需要使用该连接,为了保证连接的状态正确,加锁不可避免,如果连接只有一个,就意味着所有请求都需要排队等待。因此长连接通常意味着连接池的存在:连接池中将保留一定数量的连接不关闭,有请求时从池中取出可用的连接,请求结束将连接返回池中。

    用go实现一个简单的连接池(参考《Go语言实战》):

    import (
        "errors"
        "io"
        "sync"
    )
    
    type Pool struct {
        m         sync.Mutex
        resources chan io.Closer
        closed    bool
    }
    
    func (p *Pool) Acquire() (io.Closer, error) {
        r, ok := <-p.resources
        if !ok {
            return nil, errors.New("pool has been closed")
        }
        return r, nil
    }
    
    func (p *Pool) Release(r io.Closer) {
        p.m.Lock()
        defer p.m.Unlock()
    
        if p.closed {
            r.Close()
            return
        }
        
        select {
        case p.resources <- r:
        default:
            // pool is full , just close
            r.Close()
        }
    }
    
    func (p *Pool) Close() error {
        p.m.Lock()
        defer p.m.Unlock()
        if p.closed {
            return nil
        }
        
        p.closed = true
        close(p.resources)
        for r := range p.resources {
            if err := r.Close(); err != nil {
                return err
            }
        }
    
        return nil
    }
    
    func New(fn func() (io.Closer, error), size uint) (*Pool, error) {
        if size <= 0 {
            return nil, errors.New("size too small")
        }
    
        res := make(chan io.Closer, size)
        for i := 0; i < int(size); i++ {
            c, err := fn()
            if err != nil {
                return nil, err
            }
    
            res <- c
        }
    
        return &Pool{
            resources: res,
        }, nil
    }
    

    池的对象只需实现io.Closer接口即可,利用go缓冲通道的特性可以轻松地实现连接池:获取连接时从通道中接收一个对象,释放连接时将该对象发送到连接池中。由于go的通道本身就是goroutine安全的,因此不需要额外加锁。Pool使用的锁是为了保证Release操作和Close操作的并发安全,防止连接池在关闭的同时再释放连接,造成预期外的错误。

    连接池经常遇到的一个问题就是池大小的控制:过大的连接池会带来资源的浪费,同时对服务端也会带来连接压力;过小的连接池在高并发场景下会限制并发性能。通常的解决办法是延迟创建和设置空闲时间,延迟创建是指连接只在请求到来时才创建,空闲时间是指连接在一定时间内未被使用则将被主动关闭。这样日常情况下连接池控制在较小的尺度,当并发请求量较大时会为新的请求创建新的连接,这些连接在请求完毕后返还连接池,其中的大部分会在闲置一定时间后被主动关闭,这样就做到了并发性能和IO资源之间较好的平衡。

    连接保活

    长连接的第二个问题就是连接保活的问题。虽然TCP协议并没有限制一个连接可以保持多久,理论上只要不关闭连接,连接就一直存在。但事实上由于NAT等网络设备的存在,一个连接即使没有主动关闭,它也不会一直存活。

    NAT

    NAT(Network Address Translation)是一种被广泛应用的网络设备,直观地解释就是进行网络地址转换,通过一定策略对tcp包的源ip、源端口、目的ip和目的端口进行替换。可以说,NAT有效缓解了ipv4地址紧缺的问题,虽然理论上ipv4早已耗尽,但正由于NAT设备的存在,ipv4的寿命超出了所预计的时间。公司内部的网络也是通过NAT构建起来的。

    虽然NAT有如此的优点,但它也带来了一些新的问题,对TCP长连接的影响就是其中之一。我们将一个通过NAT连接的网络简化成下面的模型:
    在这里插入图片描述A如果想保持对B的长连接,它实际并不与B直接建立连接,而是与NAT A建立长连接,而NAT A又与NAT B、NAT B与B建立长连接。如果NAT设备任由下面的机器保持连接不关闭,那它很容易就耗尽所能支持的连接数,因此NAT设备会定时关闭一定时间内没有数据包的连接,并且它不会通知网络的双方。这就是为什么我们有时候会遇到这种错误:

    error: read tcp4 1.1.1.1:8888->2.2.2.2:9999: i/o timeout
    

    按照TCP的设计,连接有一方要关闭连接时会有“四次挥手”的过程,通过一个关闭的连接发送数据时会抛出Broken pipe的错误。但NAT关闭连接时并不通知连接双方,发送方不知道连接已关闭,会继续通过该连接发送数据,并且不会抛出Broken pipe的错误,而接收方也不知道连接已关闭,还会持续监听该连接。这样发送方请求能成功发送,但接收方无法接收到该请求,因此发送方自然也等不到接收方的响应,就会阻塞至接口超时。经过实践发现公司的NAT超时是一个小时,也就是保持连接不关闭并闲置一个小时后,再通过该连接发送请求时,就会出现上述timeout的错误。

    我们上面提到连接池大小的控制问题,其实看起来有点类似NAT的超时控制,那既然我们允许连接池关闭超时的闲置连接,为什么不能接受NAT设备关闭呢?答案就是上面提到的,NAT设备关闭连接时并未通知连接双方,因此客户端使用连接请求时并不知道该连接实际上是否可用,而如果是由连接池主动关闭连接,那它自然知道连接是否是可用的。

    Keepalive

    通过上面的描述我们就知道怎么解决了,既然NAT会关闭一定时间内没有数据包的连接,那我们只需要让这个连接定时自动发送一个小数据包,就能保证连接不会被NAT自动关闭。

    实际上TCP协议中就包含了一个keepalive机制:如果keepalive开关被打开,在一段时间(保活时间:tcp_keepalive_time) 内此连接不活跃,开启保活功能的一端会向对端发送一个保活探测报文。只要我们保证这个tcp_keepalive_time小于NAT的超时时间,这个探测报文的存在就能保证NAT设备不会关闭我们的连接。

    unix系统为TCP开发封装的socket接口通常都有keepalive的相关设置,以go语言为例:

    conn, _ := net.DialTCP("tcp4", nil, tcpAddr)
    
    _ = conn.SetKeepAlive(true)
    
    _ = conn.SetKeepAlivePeriod(5 * time.Minute)
    

    另一个常见的保活机制是HTTP协议的keep-alive,不同于TCP协议,HTTP 1.0设计上默认是不支持长连接的,服务器响应完立即断开连接,通过请求头中的设置“connection: keep-alive”保持TCP连接不断开(HTTP 1.1以后默认开启)。

    流水线控制

    尽管使用连接池一定程度上能平衡好并发性能和io资源,但在高并发下性能还是不够理想,这是因为可能有上百个请求都在等同一个连接,每个请求都需要等待上一个请求返回后才能发出:
    在这里插入图片描述

    这样无疑是低效的,我们不妨参考HTTP协议的流水线设计,也就是请求不必等待上一个请求返回才能发出,一个TCP长连接会按顺序连续发出一系列请求,等到请求发送成功后再统一按顺序接收所有的返回结果:
    在这里插入图片描述
    这样无疑能大大减少网络的等待时间,提高并发性能。随之而来的一个显而易见的问题是如何保证响应和请求的正确对应关系?通常有两种策略:

    1. 如果服务端是单线程/进程地处理每个连接,那服务端天然就是以请求的顺序依次响应的,客户端接收到的响应顺序和请求顺序是一致的,不需要特殊处理;
    2. 如果服务端是并发地处理每个连接上的所有请求(例如将请求入队列,然后并发地消费队列,经典的如redis),那就无法保证响应的顺序与请求顺序一致,这时就需要修改客户端与服务端的通信协议,在请求与响应的数据结构中带上独一无二的序号,通过匹配这个序号来确定响应和请求之间的映射关系;

    HTTP 2.0实现了一个多路复用的机制,其实可以看成是这种流水线的优化,它的响应与请求的映射关系就是通过流ID来保证的。

    总结

    以上就是对TCP长连接实践中遇到的挑战和解决思路的总结,结合笔者在公司内部的实践经验分别探讨了连接池、连接保活和流水线控制等问题,梳理了实现TCP长连接经常遇到的问题,并提出了解决思路,在降低频繁创建连接的开销的同时尽可能地保证高并发下的性能。

    参考


    🔥 火山引擎 APMPlus 应用性能监控是火山引擎应用开发套件 MARS 下的性能监控产品。我们通过先进的数据采集与监控技术,为企业提供全链路的应用性能监控服务,助力企业提升异常问题排查与解决的效率。目前我们面向中小企业特别推出***「APMPlus 应用性能监控企业助力行动」,为中小企业提供应用性能监控免费资源包。现在申请,有机会获得60天免费性能监控服务,最高可享6000万*条事件量。

    👉 点击这里,立即申请

    展开全文
  • Android的socket长连接(心跳检测)

    千次下载 热门讨论 2014-12-18 17:10:54
    这是Android的socket长连接(心跳包),由于本人项目中需要用到长连接,所以先做了个demo,亲测是没有问题的。
  • Java如何实现长连接

    千次阅读 2021-02-13 02:23:56
    实现原理:长连接的维持,是要客户端程序,定时向服务端程序,发送一个维持连接包的。如果,长时间未发送维持连接包,服务端程序将断开连接。客户端:Client通过持有Socket的对象,可以随时(使用sendObject方法)发送...

    实现原理:

    长连接的维持,是要客户端程序,定时向服务端程序,发送一个维持连接包的。

    如果,长时间未发送维持连接包,服务端程序将断开连接。

    客户端:

    Client通过持有Socket的对象,可以随时(使用sendObject方法)发送Massage Object(消息)给服务端。

    如果keepAliveDelay毫秒(程序中是2秒)内未发送任何数据,则自动发送一个KeepAlive Object(心跳)给服务端,用于维持连接。

    由于,我们向服务端,可以发送很多不同的消息对象,服务端也可以返回不同的对象。所以,对于返回对象的处理,要编写具体的ObjectAction实现类进行处理。通过Client.addActionMap方法进行添加。这样,程序会回调处理。

    服务端:

    由于客户端会定时(keepAliveDelay毫秒)发送维持连接的信息过来,所以,服务端要有一个检测机制。

    即当服务端receiveTimeDelay毫秒(程序中是3秒)内未接收任何数据,则自动断开与客户端的连接。

    ActionMapping的原理与客户端相似(相同)。

    通过添加相应的ObjectAction实现类,可以实现不同对象的响应、应答过程。

    心跳反映的代码:

    package com.java.excel.keepalive;

    import java.io.Serializable;

    import java.text.SimpleDateFormat;

    import java.util.Date;

    /**

    *

    * 维持连接的消息对象(心跳对象)

    */

    public class KeepAlive implements Serializable {

    private static final long serialVersionUID = -2813120366138988480L;

    /* 覆盖该方法,仅用于测试使用。

    * @see java.lang.Object#toString()

    */

    @Override

    public String toString() {

    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"\t维持连接包";

    }

    }

    服务端

    package com.java.excel.keepalive;

    import java.io.IOException;

    import java.io.InputStream;

    import java.io.ObjectInputStream;

    import java.io.ObjectOutputStream;

    import java.net.ServerSocket;

    import java.net.Socket;

    import java.util.concurrent.ConcurrentHashMap;

    public class Server {

    /**

    * 要处理客户端发来的对象,并返回一个对象,可实现该接口。

    */

    public interface ObjectAction{

    Object doAction(Object rev, Server server);

    }

    public static final class DefaultObjectAction implements ObjectAction{

    public Object doAction(Object rev,Server server) {

    System.out.println("处理并返回:"+rev);

    return rev;

    }

    }

    public static void main(String[] args) {

    int port = 65432;

    Server server = new Server(port);

    server.start();

    }

    private int port;

    private volatile boolean running=false;

    private long receiveTimeDelay=3000;

    private ConcurrentHashMap actionMapping = new ConcurrentHashMap();

    private Thread connWatchDog;

    public Server(int port) {

    this.port = port;

    }

    public void start(){

    if(running)return;

    running=true;

    connWatchDog = new Thread(new ConnWatchDog());

    connWatchDog.start();

    }

    @SuppressWarnings("deprecation")

    public void stop(){

    if(running)running=false;

    if(connWatchDog!=null)connWatchDog.stop();

    }

    public void addActionMap(Class cls,ObjectAction action){

    actionMapping.put(cls, action);

    }

    class ConnWatchDog implements Runnable{

    public void run(){

    try {

    ServerSocket ss = new ServerSocket(port,5);

    while(running){

    Socket s = ss.accept();

    new Thread(new SocketAction(s)).start();

    }

    } catch (IOException e) {

    e.printStackTrace();

    Server.this.stop();

    }

    }

    }

    class SocketAction implements Runnable{

    Socket s;

    boolean run=true;

    long lastReceiveTime = System.currentTimeMillis();

    public SocketAction(Socket s) {

    this.s = s;

    }

    public void run() {

    while(running && run){

    if(System.currentTimeMillis()-lastReceiveTime>receiveTimeDelay){

    overThis();

    }else{

    try {

    InputStream in = s.getInputStream();

    if(in.available()>0){

    ObjectInputStream ois = new ObjectInputStream(in);

    Object obj = ois.readObject();

    lastReceiveTime = System.currentTimeMillis();

    System.out.println("接收:\t"+obj);

    ObjectAction oa = actionMapping.get(obj.getClass());

    oa = oa==null?new DefaultObjectAction():oa;

    Object out = oa.doAction(obj,Server.this);

    if(out!=null){

    ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());

    oos.writeObject(out);

    oos.flush();

    }

    }else{

    Thread.sleep(10);

    }

    } catch (Exception e) {

    e.printStackTrace();

    overThis();

    }

    }

    }

    }

    private void overThis() {

    if(run)run=false;

    if(s!=null){

    try {

    s.close();

    } catch (IOException e) {

    e.printStackTrace();

    }

    }

    System.out.println("关闭:"+s.getRemoteSocketAddress());

    }

    }

    }

    客户端

    package com.java.excel.keepalive;

    import java.io.IOException;

    import java.io.InputStream;

    import java.io.ObjectInputStream;

    import java.io.ObjectOutputStream;

    import java.net.Socket;

    import java.net.UnknownHostException;

    import java.util.concurrent.ConcurrentHashMap;

    public class Client {

    /**

    * 处理服务端发回的对象,可实现该接口。

    */

    public static interface ObjectAction{

    void doAction(Object obj,Client client);

    }

    public static final class DefaultObjectAction implements ObjectAction{

    public void doAction(Object obj,Client client) {

    System.out.println("处理:\t"+obj.toString());

    }

    }

    public static void main(String[] args) throws UnknownHostException, IOException {

    String serverIp = "127.0.0.1";

    int port = 65432;

    Client client = new Client(serverIp,port);

    client.start();

    }

    private String serverIp;

    private int port;

    private Socket socket;

    private boolean running=false; //连接状态

    private long lastSendTime; //最后一次发送数据的时间

    //用于保存接收消息对象类型及该类型消息处理的对象

    private ConcurrentHashMap actionMapping = new ConcurrentHashMap();

    public Client(String serverIp, int port) {

    this.serverIp=serverIp;

    this.port=port;

    }

    public void start() throws UnknownHostException, IOException {

    if(running)return;

    socket = new Socket(serverIp,port);

    System.out.println("本地端口:"+socket.getLocalPort());

    lastSendTime=System.currentTimeMillis();

    running=true;

    new Thread(new KeepAliveWatchDog()).start(); //保持长连接的线程,每隔2秒项服务器发一个一个保持连接的心跳消息

    new Thread(new ReceiveWatchDog()).start(); //接受消息的线程,处理消息

    }

    public void stop(){

    if(running)running=false;

    }

    /**

    * 添加接收对象的处理对象。

    * @param cls 待处理的对象,其所属的类。

    * @param action 处理过程对象。

    */

    public void addActionMap(Class cls,ObjectAction action){

    actionMapping.put(cls, action);

    }

    public void sendObject(Object obj) throws IOException {

    ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

    oos.writeObject(obj);

    System.out.println("发送:\t"+obj);

    oos.flush();

    }

    class KeepAliveWatchDog implements Runnable{

    long checkDelay = 10;

    long keepAliveDelay = 1000;

    public void run() {

    while(running){

    if(System.currentTimeMillis()-lastSendTime>keepAliveDelay){

    try {

    Client.this.sendObject(new KeepAlive());

    } catch (IOException e) {

    e.printStackTrace();

    Client.this.stop();

    }

    lastSendTime = System.currentTimeMillis();

    }else{

    try {

    Thread.sleep(checkDelay);

    } catch (InterruptedException e) {

    e.printStackTrace();

    Client.this.stop();

    }

    }

    }

    }

    }

    class ReceiveWatchDog implements Runnable{

    public void run() {

    while(running){

    try {

    InputStream in = socket.getInputStream();

    if(in.available()>0){

    ObjectInputStream ois = new ObjectInputStream(in);

    Object obj = ois.readObject();

    System.out.println("接收:\t"+obj);

    ObjectAction oa = actionMapping.get(obj.getClass());

    oa = oa==null?new DefaultObjectAction():oa;

    oa.doAction(obj, Client.this);

    }else{

    Thread.sleep(10);

    }

    } catch (Exception e) {

    e.printStackTrace();

    Client.this.stop();

    }

    }

    }

    }

    }

    b43a14207ac4c559aa31cc6ca8aa7c82.png

    以上就是Java如何实现长连接的详细内容,更多关于java实现长连接的资料请关注脚本之家其它相关文章!

    展开全文
  • HTTP长连接和WebSocket长连接的区别

    千次阅读 2020-11-10 16:04:42
    要理解HTTP长连接和websocket长连接的区别,首先要理解一下什么是HTTP的长连接和短连接。 一、HTTP的长连接和短连接区别 首先需要消除一个误解:HTTP协议是基于请求/响应模式的,因此客户端请求后只要服务端给了...
  • restTemplate做长连接

    千次阅读 2021-03-15 20:44:14
    restTemplate是springboot自带的http请求客户端,里面包装了HttpClient,是在SpringBoot项目中进行http请求常用的方式,本篇文章主要是讲解怎么进行restTemplate的长连接。 一、普通情况下的RestTemplate配置 @Bean...
  • TCP长连接(Keepalive)

    千次阅读 2021-10-22 16:31:14
    3.1 TCP短连接3.2 TCP长连接(Keepalive)4 长连接和短连接的优点和缺点5 什么时候用长连接,短连接6 TCP Keepalive作用7 TCP Keepalive可能导致的问题 1 TCP 长连接(Keepalive)的起源 TCP协议中有长连接和短连接之分...
  • 长连接和心跳 1 概念 2 优缺点 3 长连接的生命周期 4 使用场景 5 心跳 三 短连接 1 概念 2 优缺点 3 使用场景 四 长连接和短连接的选择 1 长连接:高频、服务端主动推送和有状态 2 短连接使用场景:...
  • socket长连接

    热门讨论 2014-11-17 10:35:10
    简单的demo用于客户端与服务端通过socket建立长连接,服务器和客户端相互发送消息以及接受消息
  • http的长连接和短连接(史上最通俗!)

    万次阅读 多人点赞 2019-02-15 10:36:08
    ...但终究觉得对于长连接一直懵懵懂懂的,有种抓不到关键点的感觉。 今天通过一番研究,终于明白了这其中的奥秘。而之前,也看过长连接相关的内容,但一直都是云里雾里的。这次之所以能在这么...
  • java socket长连接客户端服务端(标准实例),准确无误,流行结构。
  • 谈谈http的长连接和websocket的长连接

    千次阅读 2019-07-31 18:37:12
    关于Keep-alive的缺点 Keep-alive的确可以实现长连接,但是这个长连接是有问题的,本质上依然是客户端主动发起-服务端应答的模式,是没法做到服务端主动发送通知给客户端的。也就是说,在一个HTTP连接中,客户端...
  • android长连接实现

    万次阅读 2017-12-12 11:08:35
    一般而言长连接已经是App的标配了,推送功能的实现基础就是长连接,当然了我们也可以通过轮训操作实现推送功能,但是轮训一般及时性比较差,而且网络消耗与电量销毁比较多,因此一般推送功能都是通过长连接实现的。...
  • nginx长连接——keepalive

    千次阅读 2021-02-07 09:33:54
    当使用nginx作为反向代理时,为了支持长连接,需要做到两点: 从client到nginx的连接是长连接 从nginx到server的连接是长连接 1、保持和client的长连接: 默认情况下,nginx已经自动开启了对client连接的keep ...
  • Nginx 配置长连接

    千次阅读 2020-06-29 16:02:41
    对于客户端而言,Nginx是服务器,对于后端服务器而言,Nginx是客户端。 一、配置客户端到nginx的长连接 二、配置nginx到后端服务器的长连接
  • 长连接及Java Socket实现

    千次阅读 2020-07-29 19:39:43
    Socket默认是不关闭的,除非手动关闭,建立连接的两端可互相发送信息,连接的长短主要针对的是连接的时间,长时间不关闭的连接即长连接,短连接即建立连接的两端在发送一次或几次数据后很快关闭Socket的连接。
  • C# socket异步长连接

    热门讨论 2011-07-28 15:59:09
    服务端监听接收端口,以等待建立连接。 客户端向服务端发送连接请求,得到允许后,建立连接。 客户端发送的所有数据都是通过同一条连接
  • 本例子分为客户端(就是android手机),网页端为后台服务器。可以实现网页消息推送到手机,手机也可发消息到网页,很好的实现了消息的同步,不会有延迟,刷新现象。可以实现网页微信,二维码扫描登陆,聊天室等等。
  • 最近,公司在做Redis替换memcached的项目,那mc可以设置是长连接,那组件中的Redis到底用的是长连接还是短连接呢? 大牛军哥只给我一个命令:netstat -anp | grep "redis" 查看是否有类似下边的 上图中的代表的...
  • dubbo消费者与提供者之间的tcp长连接

    千次阅读 2022-01-21 18:04:24
    dubbo消费者同提供者之间的tcp连接是长连接形式,连接由消费方建立随机端口主动向提供者的dubbo端口发起连接请求,一旦连接建立,除非服务停止、网络异常,否则双方不会主动关闭tcp连接。也就是说dubbo消费方在调用...
  • HTTP 的长连接和短连接

    千次阅读 2022-04-10 21:15:38
    HTTP 的长连接和短连接
  • 断开连接则需要四次握手(客户端和服务端都可以发起,FIN-ACK-FIN-ACK)。 1、为什么连接的时候是三次握手,关闭的时候却是四次握手? 答: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;因为当...
  • Websocket如何实现长连接

    千次阅读 2019-12-02 11:30:25
    实现长连接 由于websocket连接成功过段时间自动关闭,无法继续获取消息 于是我就想到一个办法,就是自动关闭时再重新创建一次(因为自动关闭也不是很快就关闭的,所以我就这么写),虽然实现方式不是很好,但是也...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,925,512
精华内容 770,204
关键字:

长链接

友情链接: 209.zip