精华内容
下载资源
问答
  • 在上一篇《客户端启动源码分析》文章中讲到了客户端会使用两个线程去协调处理客户端与服务端的通信和watchers事件的回调,在这篇文章中我们会分析这两个线程是怎么相互纠缠的。 1. 例子 首先还是由第一篇文章来作为...

    在上一篇《客户端启动源码分析》文章中讲到了客户端会使用两个线程(SendThread和EventThread)去协调处理客户端与服务端的通信和watchers事件的回调,原本打算在这篇文章去分析这两个线程是怎么相互纠缠的。但是写着写着发现在客户端连接就花了很大的篇幅,不如这篇把标题改成ZK客户端与服务端建立连接的过程,那我在下一篇文章中再去分析SendThread和EventThread。当然这篇文章中也介绍了SendThread在客户端建立连接过程中发挥的作用。

    引例

    首先还是由第一篇文章中的Test来作为例子

    public class ZooKeeperTestClient extends ZKTestCase implements Watcher {
        protected String hostPort = "127.0.0.1:22801";
        protected static final String dirOnZK = "/test_dir";
        protected String testDirOnZK = dirOnZK + "/" + Time.currentElapsedTime();
    
    
        private void create_get_stat_test() throws IOException, InterruptedException, KeeperException {
            ZooKeeper zk = new ZooKeeper(hostPort, 10000, this);
            String parentName = testDirOnZK;
            String nodeName = parentName + "/create_with_stat_tmp";
            deleteNodeIfExists(zk, nodeName);
            deleteNodeIfExists(zk, nodeName + "_2");
            Stat stat = new Stat();
            //创建一个持久节点
            zk.create(nodeName, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat);
            assertNotNull(stat);
            assertTrue(stat.getCzxid() > 0);
            assertTrue(stat.getCtime() > 0);
            zk.close();
        }
    
    
        public synchronized void process(WatchedEvent event) {
            try {
                System.out.println("Got an event " + event.toString());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    

    先把涉及到的几个类的类图放出来,后面阅读的时候可做参考

    类图:

    1. 启动SendThread

    在上一篇文章中最后讲到了客户端启动的时候调用SendThread#start()方法

        public void start() {
            //负责客户端和服务端的通信
            sendThread.start();
            //主要负责在客户端回调注册的Watchers进行通知处理
            eventThread.start();
        }
    

    sendThread是一个线程,并且是ClientCnxn的内部类,条件反射地想到SendThread肯定有一个run方法,找到它:

            @Override
            public void run() {
    			//省略部分代码
                while (state.isAlive()) {
                   //省略部分代码
               }
            }
    

    State#isAlive()

            public boolean isAlive() {
                return this != CLOSED && this != AUTH_FAILED;
            }
    
    

    2. 状态初始化

    可以看到run方法里面去监听了网络状态,这个state是由一个全局变量去标识的,只要状态不是关闭和认证失败的状态就会一直循环在那里,那么状态是什么时候初始化的呢,这要回到创建Zookeeper实例的时候:

    ClientCnxn#changeZkState()

       
       
        volatile States state = States.NOT_CONNECTED;
        
       synchronized void changeZkState(ZooKeeper.States newState) throws IOException {
                if (!state.isAlive() && newState == States.CONNECTING) {
                    throw new IOException(
                            "Connection has already been closed and reconnection is not allowed");
                }
                // It's safer to place state modification at the end.
                state = newState;
            }
    

    由上面的流程知道,状态默认是NOT_CONNECTED,但在ZooKeeper实例化的时候就将状态(States)置为CONNECTING了,现在可以把SendThread的run方法拿出来。

    public void run{
                while (state.isAlive()) {
                    try {
                        if (!clientCnxnSocket.isConnected()) {
                            // don't re-establish connection if we are closing
                            if (closing) {
                                break;
                            }
                            if (rwServerAddress != null) {
                                serverAddress = rwServerAddress;
                                rwServerAddress = null;
                            } else {
                                serverAddress = hostProvider.next(1000);
                            }
                            onConnecting(serverAddress);
                            //开始连接服务
                            startConnect(serverAddress);
                            clientCnxnSocket.updateLastSendAndHeard();
                      }
                      //省略其他判断逻辑
                }
             }
    

    由于初始状态是CONNECTING,那么首先会进入到第一个判断去连接服务:

    3. 开始连接

    请注意,接下来会在ClientCnxn和ClientCnxnSocketNIO两个类中跳来跳去,请抓稳!

    ClientCnxn#startConnect()

      private void startConnect(InetSocketAddress addr) throws IOException {
                // initializing it for new connection
                changeZkState(States.CONNECTING);
                logStartConnect(addr);
    			//省略部分代码
    			//连接服务端
                clientCnxnSocket.connect(addr);
            }
    
    

    connect方法是ClientCnxnSocket中的抽象方法,子类ClientCnxnSocketNIO中实现了这个方法:

    ClientCnxnSocketNIO#connect()

       @Override
        void connect(InetSocketAddress addr) throws IOException {
            SocketChannel sock = createSock();
            try {
                registerAndConnect(sock, addr);
            } catch (UnresolvedAddressException | UnsupportedAddressTypeException | SecurityException | IOException e) {
                LOG.error("Unable to open socket to {}", addr);
                sock.close();
                throw e;
            }
            //是否初始化完成(是否连接成功)
            initialized = false;
    
            /*
             * Reset incomingBuffer
             */
            lenBuffer.clear();
            incomingBuffer = lenBuffer;
        }
    
    
        void registerAndConnect(SocketChannel sock, InetSocketAddress addr) throws IOException {
            sockKey = sock.register(selector, SelectionKey.OP_CONNECT);
            //建立socket连接
            boolean immediateConnect = sock.connect(addr);
            if (immediateConnect) {
                sendThread.primeConnection();
            }
        }
    

    连接成功后又会去调用SendThread#primeConnection()方法:

    SendThread#primeConnection()

            void primeConnection() throws IOException {
                LOG.info(
                    "Socket connection established, initiating session, client: {}, server: {}",
                    clientCnxnSocket.getLocalSocketAddress(),
                    clientCnxnSocket.getRemoteSocketAddress());
                isFirstConnect = false;
                long sessId = (seenRwServerBefore) ? sessionId : 0;
                //构造连接请求
                ConnectRequest conReq = new ConnectRequest(0, lastZxid, sessionTimeout, sessId, sessionPasswd);
                //讲请求报文添加到outgoingQueue队列
                outgoingQueue.addFirst(new Packet(null, null, conReq, null, null, readOnly));
                //告知ClientCnxnSocket连接请求已经发送
                clientCnxnSocket.connectionPrimed();
                LOG.debug("Session establishment request sent on {}", clientCnxnSocket.getRemoteSocketAddress());
            }
    

    ClientCnxnSocketNIO#connectionPrimed():

       void connectionPrimed() {
            sockKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
        }
     
    

    好了,这里先暂停一下,咱们总结一下上面过程做了哪些事情:

    1. 初始化状态为CONNECTING
    2. 建立Socket连接
    3. 构造连接请求Packet
    4. 发送请求报文
    5. 将ClientCnxnSocketNIO的全局变量sockKey置为SelectionKey.OP_READ | SelectionKey.OP_WRITE,即设置读写事件的监听,因为后面需要监听服务端的返回,并且会影响到SendThread的run方法后面的逻辑。

    4. 处理服务端连接响应

    上面只是分析了SendThread#run()方法的一部分,这时候只是建立了Socket连接,但是还不能发送读写请求,接下来继续分析run方法剩下的部分:
    SendThread#run()

    public void run(){
    	//省略部分代码,上面文章中已经分析了一部分,还有一部分这篇文章可忽略
       clientCnxnSocket.doTransport(to, pendingQueue, ClientCnxn.this);
    }
    

    又跑到了ClientCnxnSocketNIO#doTransport()方法:

    
       @Override
        void doTransport(
            int waitTimeOut,
            Queue<Packet> pendingQueue,
            ClientCnxn cnxn) throws IOException, InterruptedException {
            //等待服务端返回
            selector.select(waitTimeOut);
            Set<SelectionKey> selected;
            synchronized (this) {
                selected = selector.selectedKeys();
            }
            // Everything below and until we get back to the select is
            // non blocking, so time is effectively a constant. That is
            // Why we just have to do this once, here
            updateNow();
            for (SelectionKey k : selected) {
                SocketChannel sc = ((SocketChannel) k.channel());
                if ((k.readyOps() & SelectionKey.OP_CONNECT) != 0) {
                    if (sc.finishConnect()) {
                        updateLastSendAndHeard();
                        updateSocketAddresses();
                        sendThread.primeConnection();
                    }
                } else if ((k.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_WRITE)) != 0) {
                    doIO(pendingQueue, cnxn);
                }
            }
            if (sendThread.getZkState().isConnected()) {
                if (findSendablePacket(outgoingQueue, sendThread.tunnelAuthInProgress()) != null) {
                    enableWrite();
                }
            }
            selected.clear();
        }
    
    

    很简单地会想到服务端响应之后会走到:

    doIO(pendingQueue, cnxn);

    看看这个方法里面做了什么:

        void doIO(Queue<Packet> pendingQueue, ClientCnxn cnxn) throws InterruptedException, IOException {
            SocketChannel sock = (SocketChannel) sockKey.channel();
            if (sock == null) {
                throw new IOException("Socket is null!");
            }
            if (sockKey.isReadable()) {
                int rc = sock.read(incomingBuffer);
                if (rc < 0) {
                    throw new EndOfStreamException("Unable to read additional data from server sessionid 0x"
                                                   + Long.toHexString(sessionId)
                                                   + ", likely server has closed socket");
                }
                if (!incomingBuffer.hasRemaining()) {
                    incomingBuffer.flip();
                    if (incomingBuffer == lenBuffer) {
                        recvCount.getAndIncrement();
                        readLength();
                     	//第一次接受服务端的响应肯定会走到这else if里面来
                    } else if (!initialized) {
                   		 //读取服务端返回的结果
                        readConnectResult();
                        enableRead();
                        if (findSendablePacket(outgoingQueue, sendThread.tunnelAuthInProgress()) != null) {
                            // Since SASL authentication has completed (if client is configured to do so),
                            // outgoing packets waiting in the outgoingQueue can now be sent.
                            enableWrite();
                        }
    					//省略部分代码
                        initialized = true;
                    } 
             		  //省略部分代码
                }
            }
    
        }   
    

    由上面分析过的代码知道initialized的初始值为false,不行可以去上面找,在ClientCnxnSocketNIO#connect() 中

    所以后面走到了readConnectResult()中,处理服务端的相应:

    ClientCnxnSocket#readConnectResult()

    
        void readConnectResult() throws IOException {
            if (LOG.isTraceEnabled()) {
                StringBuilder buf = new StringBuilder("0x[");
                for (byte b : incomingBuffer.array()) {
                    buf.append(Integer.toHexString(b)).append(",");
                }
                buf.append("]");
                if (LOG.isTraceEnabled()) {
                    LOG.trace("readConnectResult {} {}", incomingBuffer.remaining(), buf.toString());
                }
            }
    
            ByteBufferInputStream bbis = new ByteBufferInputStream(incomingBuffer);
            BinaryInputArchive bbia = BinaryInputArchive.getArchive(bbis);
            ConnectResponse conRsp = new ConnectResponse();
            //反序列化
            conRsp.deserialize(bbia, "connect");
    
            // read "is read-only" flag
            boolean isRO = false;
            try {
                isRO = bbia.readBool("readOnly");
            } catch (IOException e) {
                // this is ok -- just a packet from an old server which
                // doesn't contain readOnly field
                LOG.warn("Connected to an old server; r-o mode will be unavailable");
            }
    
            this.sessionId = conRsp.getSessionId();
            sendThread.onConnected(conRsp.getTimeOut(), this.sessionId, conRsp.getPasswd(), isRO);
        }
    
    

    ClientCnxn#onConnected():

           void onConnected(
                int _negotiatedSessionTimeout,
                long _sessionId,
                byte[] _sessionPasswd,
                boolean isRO) throws IOException {
                negotiatedSessionTimeout = _negotiatedSessionTimeout;
                //省略部分代码
                //读写客户端不能与只读服务端建立连接
                if (!readOnly && isRO) {
                    LOG.error("Read/write client got connected to read-only server");
                }
    
                readTimeout = negotiatedSessionTimeout * 2 / 3;
                connectTimeout = negotiatedSessionTimeout / hostProvider.size();
                hostProvider.onConnected();
                sessionId = _sessionId;
                sessionPasswd = _sessionPasswd;
                changeZkState((isRO) ? States.CONNECTEDREADONLY : States.CONNECTED);
                seenRwServerBefore |= !isRO;
                LOG.info(
                    "Session establishment complete on server {}, session id = 0x{}, negotiated timeout = {}{}",
                    clientCnxnSocket.getRemoteSocketAddress(),
                    Long.toHexString(sessionId),
                    negotiatedSessionTimeout,
                    (isRO ? " (READ-ONLY mode)" : ""));
                KeeperState eventState = (isRO) ? KeeperState.ConnectedReadOnly : KeeperState.SyncConnected;
                eventThread.queueEvent(new WatchedEvent(Watcher.Event.EventType.None, eventState, null));
            }
    
    

    主要是这一行:

    changeZkState((isRO) ? States.CONNECTEDREADONLY : States.CONNECTED);

    这里就将状态置为CONNECTED了,后面就可以在SendThread里面响应其他的请求了啦。

    这里再小小总结一下:

    1. 读取服务端的响应数据并反序列化
    2. 判断服务端的状态是否是ReadOnly的状态
    3. 如果不是ReadOnly状态就将状态置为CONNECTED

    好了以上大概就是整个客户端与服务端建立连接的过程了,当然ClientCnxnSocket默认实现类由两个,本偏只是就ClientCnxnSocketNIO去分析,ClientCnxnSocketNIO是基于NIO的实现,还有另一个是基于Netty的实现,有兴趣的可以看看,后面有时间的话也会去分析。

    5. 流程图

    附赠整个流程图
    流程:
    在这里插入图片描述

    展开全文
  • 有些原因查出来却总叫人哭笑不得,比如本案例所说的,服务端的程序挂了导致的connection reset by peer问题。这种最基础的原因却往往最能怀疑到,我个人觉得主要原因有以下两点。 一,定式思维总会让人最先排出掉最...

    cdn线网运营总会遇到有各种各样的奇怪问题,而导致这些问题却对应的各种各样的原因。有些原因查出来却总叫人哭笑不得,比如本案例所说的,服务端的程序挂了导致的connection reset by peer问题。这种最基础的原因却往往最能怀疑到,我个人觉得主要原因有以下两点。

    一,定式思维总会让人最先排出掉最接近真相到原因。有点类似高中做题,总容易先入为主,陷入死胡同。

    二,没有人对各种原因做归纳总结,不同的原因导致的虽然都是连接失败这一现象,但如果深入研究,一定有独特的特征。需要有人说出“白马”和“黑马”的各自独特性。网上搜索了下connection reset by peer关键字,发现只有应用层原因说明各种原因,并没有这种问题的tcp层的原因介绍。那就我来根据线网运营遇到的案例来分析具体tcp层发生来什么,各种connection reset by peer的原因在tcp层有啥不同的特征来区分。

    废话不多扯,直接开始说事。下图是某下载业务在第三方竞速平台的错误点,错误类型是建立连接失败。幸好第三方测试平台自带错误点抓包功能,保留了错误发生的现场。

    从图中可以看到,客户端没发送一个syn包都会回复一个reset包。很直观的感觉三次握手阶段,连接还没建立肯定还没有到应用层,必然跟应用层没有关系。并且如果应用层有问题,必然会导致大面积的连接失败,而第三方测试平台显示只是两个错误点。而我首先怀疑的是前一条连接的time_wait一直存在,导致的新链接被reset,因为我们从入门就有意无意的被灌输“前一条流会影响后一条流的建联,尤其是time_wait状态的连接总是充满着各种神秘”。

    首先tcp层发送的reset是分为两种——active_reset和非active_reset, active_reset是调用tcp_send_active_reset()进行发送的,而非active_reset是调用tcp_v4_send_reset()进行发送。那从抓包来看怎么确认是哪种类型的reset呢? 关键是要看Win值是否有设置, tcp_send_active_reset()调用的是tcp_transmit_skb()进行发送的,在tcp_transmit_skb()会计算和设置接收窗口。而tcp_v4_send_reset()函数设置的Win是零。

    void tcp_send_active_reset(struct sock *sk, gfp_t priority)
    
    {
        struct sk_buff *skb;
    
        /* NOTE: No TCP options attached and we never retransmit this. */
        skb = alloc_skb(MAX_TCP_HEADER, priority);
        if (!skb) {
            NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTFAILED);
            return;
        }
    
        /* Reserve space for headers and prepare control bits. */  
        skb_reserve(skb, MAX_TCP_HEADER);
        tcp_init_nondata_skb(skb, tcp_acceptable_seq(sk),
                                TCPHDR_ACK | TCPHDR_RST);
    
        /* Send it off. */
        if (tcp_transmit_skb(sk, skb, 0, priority))
            NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTFAILED);
    
        TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTRSTS);
    
    }

    wireshark上来看Win的值为0,所以是非active_reset。那查看下调用tcp_v4_send_reset()的位置,只有四处地方会调用。

    1,监听套接口不存在

    tcp层入口函数tcp_v4_rcv函数中,会调用__inet_lookup_skb找到对应的sock,如果连符合条件的listen状态的sock都没有找到,并且数据包校验和没问题就会发送reset包断开对端。

    int tcp_v4_rcv(struct sk_buff *skb)
    
    {
    ....
        sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
    
        if (!sk)
            goto no_tcp_socket;
    ...
    no_tcp_socket:
        if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
            goto discard_it;
    
        if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) { //校验和出错
    csum_error:
            TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS);
    bad_packet:
            TCP_INC_STATS_BH(net, TCP_MIB_INERRS);
        } else {
            tcp_v4_send_reset(NULL, skb); //发送reset包
        }
    }

    2,四元组复用,但上一条流还未结束

    使用四元组在__inet_lookup_skb查询找到一个time_wait的sock,也就是上一条流并没有结束,在tcp_timewait_state_process()调用后返回TCP_TW_RST,认为对端有错误发送reset关闭对端。但该case中客户端一共发送了三次syn包,如果第一次发送reset是有time_wait状态到sock存在,第二次和第三次syn包就不会响应reset,因为发送完第一次reset包就已经在time_wait的sock释放。

    int tcp_v4_rcv(struct sk_buff *skb)
    {
        ...
    do_time_wait:
        ...
            switch (tcp_timewait_state_process(inet_twsk(sk), skb, th)) {
            case TCP_TW_SYN: {
                    struct sock *sk2 = inet_lookup_listener(dev_net(skb->dev),
                                                            &tcp_hashinfo,
                                                            iph->saddr, th->source,
                                                            iph->daddr, th->dest,
                                                            inet_iif(skb));
                    if (sk2) {
                            inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
                            inet_twsk_put(inet_twsk(sk));
                            sk = sk2;
                            goto process;
                    }
                    /* Fall through to ACK */
            }                       
            case TCP_TW_ACK:
                    tcp_v4_timewait_ack(sk, skb);
                    break;
            case TCP_TW_RST:
                    tcp_v4_send_reset(sk, skb);
                    inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
                    inet_twsk_put(inet_twsk(sk));
                    goto discard_it;
            case TCP_TW_SUCCESS:;
            }
            goto discard_it;
    }
    

    3,第三次握手检查发现设置了syn或者rst标志的包,并且并不是此前重传的syn包

    struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
                               struct request_sock *req,
                               struct request_sock **prev,
                               bool fastopen)
    {
    ...
            /* RFC793: "second check the RST bit" and
             *         "fourth, check the SYN bit"
             */
            if (flg & (TCP_FLAG_RST|TCP_FLAG_SYN)) {
                    TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_ATTEMPTFAILS);
                    goto embryonic_reset;
            }
    ...
    embryonic_reset:
            if (!(flg & TCP_FLAG_RST)) {
                    /* Received a bad SYN pkt - for TFO We try not to reset
                     * the local connection unless it's really necessary to
                     * avoid becoming vulnerable to outside attack aiming at
                     * resetting legit local connections.
                     */
                    req->rsk_ops->send_reset(sk, skb);
            } else if (fastopen) { /* received a valid RST pkt */
                    reqsk_fastopen_remove(sk, req, true);
                    tcp_reset(sk);
            }
            if (!fastopen) {
                    inet_csk_reqsk_queue_drop(sk, req, prev);
                    NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_EMBRYONICRSTS);
            }
            return NULL;
    }

    4,TCP状态机处理

    tcp_v4_do_rcv()函数中调用tcp_child_process()和tcp_rcv_state_process()返回值非零时,都会触发发送reset,tcp_child_process()是服务端三次握手完成才会调用,tcp_rcv_state_process传递进去的sk仍为listen状态的sk,所以只剩listen状态下收到ack包,或者含有syn标志,但是调用conn_request()返回值小于零。然而tcp_conn_request只会返回0。

    int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
    {
    ...
            if (sk->sk_state == TCP_LISTEN) {
                    struct sock *nsk = tcp_v4_hnd_req(sk, skb);
                    if (!nsk)
                            goto discard;
    
                    if (nsk != sk) {
                            sock_rps_save_rxhash(nsk, skb);
                            if (tcp_child_process(sk, nsk, skb)) {
                                    rsk = nsk;
                                    goto reset;
                            }
                            return 0;
                    }
            } else
                    sock_rps_save_rxhash(sk, skb);
    
            if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
                    rsk = sk;
                    goto reset;
            }
            return 0;
    
    reset:
            tcp_v4_send_reset(rsk, skb);
    discard:
    ...
    }
    int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                              const struct tcphdr *th, unsigned int len)
    {
    ...
            case TCP_LISTEN:
                    if (th->ack)
                            return 1;
    
                    if (th->rst)
                            goto discard;
    
                    if (th->syn) {
                            if (th->fin)
                                    goto discard;
                            if (icsk->icsk_af_ops->conn_request(sk, skb) < 0)
                                    return 1;
    
                            /* Now we have several options: In theory there is
                             * nothing else in the frame. KA9Q has an option to
                             * send data with the syn, BSD accepts data with the
                             * syn up to the [to be] advertised window and
                             * Solaris 2.1 gives you a protocol error. For now
                             * we just ignore it, that fits the spec precisely
                             * and avoids incompatibilities. It would be nice in
                             * future to drop through and process the data.
                             *
                             * Now that TTCP is starting to be used we ought to
                             * queue this data.
                             * But, this leaves one open to an easy denial of
                             * service attack, and SYN cookies can't defend
                             * against this problem. So, we drop the data
                             * in the interest of security over speed unless
                             * it's still in use.
                             */
                            kfree_skb(skb);
                            return 0;
                    }
                    goto discard;
    ...
    }

    上述四种情况,虽然不愿意相信,只剩第一种可能性最大。当查看服务端日志之后发现,该时刻服务端程序确实挂了。只是第三方测试的测试点发起请求的间隔比较大,并没有造成大面积的失败点。总结来说,服务端程序挂了,客户端报错connection reset by peer,在tcp层表现就为重试好几次都发生失败。

     

    展开全文
  • 学习了socket ,在做一个类似qq的c/s结构的聊天软件。发现了一个问题 就是在客户端与服务器通信后,服务器为客户端开启一个线程,使用while(true)循环时刻监听是否...java.net.SocketException: Connection reset at

    学习了socket  ,在做一个类似qq的c/s结构的聊天软件。发现了一个问题

    就是在客户端与服务器通信后,服务器为客户端开启一个线程,使用while(true)循环时刻监听是否有通信。

    在界面开启时没有任何异常,可是当界面关闭 就一直抛出异常了。

    开启线程代码如下:


    异常内容如下:


    java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(SocketInputStream.java:113)
    at java.io.ObjectInputStream$PeekInputStream.read(ObjectInputStream.java:2266)
    at java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2279)
    at java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:2750)
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:780)
    at java.io.ObjectInputStream.<init>(ObjectInputStream.java:280)
    at cn.edu.xynu.socket.ClientThread.run(ClientThread.java:22)

    仔细想想,或许在客户端关闭后  socke是t断开了,可是服务器为客户端已经开启的线程还在执行。

    所以要处理一下异常。

    在为客户端开启的线程里面定义一个布尔型变量,isOnline=true

    在为客户端监听时while(true)修改为while(isOnLine)

    如果出现异常,即客户端退出,isOnLine赋值为false。

    完美解决了这个问题~~

    展开全文
  • connection reset by peer问题总结及解决方案 1.服务器的并发连接数超过了其承载量,服务器会将其中一些连接关闭 如果知道实际连接服务器的并发客户端数并没有超过服务器的承载量,则有可能是中了病毒或者木马,引起...
  • 该实现机制可能会导致后端ECS认为相关TCP连接出现异常(非正常退出),并在业务软件如Java连接池等日志中抛出相应的错误信息,如Connection reset by peer。 2. TCP通讯机制,3次握手 3. wireshark抓包 ...

    1. TCP健康检查

    比如阿里云,F5负载设备当前都有这种机制。

    TCP健康检查过程示意图

     

    该实现机制可能会导致后端ECS认为相关TCP连接出现异常(非正常退出),并在业务软件如Java连接池等日志中抛出相应的错误信息,如Connection reset by peer

     

     

    2. TCP通讯机制,3次握手

     

    3. wireshark抓包

    转载于:https://www.cnblogs.com/jiftle/p/11419887.html

    展开全文
  • 关闭服务端连接

    2015-01-28 11:48:59
    在客户端和服务端的数据交互完成后,一般需要关闭网络连接。对于服务端来说,需要关闭Socket和ServerSocket.  在关闭Socket后,客户端并不会马上感知自已的Socket已经关闭,也就是说,在服务端的Socket关闭后,...
  • 如下图: 这个抓包很好的反应了压测中的现象:错误提示connection reset by peer,但是应用层并没有任何的读写,TCP三次握手后服务端直接通过RST关闭了连接。RST的情况见的多,这种情况着实没有遇到过。最后N次baidu...
  • TCP异常终止(reset报文) 在之前做智能家居系统时,师弟做的服务端与WiFI盒子(客户端)进行通信时,总是出现异常情况,然后服务端不停地向客户端发送RESET报文,之前一直都不知道是什么情况,因此一直不知道是...
  • 本机执行git push时出现ssh_exchange_identification: read: Connection reset by peer fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository ...
  • 比如说:IE访问IIS,获取文件,肯定是要建立一个连接,这个连接在完成通讯后,是客户端Close了连接,还是服务端Close了连接。我用程序测模拟IE和IIS,都没有收到断开连接的消息,也就是都没有触发OnClose事件。我是...
  • 关于netty连接报报Connection reset by peer

    千次阅读 2020-12-10 11:31:28
    java.io.IOException: Connection reset by peer at sun.nio.ch.FileDispatcherImpl.read0(Native Method) ~[?:1.8.0_181] at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39) ~[?:1.8.0_181] at ...
  • 如果在server端开一条线程专门处理socket连接,这就涉及到一个问题,如果socket连接断开(不论是正常断开还是异常掉线),怎么才能知道客服端的连接情况呢,server端这边是绝对被动的,sever端不能主动断开连接。...
  • 在使用HttpClient调用后台resetful服务时,“Connection reset”是一个比较常见的问题,有同学跟我私信说被这个问题困扰很久了,今天就来分析下,希望能帮到大家。例如我们线上的网关日志就会抛该错误: 从日志...
  • tcp 服务端如何判断客户端断开连接

    千次阅读 2019-06-25 15:57:47
    功能方面比较简单就是client端与server端建立连接,然后发送消息给server。我在server端会使用专门的线程处理一条socket连接。这就涉及到一个问题,如果socket连接断开(异常,正常)后,我如何才能感知到?server端...
  • 及时释放服务端与客户端之间的TCP连接的方法TCP的状态转换图 先贴上tcp状态转换图,方便后面分析问题 感知对端关闭,及时关闭己方连接  前几天遇到了一个问题,服务端下线,主动断开了连接。但客户端并...
  • 问题:上传文件到fastDFS报错:Connection reset by peer: socket write error等还有一下其他的错误。 查看fastDFS日志如下信息: 经检查,使用命令删除fastDFS存储的文件时,不小心将目录给删除了。上传文件到...
  •  (并发中调用了数据库,没调用一次建立一个连接,由于是并发环境,导致连接池资源耗尽) Caused by: org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate...
  • 初次学习socket,写了下面例子...java.net.SocketException: Connection reset异常,这是由于client退出但是没有关闭连接导致。 当然,socket.shutdownOutputStream() 可以提示服务端,发送完成而不阻塞read  
  • RESET来自何处

    2017-10-09 15:40:00
    有时候我们抓取网络包发现TCP RESET帧,我们想知道此时网络出了什么问题。仅看到TCP RESET帧不能说明网络出现问题,...举个例子,我们的应用建立了很多短连接,但我们不想在服务端time wait状态时继续保持连接,所以...
  • private void processSelectedKeysOptimized() { for (int i = 0; i < selectedKeys.size; ++i) { final SelectionKey k = selectedKeys.keys[i... selectedKeys.reset(i + 1); selectAgain(); i = -1; } } }
  • 2021年前端面试题及答案

    万次阅读 多人点赞 2020-02-11 19:29:34
    205 Reset Content 没有新的内容,但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容(HTTP 1.1新)。 206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完成了它(HTTP 1.1新)。 3...
  • 前端面试锦集

    千次阅读 多人点赞 2019-07-20 13:41:45
    WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。 ...
  • 控制台报错java.net.SocketException: Connection reset: 网上查博客后,发现原因:服务器端代码中有 socket.sendUrgentData(0) 之前我写过这个,是用于检测网络状态,判断客户端是否断开连接,但是原理不是很清楚...
  • 1.Reset为重置操作,Reset包不用等所有包都发送完在发送,随时可以发送,发送时会丢弃发送缓冲区的数据 2.FIN会等缓冲区的数据包发送完后再发送 3.理论上TCP包每个seq的包都要收到Ack确认后才会发送后续的包,但有了...
  • 在前面一期我们讲了Netty服务端启动流程,本期就带大家看看服务端是如何处理客户端连接的流程。 我们直接从#NioEventLoop的processSelectedKeys()方法开始,此方法的被调用处在#NioEventLoop的run()方法中,熟悉...
  • 基于早期质量低下的数据传输网络,连接建立只不过是开始,在通讯过程中保持稳定和通畅是TCP协议的重要内容。 由于TCP协议目的是保持长时间数据传输的稳定,因此它必须有效应对在连接过程中出现的突然中断情况。突然...
  • 测试开发笔记

    万次阅读 多人点赞 2019-11-14 17:11:58
    测试开发笔记 第一章 测试基础 7 什么是软件测试: 7 ★软件测试的目的、意义:(怎么做好软件测试) 7 3.软件生命周期: 7 第二章 测试过程 8 1.测试模型 8 H模型: 8 V模型 9 2.内部测试 10 ...
  • NIO

    千次阅读 多人点赞 2019-01-30 21:31:07
    用于源节点和目标节点的连接。在Java nio中负责缓冲区中数据的传输。 Channel本身不存储数据,因此需要配合缓冲区进行传输。 二、通道的主要实现类 java . nio . channels . Channel 接口 | -- FileChannel ...
  • 在前两篇博客中,我们讲解了服务端的启动和EventLoop相关的源码,接下来,我们看一下服务端是如何监听客户端接入并建立连接的: private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) 当...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,537
精华内容 10,214
关键字:

服务端reset连接