精华内容
下载资源
问答
  • 而且,当我使用嵌入式node启动节点后,系统自动启动一个node节点,node.name 也是随机的,并且并没有将该节点加入启动的es集群中,我加入的索引和数据也不能出现在 my-cluster 集群中 所以有两个问题: 1、...
  • 不断添加数据数据是之前输入过的,返回上一个页面回来也一样 输入数据 <p><img alt="" height="118" src="https://img-ask.csdnimg.cn/upload/1613226499043.png" width="372" /></p> 添加成功 ...
  • ![图片说明](https://img-ask.csdn.net/upload/201710/25/1508915399_421295.png) 为什么会出现这种情况啊,我明明设置了白名单,返回也正常但是还是会出现这种情况
  • 刚开始怀疑所JSON的格式错了,但为什么有时候成功呢? 于是怀疑所有些数据里面可能保护\n,\r等字符,影响了解析。可是加入了转义甚至替换之后,该问题还是没解决,陷入了困境。 难道所因为数据被截断了? 没...


    最近遇到被一个问题折腾惨了,后来终于解决,这里记录一下。

    问题现象:

    JSON解析失败,报错如下:


    可又不是每次都失败,有些时候又会成功。


    刚开始怀疑所JSON的格式错了,但为什么有时候会成功呢?

    于是怀疑所有些数据里面可能保护\n,\r等字符,影响了解析。可是加入了转义甚至替换之后,该问题还是没解决,陷入了困境。


    难道所因为数据被截断了?

    没办法,只能打日志进行调试,发现:

    //实现获取到数据的委托方法
    -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
        NSLog(@"length: %d", [data length]);
    }

    发现data的内容有时候为8520,有时候又是4536和3984。

    果然,数据真的被截断,被分成了两部分返回。




    于是开始找答案,发现当异步请求返回数据过大时,didiReceiveData这个代理函数所会调用多次的,应该使用connectionDidFinishLoading函数,把数据接收完全后进行处理。


    解决代码如下:

    1、在.h文件里面加入变量来接收数据

    @property(strong, nonatomic) NSMutableData *recvData;

    2、在didiReceiveData这个委托方法里面获取接收的数据进行存储

    //实现获取到数据的委托方法
    -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
        NSLog(@"length: %d", [data length]);
        [self.recvData appendData:data];
    }


    3、加入connectionDidFinishLoading函数,在这个函数中完成数据解析

    //获取到全部数据后,进行数据解析
    -(void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        NSError *error;
        NSDictionary *json = [NSJSONSerialization JSONObjectWithData:self.recvData options:NSJSONReadingMutableLeaves error:&error];
        if (json == nil) {
            [self doneLoadingTableViewData];
            NSLog(@"json parse failed. \n error is: %@", error);
            return;
        }
        
        NSString *result = [json objectForKey:@"result"];
        if ([result boolValue]) {
            //to do
        }
    }


    总结:

    对于非常少量的数据,可以在didiReceiveData里面写接收数据后的处理逻辑,对于请求大量数据的场景,切记,一定要在connectionDidFinishLoading中,对接收完全的数据进行处理。



    展开全文
  • 一般需求中会出现在Activity启动中需要获取Ui控件相关大小或者在界面绘制完成之后刷新数据,我们都知道在UI绘制完成之后,时机最好,不会阻塞主线程导致卡顿或者UI控件参数获取失败。 也许大家使用过或 知道Handler...

    一般需求中会出现在Activity启动中需要获取Ui控件相关大小或者在界面绘制完成之后刷新数据,我们都知道在UI绘制完成之后,时机最好,不会阻塞主线程导致卡顿或者UI控件参数获取失败。

    也许大家使用过或 知道Handler(MainLooper).Post(Runnable)和View.Post(Runnable)都是把Runnable封装成Message再 push到主线程中looper中MessageQueue中,会发现在Activity的生命周期中执行这两种方式效果不同,前者不满足我们的需求,而后者却能做到,但这是为啥,有没有深入分析,本文就从Activity启动流程以及UI刷新和绘制流程原理以及消息循环机制、同步障碍机制来剖析。

    先看demo运行效果,以获取Ui控件大小为例子,如下:

    class MyActivity extends Activity{
    .....
      @Override
     protected void onCreate(Bundle savedInstanceState) 
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
      myCustomView = findViewById(R.id.custom);
       Log.i("chuan", "onCreate init myCustomView  width=" + myCustomView.getWidth());
    }
     @Override
        protected void onResume() {
            super.onResume();
       Log.i("chuan", "Main onResume");
            new Handler().post(new Runnable() {
                @Override
                public void run() {
                    Log.i("chuan", "onResume Handler post runnable button width=" + myCustomView.getWidth());
                }
            });
            myCustomView.post(new Runnable() {
                @Override
                public void run() {
                    Log.i("chuan", "onResume myCustomView post runnable button width=" + myCustomView.getWidth());
                }
            });
        }
    public class MyView extends View {
      @Override
        public void layout(int l, int t, int r, int b) {
            super.layout(l, t, r, b);
            Log.i("chuan", "myView layout");
        }
        @Override
        protected void onAttachedToWindow() {
            Log.i("chuan", "myView onAttachedToWindow with"+getWidth());
            try {
                Object mAttachInfo = ReflectUtils.getDeclaredField(this, View.class, "mAttachInfo");
                Log.i("chuan", "myView onAttachedToWindow mAttachInfo=null?" + (mAttachInfo == null));
                Object mRunQueue = ReflectUtils.getDeclaredField(this, View.class, "mRunQueue");
                Log.i("chuan", "myView onAttachedToWindow mRunQueue=null?" + (mRunQueue == null));
            } catch (Exception e) {
            }
            super.onAttachedToWindow();
        }
        @Override
        public boolean post(Runnable action) {
            try {
                Object mAttachInfo = ReflectUtils.getDeclaredField(this, View.class, "mAttachInfo");
                Log.i("chuan", "myView post mAttachInfo=null?" + (mAttachInfo == null));
            } catch (Exception e) {
            }
            return super.post(action);
        }
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            Log.i("chuan", "myView onDraw");
            }
    }

    日志显示结果:

    /chuan: myView init
    /chuan: Main onCreate   
    /chuan: onCreate init myCustomView  width=0
    /chuan: Main onResume
    /chuan:
        myView post mAttachInfo=null?true
    /chuan: onResume Handler post runnable button width=0
    /chuan: myView onAttachedToWindow width=0
        myView onAttachedToWindow mAttachInfo=null?false
    /chuan: myView layout
    /chuan: myView onDraw
    /chuan: onResume myCustomView post runnable button width=854 

    从日志中可以看出几点

    1. 在Activity可交互之前的生命周期中UI直接操作是失效的,即使通过handler把Ui操纵任务post到onResume生命周期之后,也依然获取失效,日志可以看到此时ui界面都没有绘制。

    2. 发现View.post(Runnable)会让runnable在该View完成了measure、layout、draw之后再执行,这个时候当然就可以获取到Ui相关参数了。

    先看下两者的源码实现:

    1、handler.post(Runnable)

    Handler.class
    public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

    代码简单可以看到就是把runnable封装成Message然后加入当前Looper的MessageQueue队列中。

    2、再看下View.post(Runnable)

    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        //先是通过attachInfo.mHandler.post来实现,实际上就是用Handler.post和上述一样
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        //若attachInfo =null时,维护一个mRunQueue 队列,
        // 然后在dispatchAttachedToWindow通过mRunQueue.executeActions(info.mHandler);跟上述方法一样
        getRunQueue().post(action);
        return true;
    }
      void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;
        if (mOverlay != null) {
            mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
        }
    ..........省略....
        // Transfer all pending runnables.
        if (mRunQueue != null) {
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }
        performCollectViewAttributes(mAttachInfo, visibility);
        onAttachedToWindow();
    .......省略......
    }
    private HandlerActionQueue getRunQueue() {
        if (mRunQueue == null) {
            mRunQueue = new HandlerActionQueue();
        }
        return mRunQueue;
    }
    
    //实际也是通过handler来post到主线程
    public void executeActions(Handler handler) {
        synchronized (this) {
            final HandlerAction[] actions = mActions;
            for (int i = 0, count = mCount; i < count; i++) {
                final HandlerAction handlerAction = actions[i];
                handler.postDelayed(handlerAction.action, handlerAction.delay);
            }
            mActions = null;
            mCount = 0;
        }
    }
    
    

    通过源码调用发现最终都是通过handler.post()方式来加入到主线程队列中,api调用一样为何效果不一样,下面就从如下几个知识点来分析:

    1. Activity生命周期启动流程

    2. Message消息发送和执行原理机制

    3. UI绘制刷新触发原理机制

    4. MessageQueue同步障碍机制

    Activity启动流程 :ApplicationThread收到AMS的scheduleLaunchActivity的Binder消息之后,原因是binder线程,会通过ActivityThread中的mH(Handler)来sendMessage;

    mH(Handler)会把这个异步消息加入到MainLooper中MessageQueue,等到执行时候回调handleLaunchActivity,handleLaunchActivity方法会执行很多方法,这个是入口,简单来说会创建Activity对象,调用其启动生命周期,attach、onCreate、onStart、onResume,以及添加到WindowManager中,重点看下本文中onResume生命周期是如何回调的。

    在Activity可见之后,紧接着就是要触发绘制界面了,会走到handleResumeActivity方法,会performResumeActivity调用activity的onResume方法

    public final ActivityClientRecord performResumeActivity(IBinder token,
            boolean clearHide) {
        ActivityClientRecord r = mActivities.get(token);
        if (r != null && !r.activity.mFinished) {
           .....................
            try {
                r.activity.onStateNotSaved();
                r.activity.mFragments.noteStateNotSaved();
                //调用activity的onResume方法
                r.activity.performResume();
    ...............................
            } catch (Exception e) {
           ........   
            }
        }
        return r;
    }
    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        //1、调用activity的onResume方法
        ActivityClientRecord r = performResumeActivity(token, clearHide);
           ......
        if (r != null) {
            final Activity a = r.activity;
            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
          .......................
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                //2、decorView先暂时隐藏
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    //3、关键函数 添加到window触发ui测量、布局、绘制
                    wm.addView(decor, l);
                }
            ..............       
                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                //4、添加decorView之后,设置可见,从而显示了activity的界面
                    r.activity.makeVisible();
                }
            }
    }  

    由此可见:从handleResumeActivity执行流程来看onResume调用时候,Activity中的UI界面并没有经过measure、layout、draw等流程,所以直接在onResume或者之前的onCreate中执行ui操纵都是无用的,因为这个时候Ui界面不可见,没有绘制。那为何通过hander.post(Runnable)让执行发生在handleLaunchActivity这个Message之后,这个时候流程已经走完了,也是在Ui界面触发绘制之后,怎么还是不行呢。

    Message消息发送和执行原理机制这里就不阐述了,hander.post(Runnable)让执行发生在handleLaunchActivity这个Message之后就是因为这个Message循环机制原理,可以让任务通常让加入的先后顺序依次执行,所以handleLaunchActivity这个Message执行之后,就是onResume中的push的Message。

    但是为何onResume中hander.post(Runnable)还不能ui操作呢,就猜测handleLaunchActivity之后还没有同步完成UI绘制,那UI绘制刷新触发原理机制是怎么样的了,直接分析触发条件,上文中的wm.addVIew开始:windowManager会通过子类WindowManagerImpl来实现,其内部又通过WindowManagerGlobal的单实例来实现addVIew,源码如下 

    WindowManagerGlobal.class

    public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
          ...............
            ViewRootImpl root;
            View panelParentView = null;
            ......
            //ViewRootImpl整个UI操作实际控制着
                root = new ViewRootImpl(view.getContext(), display);
                view.setLayoutParams(wparams);
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
            }
    .................
            // do this last because it fires off messages to start doing things
            try {
            //绑定decorView,并触发开发绘制
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
            }
        }

    addView动作又转给ViewRootImpl.setView来实现,具体源码如下: 
    ViewRootImpl.class

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ..........
                //触发刷新绘制的关键
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                ..........
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    //通过binder call添加到Display
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
           ...............
            //decorView添加父类ViewParent 就是ViewRootImpl
                view.assignParent(this);
            }
        }
    }

    setView完成了上述几个重要步骤,其中requestLayout的实现是如何触发刷新绘制的:

    从上述代码可以发现在addView之后同步执行到requestLayout,再到scheduleTraversals中设置了同步障碍消息,这个简单阐述,看下源码实现: 

    MessageQueue.class

    private int postSyncBarrier(long when) {
       synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;
            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }
    //根据token移动这个Message
    public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();
            // If the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

    MessageQueue同步障碍机制: 可以发现就是把一条Message,注意这个Message是没有设置target的,整个消息循环唯一一处不设置回调的target(hander),因为这个即使标志了同步障碍消息,也是不需要handler来pushMessage到队列中,直接手动循环移动链表插入到合适time的Message之后的即可。

    然后是如何识别这个障碍消息的呢,在Looper的loop循环获取MessageQueue.next()函数获取下一个的message,是如何实现的,

    MessageQueue.class

    Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //查询是否有下一个消息,没有就阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                //关键地方,首先识别 msg.target == null情况就是同步障碍消息,如果该消息是同步障碍消息的话,就会循环查询下一个消息是否是isAsynchronous状态,异步Message,专门给刷新UI消息使用的
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                //如果查到异步消息或者没有设置同步障碍的消息,直接返回执行
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
             ........省略.................
          nextPollTimeoutMillis = 0;
        }
    }

    可以看到scheduleTraversals中设置了同步障碍消息,就是相当于在MessageQueue中插入了一个Message,并且是在onResume之后插入的,所以在onResume中handler.post(Runnable)之后,这个消息会在同步障碍Message之前,会先被执行,这个时候依然没有刷新绘制界面,待查询到同步障碍Message时候,会等待下个异步Message(刷新Message)出现。

    所以在onResume中handler.post(Runnable)是Ui操作失效的。

    那么为何View.post(Runnable)就可以了,再回过头来看下其源码: 
    View.class

    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

    由于在onResume中执行,这个时候ViewRootImpl还没有初始化(addView时),而mAttachInfo是在ViewRootImpl构造函数中初始化的,过此时mAttachInfo=null,从上文知道 getRunQueue()维护了一个mRunQueue 队列,然后在dispatchAttachedToWindow通过mRunQueue.executeActions(info.mHandler);那这个方法dispatchAttachedToWindow什么会被调用,回顾上文中ViewRootImpl第一次收到Vsync同步刷新信号之后会执行performTraversals,这个函数内部做了个判断当时第一次mFirst时候会调用host.dispatchAttachedToWindow(mAttachInfo, 0);把全局mAttachInfo下发给所有子View,其源码如下: 

    View.class

    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;
        if (mOverlay != null) {
        //向下分发info,其实现在ViewGroup中
            mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
        }
        mWindowAttachCount++;
        // We will need to evaluate the drawable state at least once
     .........
        // Transfer all pending runnables.
        if (mRunQueue != null) {
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }
        performCollectViewAttributes(mAttachInfo, visibility);
        onAttachedToWindow();
        ........
    }

    由此可以知道在performTraversals(Message)中push Message到主线中,肯定会这个performTraversals(Message)之后再执行,并且在doTraversals中移除了同步障碍消息(Message),故会依次执行。所以onResume中View.post的Message就会在performTraversals之后执行,而performTraversals就是完成了View整个测量、布局和绘制。当View的mAttachInfo !=null时也说明肯定完成过UI绘制。

    感谢看完,看似简单的东西,其实内部原理没有分析清楚容易糊涂,同时研究源码会学到很多相关的知识点,例如要看懂本文就需要了解上文中提到的4个知识点。 
    1. Activity生命周期启动流程 
    2. Message消息发送和执行原理机制 
    3. UI绘制刷新触发原理机制 
    4. MessageQueue同步障碍机制

     

     

     

     

     

     

     

     

    展开全文
  • UDP报文头部(源端口,目的端口,报头长度和数据长度和,校验和),有16位的校验...若数据一致表示这条数据可以交付给上层处理(当用户调用recvfrom能够获取数据);因为协议中有长度所以就根据头部中的长度进行交...

    UDP报文头部(源端口,目的端口,报头长度和数据长度和,校验和),有16位的校验和指的是一条数据通过sendto发送,数据到了传输层封装了UDP报头之后直接进行发送;对端收到UDP报文;对整个报文进行二进制反码求和(协议字段中的校验和),判断接受的报文是否和发送的一致;若数据一致表示这条数据可以交付给上层处理(当用户调用recvfrom能够获取数据);因为协议中有长度所以就会根据头部中的长度进行交付。因此面向数据报只能收发一整条,缓冲区不够就会报错。

    因为报长度只用了两个字节来保存,因此最大65595(64k)字节的长度,要是发送的数据大于64k就会报错

    因此要是数据过大需要用户在应用层分包处理,又因为UDP不保证数据的有序到达,因此需要用户在应用层进行包序管理。

    在TCP通信里面,在客户端的连接请求发送过来之后,需要三次握手连接,在SYN请求发送过来之后,就会建立一个新的new socket(小桃红)然后回复ACK+SYN得操作就会交给new socket去完成。

    关于三次握手为什么是三次而不是两次,或者四次的答复?

    其实四次也可以,但是两次是绝对不可以的,因为关于三次握手可以解释为,第一次客户端发来SYN类比于问服务端你在
    吗?服务端回复ACK+SYN类是在询问客户但是否具有收发数据的能力,然后客户端回复ACK接着就可以进行通信了,如
    果只有两次的话,很有可能在客户端发送完第一次的SYN之后,然后由于网络延迟很久才到达了服务端这个时候客户端都
    已经退出了,那么这个时候客户端在回复第二次之后,就开始数据的传送是不合理的。所以必需双方都确认具备有数据传
    送能力之后才可以进行数据的传送。又或者第一次请求由于网络延迟超过了应答的时间这个时候,服务端再发一次SYN这
    样加入第一次发的请求虽然延迟了但是还是到了服务端这样就会创建两个连接,浪费了资源。
    
    举个例子,你去找你们隔壁的小朋友玩,然后你敲门问,在吗(第一次SYN请求),由于他在拉屎没及时回复,然后你走
    了,等你走了之后,他说我再(第二次的ACK+SYN),但是你以为他不在你就走了,然后他说他在之后你也没回复他也就
    没有开门,要是你没走的话,你就可以回复他我在(第三次ACK),然后他就来开门了(传送数据)。万一你邻居小朋友
    最近耳屎有点多,她怕自己没听清,就在问,你真的在门口吗?你在回复在,这就是5次握手,所以说三次是最少握手三
    次,才可以传送数据。
    
    三次握手确认对方是否具备数据收发能力。
    
    回答上面的问题主要基于安全回答,两次连接所建立的连接是不安全的。
    

    关于挥手为什么是四次?
    在这里插入图片描述
    四次挥手:被动关闭方接收到关闭请求之后,进行ack确认,但是操作系统需要用户处理完所有的数据之后才会调用close发送FIN请求。
    三次握手连接失败怎么办?
    三次握手失败通常指的是最后一次的ACK没有发送给服务端,那失败之后服务端如何处理?

    虽然在网络基础2-2的时候我说了一个超时重传机制,但是那个只是在数据传输的时候有作用,在这一块是不一样的,在握手阶段服务端在最后一次ACK请求等了半天,然后就会给你发送一个RST报文,要求对方重新开始握手,将以创建的socket(小桃红)销毁掉。

    有一种攻击就叫做SYN泛洪攻击,黑客找了很多的肉鸡(所谓电脑肉鸡,就是拥有管理权限的远程电脑。也就是受别人控制的远程电脑。肉鸡可以是各种系统,如win,linux,unix等;更可以是一家公司\企业\学校甚至是政府军队的服务器,一般所说的肉鸡是一台开了3389端口的Win2K系统的服务器,所以3389端口没必要开时关上最好。要登陆肉鸡,必须知道3个参数:远程电脑的IP、用户名、密码。),然后给一个服务器发送SYN请求但是不进行ACK回复,然后服务器同时就会具有很多的未完成连接的socket然后服务器就会瘫痪。

    TIME_WAIT:问题:避免因为最后一次ACK的丢失对后续连接的影响
    1.假如没有TIME_WAIT状态会出现什么情况?

    如果没有TIME_WAIT的话,四次挥手最后一次ACK包如果丢失的话,那么客户端直接退出了,然后启用相同的端口和服务端进行连接的话就会使后续的连接刚上来就会收到服务端回复上一个客户端的请求重传的包,会引发混乱。其次是新起的客户端给服务端发送SYN连接请求之后,服务端还在等待上一次的ACK包,这样的话服务端会认为混乱,每一个状态都是为了等待下一步的操作。

    2.解决了什么情况,为什么要等待两个SML?
    (主要是为了防止对面让他重发第三次请求信息)
    两次SML指的是两次最大的数据生存周期,两次SML等的是,假如服务端没有收到客户端发来的请求就会所以他等的就是第四次ACK发送那个过去之后,在网络中的最大生命周期,假如过了最大生命周期服务端还没有收到的话(这个是等的第一个SML),那么服务端就会重发第三次的FIN包,等待这个包在消失在网络中之前被自己接收,这个是等待的第二个SML。

    服务端出现大量TIME_WAIT是什么原因,怎么解决?

    TIME_WAIT是主动断开连接的一方才会产生TIME_WAIT,就是服务端主动断开连接,在服务端产生大量的TIME_WAIT,解决的办法就是缩短TIME_WAIT的时间,或者开启套接字选项,端口重用,一般写服务器的时候一般都会采用端口重用,确保安全。

    可靠传输:连接管理,确认应答,超时重传,序号/确认序号,校验和
    性能提高:滑动窗口机制,流量控制,快速重传,拥塞机制,延迟应答机制,捎带应答机制。

    面向字节流:

    对数据的收发按照字节为单位的流式传输

    面向字节流的特性:传输比较灵活,发送端可以一点一点发,接收端也可以一点一点收,也可以,在缓冲区足够大的情况下,接收端可以先让数据堆积一会然后,再一个字节一个字节的读取。前提是缓冲区足够大。
    但是有一个缺点就是容易产生粘包问题。

    首先我们对TCP的粘包问题先来了解一下,TCP发送数据比较慢,在客户端和服务端各有一块socket缓冲区(一般建立socket之后会有两个缓冲区,一个是读缓冲区,一个是写缓冲区),然后服务端第一次发送了一个HELLO,第二次发送了一个WORLD,发送的数据会被拷贝到socket缓冲区,但是数据被没有被立即从缓冲区中拿走并且发送,而是停留在缓冲区,原因是要想发送这个数据,就需要TCP对这个数据进行一个封装,一个TCP的头部就有20-60个字节,用来发送5个字节的数据很不划算,所以他就会在缓冲区中等,在等要么缓冲区被写满,要么等待时间到了,就会把缓冲区中的数据统一拿出来再进行发送,这样的话数据被发送出去,等到客户端接到的话就是一条数据HELLOWORLD,但是客户端并不知道其实这个是服务端发送的两条数据,有可能用户应用的时候发生错误。这个是产生粘包的一个原因,是因为一端的缓冲区发送太慢引起的,同理还有一个产生粘包的原因是因为,在接收端的将缓冲区,也不会被立即读走,等到缓冲区满了之后,就会引起多个数据的粘包。

    两个产生可能一个是来不及读引起的,一个是来不及写引起的。所以说TCP粘包既有可能在发送端产生又有可能在接收端。

    那么问题来了TCP为什么会粘包。

    在这个之前我们先来说一下,UDP为什么不会粘包,因为UDP在缓冲区中存数据的时候,每往缓冲区里面放一条数据就会给他封装一个UDP的头部,UDP的头部中又有16位的UDP头长度和数据长度的和,那么等到数据发送过去之后,读方从缓冲区中拿数据的时候,看到长度他就知道这一条数据到底有多长,他就会一次性把一条完整的数据从缓冲区中拿出来。所以UDP不会粘包。

    TCP产生粘包就是内核内核并没有对send要发送的数据进行明确的边界区分。
    那么就需要用户在应用层进行解决。怎么解决粘包问题?

    • 1.在应用层给数据增加特殊字符进行间隔,
    • 2.或者给数据定长,固定每条数据多长
    • 3.不定长数据的应用协议头中定义数据长度(采用UDP的方法)

    TCP连接管理中的保活机制:
    TCP协议栈内部实现保活机制:
    长时间无数据通信,则发送保活探测包;若多次保活探测都无回复;则认为连接断开;

    TCP连接断开的体现:
    recv返回0 ;send触发SIGPIPE异常
    TCP只有断开连接的时候才会返回0,发生错误一般都是返回-1
    close—退出进程—关机(正常关机)
    突然断电就会使得一端断开,这样的话就会使得看另一方的socket无意义,发送 保活探测包之后就会断开连接

    基于TCP协议实现的应用层协议:HTTP/ftp

    UDP如何实现可靠传输:UDP是无法实现应答传输的,但是如果要实现可靠传输的话,就要模仿TCP的各种东西,这些都是需要在应用层实现的。

    了解LINUX下的tcpdump抓包工具

    使用方法为 	sudo tcpdump -i ens33 将流经ens33网卡的包全部抓取
      		sudo tcpdump -i ens33 -w a.pcap  将抓到的包写入一个文件保存
    

    网络基础3:网络层,链路层
    网络层:负责地址管理与路由选择;

    路由选择指的是,在复杂的网络传输环境中,对数据的传输选择一条合适的路径,依赖于地址管理,地址管理不好,就没办法路由。

    主要讲解地址管理。

    主机:个人pc设备

    路由器:路由转发设备

    节点:网络中的任意设备

    如何在LINUX中查询运营商的服务器IP,以QQ为例:ping www.qq.com就可以弹出IP地址

    IP地址不能随意的分配,因为随意分配就有很大的概率造成IP地址的冲突(数据无法发送)
    解决方法:将IP地址的分配规范起来
    每个路由器都能够组建一个局域网,这个局域网就应该具备自己的网络标识符(网络号),这个路由器向自己组建的局域网中的主机分配的IP地址都应该包含这个网络标识;同时为了在局域网中能够唯一的标识一台主机,IP地址中还应该包含主机标识(主机号)
    由此可知IP地址组成::网络号+主机

    IP地址的分配规范起来之后,只需要对网络号的取值进行规范就可以了:相邻的网络不能具备相同的网络号。

    一个路由器上相连的所有网络(一个路由器上可能连着下线路由器)网络号不能相同,因为这些网络都属于相邻网络,数据到了路由器之后,一旦网络号冲突就会导致路由器不知道数据该给谁给
    我们的主机连自己的路由器,自己的路由器又连接运营商的路由器,然后才连接到互联网。
    在这里插入图片描述

    展开全文
  • //这也是说明为什么stSize既是一个输入量也是一个输出量 //释放原来的内存空间 delete pIpAdapterInfo; //重新申请内存空间用来存储所有网卡信息 pIpAdapterInfo = (PIP_ADAPTER_INFO)new BYTE[stSize...
  • 为什么会出现在mounted钩子函数中获取元素失败呢 原因:我们的dom中的数据是通过异步获取到的 而mounted钩子函数只会执行一次并且在他执行的时候数据还没有请求过来 所以获取不到dom 解决方法:我们可以watch与...

    为什么会出现在mounted钩子函数中获取元素失败呢
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    原因:我们的dom中的数据是通过异步获取到的 而mounted钩子函数只会执行一次并且在他执行的时候数据还没有请求过来 所以获取不到dom

    解决方法:我们可以watch与dom相关的数据 这里我们异步获取的数据是list
    在这里插入图片描述
    因为我们watch数据变化后dom不会立即更新 这里需要加上nextTick这样我们就可以获取到dom元素了
    在这里插入图片描述

    **心得:**以后碰到在钩子函数中获取不到dom元素 就应该想到是不是数据是异步获取的 元素还没有渲染出来就执行了钩子函数 然后通过watch数据的方法解决 但是注意要用nextTick 因为数据变化后dom不会立即更新 nextTick之后才会更新

    展开全文
  • 心力交瘁啊,这个错误一般都是数据库和模型不对一产生的问题,但是还是有特殊情况的嘛。 我遇到特殊情况就是...解决方法:重新拉取模型,我不知道为什么会出现这种问题,因为平常很少看警告这里东西,所以下次...
  • 为什么会出现这样的情况呢?这期我们就来看看这个"诡异"的事件。坐好了,准备发车!查看内存使用情况首先想要知道 Redis 内存的使用情况,我们就需要获取相关的信息。Redis 中查看内存相关信息是很简单的,只需要在...
  • 如果MySQL服务器从复制服务器,则无论选择什么备份方法,当备份从机数据时,还应备份master.info和relay-log.info文件。恢复了从机数据后,需要这些文件来继续复制。如果从机执行复制LOAD DATA INFILE命令,你应还...
  • 在一个文本框中输出连接的结果: 得到的数据(8整数)或者 “连接失败” ; 出现的问题: 点击按钮“连接”时,有时出现 窗口暂时不能相应,等连接结果显示在文本框后,窗口就能显示了。程序中没有用线程,比较...
  • 代码为kafka传参后流式运算,获取hive数据,报错的位置代码为 目前来看是读取hive失败,不知道为什么到yarn会出现这中问题
  • 如何获取 UEditor 实例? <vue-ueditor-wrap @ready="ready"></vue-ueditor-wrap> methods: { ready (editorInstance) { console.log(`编辑器实例${editorInstance.key}: `, editorInstance) } } ...
  • 网上其实还有一些其他高效的爬虫工具,那些爬虫工具一般不是模拟人的鼠标和键盘操作而出现的,一般都是获取网站的各种链接然后将有价值的信息存储下来,为什么会用selenium这个库去爬数据呢?原因是有很多网站做了...
  • 1.为什么会报错? 因为当onshow执行时当前组件还未进行挂载,所以导致组件实例获取失败 解决办法 利用$nextTick的特性,在下次dom更新循环结束之后,执行延迟回调。在修改数据之后立即使用这个方法,获得更新后的...
  • 不知道为什么会死锁,理论上用过的之后的Connection在close之后会放回连接池中,而且我插入sql语句时又是单线程的,不知道为什么会死锁 c3p0的配置是 ${jdbc.AppTencentCollection_BC_driverClassName}" />...
  • 《你必须知道的495个C语言问题》

    热门讨论 2010-03-20 16:41:18
    为什么这些问题如此频繁地出现? 60 5.15 有没有什么简单点儿的办法理解所有这些与空指针有关的东西呢? 60 5.16 考虑到有关空指针的所有这些困惑,要求它们的内部表示都必须为0不是更简单吗? 60 5.17 说真的...
  • 3.1.0 在函数内定义一个字符数组,用gets函数输入字符串的时候,如果输入越界,为什么程序崩溃? 3.1.1 C++中引用与指针的区别 3.1.2 C/C++程序的内存分区 3.1.3 快速排序的思想、时间复杂度、实现以及优化方法...
  • 为什么这些问题如此频繁地出现? 60 5.15 有没有什么简单点儿的办法理解所有这些与空指针有关的东西呢? 60 5.16 考虑到有关空指针的所有这些困惑,要求它们的内部表示都必须为0不是更简单吗? 60 5.17 说真的...
  • 7.3.1 一种会失败的常用数据仓库技术 240 7.3.2 解释热表上超出期望的I/O 241 7.4 写一致性 244 7.4.1 一致读和当前读 244 7.4.2 查看重启动 247 7.4.3 为什么重启动对我们很重要? 250 7.5 小结 251 第8章 ...
  • 7.3.1 一种会失败的常用数据仓库技术 229 7.3.2 解释热表上超出期望的I/O 230 7.4 写一致性 233 7.4.1 一致读和当前读 233 7.4.2 查看重启动 235 7.4.3 为什么重启动对我们很重要 238 7.5 小结 239 第8章 ...
  • 为什么这些问题如此频繁地出现? 5.15 有没有什么简单点儿的办法理解所有这些与空指针有关的东西呢? 5.16 考虑到有关空指针的所有这些困惑,要求它们的内部表示都必须为0不是更简单吗? 5.17 说真的,真有机器用...
  • 为什么 runtime 统一,而编程模型不统一呢? 在我看来,这是本末倒置的事情。用户才不管你 runtime 层是否统一,用户更关心的是写一套代码。所以 Table & SQL API 就扛起了统一API的大旗,批上的查询随着输入...
  • 为什么要分库分表(设计高并发系统的时候,数据库层面该如何设计)?用过哪些分库分表中间件?不同的分库分表中间件都有什么优点和缺点?你们具体是如何对数据库如何进行垂直拆分或水平拆分的? 现在有一个未分库...
  • 飞雪桌面日历 7.6绿色

    2014-04-08 18:29:32
    4.快速关机为什么会这么快? 系统正常关机时,首先关闭所有打开的程序,然后保存系统的相关设置再关闭计算机 ,使系统关机会减慢。应该说快速不是正常的关机,它不关闭所的打开的程序,也不保存系统的相关设置,...
  • //通过切割字符串,把所有的非data标志的数据,判定分隔符 string[] one_line; //文本文件中的一行 string badrow=null; //坏行,存在问题的行 /*构建文件流读取器*/ StreamReader sr; FileStream fs ; try...
  • 发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。例如全局的配置信息,服务式服务框架的服务地址列表等就非常适合使用。 ...

空空如也

空空如也

1 2 3 4 5
收藏数 90
精华内容 36
关键字:

为什么会出现获取数据失败