精华内容
下载资源
问答
  • 简述网络连接过程

    千次阅读 2018-09-27 14:15:58
    当我们打开浏览器,输入网址(如:www.baidu.com),到浏览器显示主页面时,这个过程到底发生了什么。 先来简单说一下,当我们输入网址(也即是url或者说是域名)时,浏览器先对www.baidu.com通过dns协议进行解析...

    简述:

    当我们打开浏览器,输入网址(如:www.baidu.com),到浏览器显示主页面时,这个过程到底发生了什么。

    先来简单说一下,当我们输入网址(也即是url或者说是域名)时,浏览器先对www.baidu.com通过dns协议进行解析,查询到对应的ip地址。然后使用http协议访问web服务器,通过交换机,路由器,进入互联网中,根据对应的ip地址,找到所对应的服务器,通过服务器的防火墙,进入服务器中,获取对应的网页,再返回浏览器,显示获取到的网页数据。

     

    信息加工阶段

    这个阶段发生在本地主机中

    数据传输方式,通过tcp协议udp协议传输的。

    他们的区别是,tcp是通过建立连接,再传输数据,

    而udp则是,直接发送数据(通常用于发出数据不用返回数据的情况,如视频,音频等)。

    而他们具体通过的方式是:

    将信息分成一个个报文(message),加上头部信息(通常为数据的标识号:为了保证数据不被丢失,以及要传输的ip地址)。再放入一些被称为包(packet)的容器中。再通过交换机路由器进行传输。

     

    数据传输包装的过程中,起到作用的是协议栈(也叫网络控制软件),这个软件将路由器接收到的消息打包,加上ip地址等控制信息。同时它也有其他的作用,如通信发生错误时,重新发送包,或者调节数据发送的速率。

    接下来协议栈会将包交给网卡负责网络通信的硬件),网卡会将包(电脑中所有信息都是01组成的)转换为电信号,并通过网线发送出去。这也是为什么我们可以通过电话线连接网络的原因。

     

    网络传递阶段

    接下来包会通过交换机,到达接入互联网的路由器。进入到互联网的入口线路被称为接入网。通常我们可以通过电话线,光纤,专线等通信线路接入互联网,这些通信线路统称为接入网。接入网连接到你的网络运营商,并接入被称为接入点(pop)的设备。并通过运营商的路由器进入到主干网,最终到达ip地址所指定的web服务器所在的局域网中。

    在这个过程中传输的包会记录经过的设备的ip地址,并增加到头部的信息中。用于获取到网页信息后原址返回。

    在进入指定的web服务器前,服务器的防火墙会对包进行检查。然后可能会进入缓存服务器(存放一些可以重复利用的数据),如果在缓存服务器中能找到指定的页面,则立即返回。

    否则进入web服务器,查找所需的网页数据,没有则返回404。

    在这个过程中,包通过协议栈,由光信号转化并还原为原始请求信息,然后交给web服务器程序。

    展开全文
  • 经过前面这些过程网络连接所需要的条件就全部准备就绪,接下来就是等待网络接入。  我们把网络接入过程简单分为三个阶段:  触发阶段  ----该阶段是由各种不同事件触发的,比如SIM载入完毕、PS域Attach成功、...
            经过前面这些过程,网络连接所需要的条件就全部准备就绪,接下来就是等待网络接入。
            我们把网络接入过程简单分为三个阶段:
            触发阶段
                ----该阶段是由各种不同事件触发的,比如SIM载入完毕PS域Attach成功通话结束APN改变等,该阶段的最终都是要调用setupDataOnConnectableApns()方法;
            准备连接阶段
                ----该阶段是指,在DcTracker收到建立连接的请求之后,需要进行一系列有效性检测,比如APN是否已经激活PS是否已经就绪用户是否打开网络开关等,然后创建DataConnection()对象,准备发起连接请求;
            发送连接命令阶段

                ----该阶段是指,在DataConnection收到DcTracker的请求之后,将请求转交给RILJ的过程,经过该阶段后,请求就发送到了RIL以及Modem层,由底层完成信令的发送和接收;


    一、触发阶段


            有多种事件可以触发网络接入过程,具体来说分为以下几个原因:
            //漫游相关
            static final String REASON_ROAMING_ON = "roamingOn";
            static final String REASON_ROAMING_OFF = "roamingOff";
            //PS attach
            static final String REASON_DATA_ATTACHED = "dataAttached";
            //APN改变
            static final String REASON_APN_CHANGED = "apnChanged";
            //通话结束
            static final String REASON_VOICE_CALL_ENDED = "2GVoiceCallEnded";
            //SIM载入完毕
            static final String REASON_SIM_LOADED = "simLoaded";
            //网络模式改变
            static final String REASON_NW_TYPE_CHANGED = "nwTypeChanged";
            我们挑选最常见的REASON_DATA_ATTACHED原因来分析网络连接的发起过程。
            什么是Data Attach事件呢?其实就是手机的PS域Attach成功,而PS域是和CS域相对应,简单来说,手机(2G/3G)打电话是在CS域上进行,而数据流量是在PS域上进行,PS域附着(Attach)成功之后才可以发起数据激活的信令,然后才可以上网,默认状态下,手机开机后就会发起PS附着的信令给网络,附着成功之后将会接到成功的消息,由于DcTracker当初初始化时在registerForAllEvents()中注册了Attach的监听器:
            protected void registerForAllEvents() {
                //监听是否PS域Attach状态
                mPhone.getServiceStateTracker().registerForDataConnectionAttached(this, DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
            }
            因此当PS域Attach成功之后,将会收到EVENT_DATA_CONNECTION_ATTACHED消息:
            public void handleMessage (Message msg) {
                switch (msg.what) {
                    case DctConstants.EVENT_DATA_CONNECTION_ATTACHED:
                        onDataConnectionAttached();
                        break;
                }
            }
            继续:
            private void onDataConnectionAttached() {
                mAttached.set(true);
                if (getOverallState() == DctConstants.State.CONNECTED) {
                    //已经处于连接状态
                    startNetStatPoll();
                    startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
                    notifyDataConnection(Phone.REASON_DATA_ATTACHED);
                } else {
                    //初始化后应该处于IDLE状态
                    notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED);
                }
                if (mAutoAttachOnCreationConfig) {
                    mAutoAttachOnCreation = true;
                }
                setupDataOnConnectableApns(Phone.REASON_DATA_ATTACHED);
            }
            当前的连接状态是IDLE,因此会将REASON_DATA_ATTACHED的参数传递给notifyOffApnsOfAvailability(),里面也没什么内容,我们直接来看setupDataOnConnectableApns()的操作。
            setupDataOnConnectableApns()就是发起数据连接时使用的方法,调用该方法时需要传递一个原因的参数,该参数标明当前建立数据连接的原因(其实目前也没有发现该参数的具体作用)。
            其他诸如SIM载入完毕、通话结束等触发事件也都是通过该方法发起数据连接请求的。


    二、准备连接阶段


            当由于种种原因触发网络连接请求时,就会调用到setupDataOnConnectableApns()方法,并传递各种原因的参数,接下来就需要准备APN并进行各种有效性检测,并最终将请求转交给DataConnection来处理。
            private void setupDataOnConnectableApns(String reason) {
                //对于当前环境来说,reason为REASON_DATA_ATTACHED
                for (ApnContext apnContext : mPrioritySortedApnContexts) {
                    if (apnContext.getState() == DctConstants.State.FAILED) {
                        apnContext.setState(DctConstants.State.IDLE);
                    }
                    if (apnContext.isConnectable()) {
                        apnContext.setReason(reason);
                        trySetupData(apnContext);
                    }
                }
            }
            在这个方法里面,遍历当前所有的APN参数(mPrioritySortedApnContexts其实等同于mApnContexts,只不过是按照优先级顺序排列了一下而已,项都是一样的),找到可用的APN参数,然后通过trySetupData()发起连接请求,我们来看一下这里如何判断一个APN是否可用,也就是看一下isConnectable()的判断条件:
            @ApnContext.java
            public boolean isConnectable() {
                return isReady() && ((mState == DctConstants.State.IDLE)
                        || (mState == DctConstants.State.SCANNING)
                        || (mState == DctConstants.State.RETRYING)
                        || (mState == DctConstants.State.FAILED));
            }
            前面我们说过,激活APN的时候,其实就是让isReady()的判断通过,而mState初始值也是IDLE,因此刚才我们激活的APN参数此时就被挑选出来,并进行trySetupData()的操作:
            @DcTracker.java
            private boolean trySetupData(ApnContext apnContext) {
                boolean isEmergencyApn = apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY);
                boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
                boolean checkUserDataEnabled = !(apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS));
                if (apnContext.isConnectable() && (isEmergencyApn || (isDataAllowed(apnContext) && getAnyDataEnabled(checkUserDataEnabled) && !isEmergency()))) {
                    int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
                    if (apnContext.getState() == DctConstants.State.IDLE) {
                        ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
                        if (waitingApns.isEmpty()) {
                            notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext);
                            notifyOffApnsOfAvailability(apnContext.getReason());
                            return false;
                        } else {
                            apnContext.setWaitingApns(waitingApns);
                        }
                    }
                    //建立连接
                    boolean retValue = setupData(apnContext, radioTech);
                    notifyOffApnsOfAvailability(apnContext.getReason());
                    return retValue;
                } else {
                }
            }
            这里主要经历了有效性的检查,其中判断了四个情况:
            apnContext.isConnectable()
                ----判断当前APN是否已经被激活;
            isEmergencyApn()
                ----当前APN是否为紧急APN;
            isDataAllowed()
                ----判断是否已经ATTACH成功,SIM是否初始化完毕,当前手机服务是否支持,漫游下是否允许上网等;
            getAnyDataEnabled()
                ----该条件主要判断用户是否打开了数据开关;
            这四个条件我们主要来看最后一个,他里面包含用户数据开关的判断,我们来看一下详情,其中传递的参数checkUserDataEnabled受4G IMS的影响,由于国内暂时都没有部署IMS,因此这里的参数肯定都为true:
            public boolean getAnyDataEnabled(boolean checkUserDataEnabled) {
                synchronized (mDataEnabledLock) {
                    if (!(mInternalDataEnabled && (!checkUserDataEnabled || mUserDataEnabled) && (!checkUserDataEnabled || sPolicyDataEnabled)))
                        return false;
    
    
                    for (ApnContext apnContext : mApnContexts.values()) {
                        if (isDataAllowed(apnContext)) {
                            return true;
                        }
                    }
                    return false;
                }
            }
            这里的判断中最重要的就是mUserDataEnabled(),他的来源:
                mUserDataEnabled = Settings.Global.getInt( mPhone.getContext().getContentResolver(), Settings.Global.MOBILE_DATA, 1) == 1;
            这说明他来自于Settings.Global.MOBILE_DATA这个属性值,而这个属性值恰恰就是当用户打开或关闭移动网络时所改变的属性值,当用户打开数据网络时,该值为1,关闭网络时,该值就是0
            回到trySetupData()中来,当前所选取的APN是可用的,而且不是紧急APN,同时假设用户打开了数据流量开关,那么在该方法中就会通过setupData的方法来发送数据连接的请求:
            private boolean setupData(ApnContext apnContext, int radioTech) {
                ApnSetting apnSetting;
                DcAsyncChannel dcac = null;
                apnSetting = apnContext.getNextWaitingApn();
                int profileId = apnSetting.profileId;
                if (profileId == 0) {
                    profileId = getApnProfileID(apnContext.getApnType());
                }
    
    
                if (dcac == null) {
                    //创建DcAsyncChannel
                    dcac = findFreeDataConnection();
                    if (dcac == null) {
                        dcac = createDataConnection();
                    }
                    if (dcac == null) {
                        return false;
                    }
                }
    
    
                apnContext.setDataConnectionAc(dcac);
                apnContext.setApnSetting(apnSetting);
                apnContext.setState(DctConstants.State.CONNECTING);
                mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
    
    
                //通过DcAsyncChannel发起连接请求
                Message msg = obtainMessage();
                msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
                msg.obj = apnContext;
                dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation, msg);
    
    
                return true;
            }
            这个方法内部主要完成了两个任务:
            1、更新当前APN参数的状态并把状态发送到系统中(还是通过notifyDataConnection()来完成);
            2、通过DcAsyncChannel的bringUp()方法发起连接请求;
            我们主要分析第二个任务。
            这里显示通过findFreeDataConnection()方法搜索可用的DcAsyncChannel,找不到的话就通过createDataConnection()创建,我们由于第一次上网,因此就需要创建的过程:
            private DcAsyncChannel createDataConnection() {
                int id = mUniqueIdGenerator.getAndIncrement();
                DataConnection conn = DataConnection.makeDataConnection(mPhone, id, this, mDcTesterFailBringUpAll, mDcc);
                mDataConnections.put(id, conn);
                //创建DcAsyncChannel通道
                DcAsyncChannel dcac = new DcAsyncChannel(conn, LOG_TAG);
                //申请双向连接
                int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
                if (status == AsyncChannel.STATUS_SUCCESSFUL) {
                    mDataConnectionAcHashMap.put(dcac.getDataConnectionIdSync(), dcac);
                } else {
                }
                return dcac;
            }
            这里我们主要完成四个步骤:
            1、拿到了一个DataConnection对象;
            2、创建了DcAsyncChannel对象;
            3、通过fullyConnectSync对DataConnection发起双向连接请求;
            4、将DcAsyncChannel返回出来;
            DcAsyncChannel的属性其实是AsyncChannel:
            public class DcAsyncChannel extends AsyncChannel {}
            根据《AsyncChannel的使用和原理》中的介绍,通过fullyConnectSync()可以一次性申请到双向的AsyncChannel,对于当前环境来说,就是在DcTracker与DataConnection之间建立了双向的连接通道,而且把通道的地址传递出来,而在setupData()中我们看到,DcTracker拿到该通道后,就进行了bringUp()的调用;
            private boolean setupData(ApnContext apnContext, int radioTech) {
                DcAsyncChannel dcac = null;
                //通过DcAsyncChannel发起连接请求
                Message msg = obtainMessage();
                msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
                msg.obj = apnContext;
                dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation, msg);
                return true;
            }
            而且在调用bringUp时传递了一个Message的参数,该参数中携带了两个参数,EVENT_DATA_SETUP_COMPLETE的消息和APN的内容。
            然后我们来看bringUp:
            @DcAsyncChannel.java
            public void bringUp(ApnContext apnContext, int initialMaxRetry, int profileId, int rilRadioTechnology, boolean retryWhenSSChange, Message onCompletedMsg) {
                sendMessage(DataConnection.EVENT_CONNECT, new ConnectionParams(apnContext, initialMaxRetry, profileId, rilRadioTechnology, retryWhenSSChange, onCompletedMsg));
            }
            这里就是将刚才的两个参数封装后通过sendMessage()发送出去,那么这个消息是发送给谁了呢?

            其实就是发送给DcAsyncChannel通道的另一端DataConnection了,那么DataConnection是如何处理该消息的呢?


    三、发送连接命令阶段


            当消息流进入DataConnection时,网络请求的过程在上层也就进入了最后的发送阶段,接下来我们就来看这最后的阶段如何完成。
            DataConnection是在刚才的createDataConnection()方法中通过makeDataConnection()方法创建的:
            @DataConnection.java
            static DataConnection makeDataConnection(PhoneBase phone, int id, DcTrackerBase dct, DcTesterFailBringUpAll failBringUpAll, DcController dcc) {
                //创建DataConnection方法
                DataConnection dc = new DataConnection(phone, "DC-" + mInstanceNumber.incrementAndGet(), id, dct, failBringUpAll, dcc);
                dc.start();
                return dc;
            }
            先来看该类的属性:
            public final class DataConnection extends StateMachine {}
            然后来看该对象的构造方法:
            private DataConnection(PhoneBase phone, String name, int id, DcTrackerBase dct, DcTesterFailBringUpAll failBringUpAll, DcController dcc) {
                super(name, dcc.getHandler());
                mPhone = phone;
                mDct = dct;
                mDcTesterFailBringUpAll = failBringUpAll;
                mDcController = dcc;
                mId = id;
                mCid = -1;
                mDcRetryAlarmController = new DcRetryAlarmController(mPhone, this);
                ServiceState ss = mPhone.getServiceState();
                mRilRat = ss.getRilDataRadioTechnology();
                mDataRegState = mPhone.getServiceState().getDataRegState();
                int networkType = ss.getDataNetworkType();
                mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_MOBILE, networkType, NETWORK_TYPE, TelephonyManager.getNetworkTypeName(networkType));
                mNetworkInfo.setRoaming(ss.getRoaming());
                mNetworkInfo.setIsAvailable(true);
    
    
                //各种状态机初始化
                addState(mDefaultState);
                addState(mInactiveState, mDefaultState);
                addState(mActivatingState, mDefaultState);
                addState(mRetryingState, mDefaultState);
                addState(mActiveState, mDefaultState);
                addState(mDisconnectingState, mDefaultState);
                addState(mDisconnectingErrorCreatingConnection, mDefaultState);
                //默认状态为DcInactiveState
                setInitialState(mInactiveState);
    
    
                mApnContexts = new ArrayList<ApnContext>();
            }
            从他的属性和构造方法可以看到,该类其实是一个状态机,内部定义了七种状态,其中默认状态为DcInactiveState。这些状态分别代表了一个连接从非激活状态到激活状态再到断开状态所经历的过程。
            我们只需知道当前初始状态为DcInactiveState即可。
            前面讲到,DcTracker在准备完所有APN和条件后,会把APN参数等信息打包到EVENT_CONNECT消息中,通过DcAsyncChannel发送给DataConnection,接下来我们看DataConnection对该消息的处理。
            由于当前的DataConnection处于DcInactiveState状态机中,因此将会在该状态机中处理EVENT_CONNECT的消息:
            private class DcInactiveState extends State {
                public boolean processMessage(Message msg) {
                    switch (msg.what) {
                        case EVENT_CONNECT:
                            ConnectionParams cp = (ConnectionParams) msg.obj;
                            //初始化连接环境
                            if (initConnection(cp)) {
                                //发起连接请求
                                onConnect(mConnectionParams);
                                //进入正在激活状态
                                transitionTo(mActivatingState);
                            } else {
                                notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER, false);
                            }
                            retVal = HANDLED;
                            break;
                    }
                    return retVal;
                }
            }
            在这个方法里面我们看到,其先将EVENT_CONNECT中打包的参数在initConnection中解压出来,然后就通过onConnect()方法发起连接请求,再然后就进入DcActivatingState的状态。
            我们主要来看如何通过onConnect()方法发起连接请求:
            private void onConnect(ConnectionParams cp) {
                //向Modem注册的回调消息
                Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
                msg.obj = cp;
                //向RIL发起连接请求
                mPhone.mCi.setupDataCall(
                        Integer.toString(cp.mRilRat + 2),
                        Integer.toString(cp.mProfileId),
                        mApnSetting.apn, mApnSetting.user, mApnSetting.password,
                        Integer.toString(authType),
                        protocol, msg);
            }
            到这里我们终于看到与Modem的交互了,其实发起数据连接的最终都是通过RILJ的setupDataCall的接口来实现的,该接口传递了一些必要的连接参数,包括:当前的接入技术、APN的优先级、APN的参数、以及回调消息。
            至此,该请求就由RILJ交给Modem然后通过射频向运营商发送。
            下面是该过程的流程图:
        
    展开全文
  • netstat -natp 查看网络连接和占用的端口 tcpdump -nn -i eth0 port 9090 开监听抓取数据包 lsof -p <进程号>查看某个进程已经打开的文件状态 Socket 服务端代码 package com.bjmashibing.system.io; import ...

    Socket的连接过程、TCP的一些参数

    前置知识

    用到的命令
    netstat -natp 查看网络连接和占用的端口
    tcpdump -nn -i eth0 port 9090 开监听抓取数据包
    lsof -p <进程号>查看某个进程已经打开的文件状态

    Socket

    服务端代码

    package com.bjmashibing.system.io;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.InetSocketAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class SocketIOPropertites {
        //server socket listen property: 这些配置不是JVM层级的,是关联到内核的TCP协议栈的一些选项参数。
        private static final int RECEIVE_BUFFER = 10;
        private static final int SO_TIMEOUT = 0;  // 服务端的超时时间
        private static final boolean REUSE_ADDR = false;
        private static final int BACK_LOG = 2; // 多少个连接可以被积压
        //client socket listen property on server endpoint:
        private static final boolean CLI_KEEPALIVE = false;
        private static final boolean CLI_OOB = false;
        private static final int CLI_REC_BUF = 20;
        private static final boolean CLI_REUSE_ADDR = false;
        private static final int CLI_SEND_BUF = 20;
        private static final boolean CLI_LINGER = true;
        private static final int CLI_LINGER_N = 0;
        private static final int CLI_TIMEOUT = 0;  // 客户端的超时时间
        private static final boolean CLI_NO_DELAY = false;
    /*
    
        StandardSocketOptions.TCP_NODELAY
        StandardSocketOptions.SO_KEEPALIVE
        StandardSocketOptions.SO_LINGER
        StandardSocketOptions.SO_RCVBUF
        StandardSocketOptions.SO_SNDBUF
        StandardSocketOptions.SO_REUSEADDR
     */
        public static void main(String[] args) {
    
            ServerSocket server = null;
            try {
                server = new ServerSocket();
                server.bind(new InetSocketAddress(9090), BACK_LOG);
                server.setReceiveBufferSize(RECEIVE_BUFFER);
                server.setReuseAddress(REUSE_ADDR);
                server.setSoTimeout(SO_TIMEOUT);
    
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("server up use 9090!");
            try {
                while (true) {
                    // System.in.read();  //分水岭:
                    Socket client = server.accept();  //阻塞的,没有 -1  一直卡着不动  accept(4,
                    System.out.println("client port: " + client.getPort());
                    client.setKeepAlive(CLI_KEEPALIVE);
                    client.setOOBInline(CLI_OOB);
                    client.setReceiveBufferSize(CLI_REC_BUF);
                    client.setReuseAddress(CLI_REUSE_ADDR);
                    client.setSendBufferSize(CLI_SEND_BUF);
                    client.setSoLinger(CLI_LINGER, CLI_LINGER_N);
                    client.setSoTimeout(CLI_TIMEOUT);
                    client.setTcpNoDelay(CLI_NO_DELAY);
                    //client.read   //阻塞   没有  -1 0
                    new Thread(
                            () -> {
                                try {
                                    InputStream in = client.getInputStream();
                                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                                    char[] data = new char[1024];
                                    while (true) {
    
                                        int num = reader.read(data);
    
                                        if (num > 0) {
                                            System.out.println("client read some data is :" + num + " val :" + new String(data, 0, num));
                                        } else if (num == 0) {
                                            System.out.println("client readed nothing!");
                                            continue;
                                        } else {
                                            System.out.println("client readed -1...");
                                            System.in.read();
                                            client.close();
                                            break;
                                        }
                                    }
    
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                    ).start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    server.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    客户端代码

    package com.bjmashibing.system.io;
    
    import java.io.*;
    import java.net.Socket;
    
    public class SocketClient {
    
        public static void main(String[] args) {
            try {
                Socket client = new Socket("192.168.150.11",9090);
    
                client.setSendBufferSize(20);
                client.setTcpNoDelay(true);  // 如果数据量比较小,会不会积攒起来再发,默认是true
                client.setOOBInLine(true);
                OutputStream out = client.getOutputStream();
    
                InputStream in = System.in;
                BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    
                while(true){
                    String line = reader.readLine();
                    if(line != null ){
                        byte[] bb = line.getBytes();
                        for (byte b : bb) {
                            out.write(b);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    

    下面详细跟踪建立连接的过程

    启动服务端
    在这里插入图片描述
    开启服务端后,出现了一个对于 9090 的 listen 状态。
    TCP 三次握手是走 listen 的,建立连接之后,后面走文件描述符,那就是另外一个环节了,我们后面再讲。
    在这里插入图片描述
    使用jps得到服务端的进程id号:7932
    在这里插入图片描述
    使用lsof -p 7932查看7932端口的文件描述符的分配情况。
    在这里插入图片描述
    启动客户端
    客户端启动,进入代码的阻塞等待用户输入逻辑
    在这里插入图片描述
    在服务端抓到了三次握手的包
    在这里插入图片描述
    在服务端看到建立了连接,虽然连接还未被使用。
    在这里插入图片描述
    在客户端进行用户输入之后(服务端也有的阻塞的逻辑,需要回车才能接收client的数据)
    在这里插入图片描述
    继续查看服务端抓包监听
    在这里插入图片描述
    查看服务端的连接状态:双方开辟了资源。即便你程序不要我,我也在内核里有资源用来接收或者等待一类的。
    在这里插入图片描述
    服务端输入回车之后
    接受到了客户端发过来的数据
    在这里插入图片描述
    刚才的socket连接已经被分配给7932了
    在这里插入图片描述
    lsof 得到了新的文件描述符 6
    在这里插入图片描述

    总结一下

    TCP:面向连接的,可靠的传输协议
    在这里插入图片描述
    Socket:是一个四元组。ip:port ip:port四元组的任何一个元的不同,都可以区分不同的连接。

    面试题 1:服务端80端口接收客户端连接之后,是否需要为客户端的连接分配一个随机端口号?
    :不需要。

    面试题 2:现在,有一个客户端,有一个服务端,
    客户端的ip地址是AIP,程序使用端口号CPORT想要建立连接。
    服务端的IP地址是XIP,端口号是XPORT。
    现在假设某一个客户端A开了很多连接占满了自己的65535个端口号,那客户端A是否还能与另一个服务端建立建立连接?
    :可以,因为只要能保证四元组唯一即可
    在这里插入图片描述
    注:一台服务器是可以与超过65535个客户端保持长连接的,调优到超过百万连接都没问题,只要四元组唯一就可以了。客户端来了之后,服务端是不需要单独给它开辟一个端口号的。

    下面这个图可以说明,无论再多的连接,服务端始终是使用的同一个<ip:端口>
    在这里插入图片描述

    那么,我们常见的报错“端口号被占用”是什么原因?

    我们常见的报错“端口号被占用”实际上是在启动SocketSocket的时候,而不是Socket,两者不是一个概念。如果两个服务使用了相同的端口号,这时如果来了一个数据包,内核无法区分是哪一个服务在LISTEN,不知道要发给哪一个服务了,如下图例子
    在这里插入图片描述
    在这里插入图片描述

    每一个独立的进程只要维护它自己的文件描述符唯一即可。

    在这里插入图片描述

    keepalive

    三个不同层级的 keepalive

    • TCP协议中规定,如果双方建立的连接(虚无的,并不是物理的连接),如果双方很久都不说话,你能确定对方还活着吗?不能,因为可能突然断电。所以规定了这么一种机制,哪怕是周期性的消耗一些网络资源,也要及时把无效的连接踢掉,节省内存。
    • HTTP级别
    • 负载均衡keepalived

    网络IO的变化 演进模型(BIO)

    一句话概括BIO?

    BIO就是,客户端来一个连接,抛出一个线程,来一个连接,抛出一个线程…

    几个维度

    同步、异步、阻塞、非阻塞

    用到的命令:

    strace -ff -o out /usr/java TestSocket
    用来追踪Java程序和内核进行了哪些交互(进行了哪些系统调用)

    详细追踪 BIO 的连接过程

    TestSocket.java
    在这里插入图片描述
    在这里插入图片描述
    用JDK1.4跑起来
    在这里插入图片描述

    在服务端用jps找到进程的id号是8384
    在这里插入图片描述
    在服务端使用tail监控out.8384文件的输出(8384是main线程的输出,其他的out可能是一些垃圾回收线程等其他线程的输出)
    (这里注意一下一共有8个线程,待会儿建立连接之后再看)
    在这里插入图片描述
    可以看到JVM用到了内核系统调用的accept,main线程正在阻塞
    在这里插入图片描述
    在一个客户端上建立一个连接
    在这里插入图片描述
    在服务端我们看到,刚才阻塞 accept(3, 的位置继续执行。34178是客户端连接进来的随机端口号,192.1618.150.12是来自于客户端的ip地址
    在这里插入图片描述
    clone是linux的一个系统调用。Java当中的一个线程,就是操作系统的一个子线程。下图我们看到,(客户端连接进来之后),服务端调用clone函数,开启了一个线程号为8447的新线程。flags里面记录的是子线程共享的文件系统、打开的文件等父线程的系统资源。
    下面又开始阻塞的accept
    在这里插入图片描述
    查看用strace输出的out文件,也可以证明8447这个新线程的存在。
    在这里插入图片描述
    在服务端可以看到,多了一个文件描述符5,表示的是从node01(服务端机器名称)node02(客户端机器名称)的已连通的状态(socket四元组)
    在这里插入图片描述
    服务端 8447.out 正在recv阻塞接收
    在这里插入图片描述

    想学好Linux,去学习文档中这些man帮助手册,有时候比网络上的博客文章更准确(也可以 man man 查看帮助文档本身的帮助文档)
    使用man 2 socket,你会发现所谓socket系统调用,其实就是调用了一个有返回值(文件描述符)的函数(用于LISTEN)
    在这里插入图片描述

    稍稍总结一下

    BIO 模型的整个连接过程

    无论哪种IO模型,application想要和外界通信,都要进行上面所展示的一系列的(3步)系统调用,都是不可缺少的。
    之后服务端进入阻塞状态accept(3,等待客户端的连接。此次阻塞被成功地连接之后,又进入一的新的阻塞,等待新的客户端连接。
    一旦连接成功之后,会为这个连接抛出去一个新的线程,新的线程中又进入一个阻塞状态recv(5,等待接收消息。
    在这里插入图片描述

    展开全文
  • LTE学习笔记--LTE无线连接过程--网络接入过程

    万次阅读 多人点赞 2018-03-19 14:09:17
    系统信息广播由网络实现操作,周期性放入发送,UE开机后,会接收到由eNode B通过物理广播信道(PBCH)和物理性下行控制信道(PDCCH)发送的基站指示信息。包括本小区的物理随机接入信道(PRACH)配置索引、逻辑根...

    1,检测系统信息广播

    系统信息广播由网络实现操作,周期性放入发送,UE开机后,会接收到由eNode B通过物理广播信道(PBCH)和物理性下行控制信道(PDCCH)发送的基站指示信息。包括本小区的物理随机接入信道(PRACH)配置索引、逻辑根序列初始值、循环移位索引、上下行配置索引等于随机接入有关的参数。
    UE通过这些指示信息生成要发送给eNode B的随机接入前导序号。

    2,小区搜索过程

    UE要接入LTE网络,必须经过小区搜索、获取小区系统信息、随机接入等过程。小区搜索的主要目的:1)与小区取得频率和符号同步;2)获取系统帧timing,即下行帧的起始位置;3)确定小区的PCI(Physical-layer Cell Identity)。
    UE不仅需要在开机时进行小区搜索,为了支持移动性(mobility),UE会不停地搜索邻居小区、取得同步并估计该小区信号的接收质量,从而决定是否进行切换(handover,当UE处于RRC_CONNECTED态)或小区重选(cell re-selection,当UE处于RRC_IDLE态)。
    小区搜索过程:
    1.主同步信号,UE可以获得5ms的基准时间
    2.辅同步信号,UE可以获得帧同步和物理层的小区组
    3.下行参考信号(Reference Signal),UE可以获得物理层的小区id
    4.UE获得物理层小区id和帧同步后,UE就可以在BCH上读取系统消息,用于获取其它小区信息。
    以下是FDD和TDD主同步信号(PSS)和从同步信号在数据帧中的位置。


    这里写图片描述

    UE首先是解析出PSS信号。再通过PSS信号推算出SSS信号放入可能位置。然后尝试解析出SSS信号。以下是小区搜索过程的流程。

    这里写图片描述

    UE在事先不知道小区信息的情况下搜索小区,需要经过时隙同步、帧同步、捕获主扰码三个步骤。这三个步骤涉及到四个下行物理信道:主同步信道(P-SCH)、从同步信道(S-SCH)、主公共导频信道(P-CPICH)、主公共控制物理信道(P-CCPCH)。这里是在每个时隙10ms帧内发送两次PSCH和SSCH,先通过检测PSCH获得5ms时钟,然后检测SSCH,获得无线帧时钟,小区ID组和BCH天线配置信息。具体小区ID是通过检测下行参考信号得到的。这一步是在获得了帧同步之后进行。
    如果终端上已经存有某个小区的信息,如频率、主扰码等,那么终端可以利用这些信息来简化小区搜索过程,其搜索过程仍大致需要遵循这三个步骤。
    1)时隙同步:主同步信道(P-SCH)、从同步信道(S-SCH)、主公共导频信道(P-CPICH)、主公共控制物理信道(P-CCPCH)之间是同步的。先要获取各时隙的边界,从而与各物理信道实现时隙同步。这一步是通过捕获主同步信道来实现的。主同步信道不属于码信道,没有经过扩频和加扰处理。主同步信道在每个时隙的起始处重复发送主同步码,所有小区的主同步码相同,且终端预先知道其码片序列。捕获到该主同步码(PSC)确定各物理信道的时隙边界。主同步码的传送周期是5ms,
    2)帧同步:通过捕获从同步信道来实现的。下行扰码又分为主扰码和从扰码,其中主扰码有 512个,分为64组,每组8个。因此,在第二步实现物理信道的帧同步的同时,终端可以获悉该小区的无线帧中使用的从同步码字组合,从而可以确定该小区使用的主扰码所属的组别。
    3)捕获主扰码:通过前两步,终端能够同步到主公共导频信道的无线帧。第二步已经确定该主扰码所属的组号,因此,只需要定位到该主扰码组,然后从个主扰码中找到与本小区匹配的主扰码,捕获主扰码的工作即告结束。然后,就可以用主扰码解码主公共控制物理信道,从而解调出系统下发的广播消息,通过读取BCH获得小区的其他系统信息。
    在小区搜索的时候,搜索的次序是同频小区、异频小区、然后找不同系统之间的小区。在经过前面的小区搜索过程后,终端仍需判定该小区的信号质量是否达到一定的要求,才能进一步确定是否可以驻留在该小区。

    3,初始接入

    为了注册到网络,终端还需要经过初始接入过程,首先进行接入等级的判定,目的在于在一些特定条件下动态阻止或限制网络的接入,例如网络严重超负荷和一些紧急条件下。当用户端符合接入等级时,通过随机接入过程,使得终端能够与网络建立一个RCC连接。之后可以在上行共享控制信道上发消息。

    4,随机接入

    4.1,RA的触发条件和发起方式

    RA过程是UE与eNode B的RRC层通过一组消息进行交互的过程,通过此类过程实现RRC对整个协议栈的管理和控制。随机接入过程(RA)的大致过程都分为以下几步。


    这里写图片描述

    触发随机接入过程过程的事件如下:
    ①UE处于IDLE态,初始接入:当UE开机之后或者处于IDLE的时候,为了接入网络,UE搜网和接收网BCH之后,就可以发起RACH。
    ②UE重建过程:当UE处于连接态时,由于无线环境的变化,UE掉链进入重建过程
    ③UE切换:当UE处于连接状态时,由于移动,UE需要从一个小区切换到另一个小区
    ④UE处于连接状态,上行数据到达:当UE在链接态时,由于上行失步,这时UE有数据需要发送,此时需要发送RACH,先进行上行同步
    ⑤UE处于连接状态,下行数据到达:UE处于连接状态时,由于上行失步,当时eNB 还有下行数据需要发送,可以通过发送PDCCH order的方法快速回复上行同步。
    ⑥UE处于连接状态,UE定位:需要UE做定位
    RRC接入包括两种不同的模式:基于竞争的随机接入和非基于竞争的随机接入。不同的事件触发不同的接入过程:
    触发基于竞争的随机接入过程(CR-RA)的事件有:初始接入,重建立,切换下行数据到达,上行数据到达。触发基于非竞争的随机接入过程(NCR-RA)的时间有:切换,下行数据到达,和UE定位。从上面可以发现,切换和下行数据到达的随机接入过程两种模式均可。
    UE发起随机接入需要使用preamble码,每个小区有64个可用的Preamble码,eNB通过广播系统消息SIB2通知所有的UE,告诉UE 64个Preamble的具体内容(包括Preamble本身和其发送的可能时频资源)。UE会选择其中一个(当然也可能eNB指定)。发送Preamble意味着RA过程的开始。触发UE发起RA过程(也就是发送preamble码)的方式有以下3种:(1)PDCCH order 触发:eNodeB 通过特殊的 DCI format 1A 告诉 UE 需要重新发起随机接入,并告诉 UE 应该使用的 Preamble Index 和 PRACH Mask Index;(2)MAC 子层触发:UE 自己选择 preamble 发起随机接入;(3)上层触发:如初始接入,RRC 连接重建,handover 等。
    UE 发送preamble 给 eNB,实际上是暗示eNB有一个随机接入请求(MSG3),同时通过preamble的接收,eNB 能估计其与 UE 之间的传输时延并以此校准上行 timing。UE 发送 preamble,需要一下几个步骤:(详细内容参见PHY.PRACH相关章节)
    (1)选择 preamble index;
    (2)选择用于发送 preamble 的PRACH 资源;
    (3)确定对应的 RA-RNTI;
    (4)确定目标接收功率PREAMBLE_RECEIVED_TARGET_POWER。
    每个preamble在频域上占用6个连续RB的带宽,这正好等于LTE支持的最小上行带宽。因此,不管小区的传输带宽有多大,都可以使用相同的RA preamble结构。 preamble在时域上的长度取决于配置。

    4.2,RA Preamble的选取

    每个cell下的64个Preamble可以分成三个部分:PreambleGoupA,PreambleGroupB和contenion-free Preamble。其具体划分由sizeOfRA-PreambleGroupA和numberOfRA-Preamble( SIB2)决定。


    这里写图片描述

    其中GroupA和GroupB(可能不存在)用于竞争接入,这两组Preamble的本身是不存在区别的,仅在eNB作UL grant决策时发挥作用。如果UE的MSG3比较大(>messageSIzeGroupA)并且路径损耗pathloss小于PCMAX,c-preambleInitialReceivedTargetPower-DeltaPreambleMsg3-messagePowerOffsetGroupB,则使用GroupB的Preamble,eNB发现接收到的Preamble属于GroupB,则会分配一个较大的资源给此UE。如果不分组,eNB始终必须配置一个很大的UL grant给msg3。
    选择好Group后,UE从该Group随机选择一个Preamble并将PRACHMaskIndex置0(意思是所有可能的PRACH资源)。基于竞争随机的RA过程如下图所示。


    这里写图片描述

    UE也可能进行基于非竞争的RA,比如handover等非竞争的RA过程如下图所示。


    这里写图片描述

    从上图可以看到preamble是eNB直接指定的contention-free preamble。基于非竞争的RA,其PreambleIndex是eNB指定的。指定方式包括两种情况:1,如handover通过RACH-ConfigDedicated内部字段(ra-PreambleIndex和ra-PRACH-MaskIndex)设置;2,如下行数据到达或定位通过DCI format 1A的内部字段(Preamble Index和PRACH Mask Index)设置。
    还有一种特殊的情况,eNB指定了PreambleIndex但是等于00000,此时按基于竞争的随机接入执行RA过程。(这一情况可能只针对所有64个preamble全被设置成contention-preamble)。

    4.3,Preamble时频资源

    SIB2中的prach-configIndex指定了时域上可用的PRACH的资源集合,PRACH Mask Index指定了某个UE可以在系统帧内的哪些PRACH上发送preamble(值0表示所有可用的PRACH资源)。在基于非竞争的随机接入中eNB可以通过mask直接指定UE在某个特定的PRACH上发送preamble。


    这里写图片描述

    比如,对于prach-configIndex查表后的得出PRACH resource={RF,SF}={any,[0,2,4,6,8]},而ra-PRACH-MaskIndex=3,从上表可知,对应的PRACH的资源号为3,表示preamble在系统帧内的第4个PRACH资源上发送。PRACH资源号是系统帧内的PRACH资源的编号,从0开始并以PRACH资源在下标出现的排序(FDD)。


    这里写图片描述

    preamble 的时频位置决定了 RA-RNTI 的值,UE 发送了 preamble 之后,会在 RAR 时间窗内,根据这个 RA-RNTI 值来监听对应的 PDCCH。 Legacy LTE的RA-RNTI 的计算如下。:
    RA-RNTI= 1 + t_id+10*f_id
    其中,t_id 是发送 preamble 的 PRACH 所在的第一个子帧号(0 ≤ t_id < 10),f_id 是在该子帧发送 preamble 的 PRACH 在频域上的索引(0 ≤ f_id < 6)。 对于 FDD 而言,每个子帧只有一个PRACH 资源,因此 f_id 固定为 0。 (RA-RNTI 的计算见 36.321 的 5.1.4 节)

    4.4,确定目标接收功率

    preamble 的目标接收功率 PREAMBLE_RECEIVED_TARGET_POWER 通过下面的公式计算:
    preambleInitialReceivedTargetPower + DELTA_PREAMBLE + (PREAMBLE_TRANSMISSION_COUNTER – 1) * powerRampingStep
    其中 preambleInitialReceivedTargetPower 是 eNodeB 期待接收到的 preamble 的初始功率。DELTA_PREAMBLE 与 preamble format 相关,其值见 36.321 的的 Table 7.6-1。而powerRampingStep 是每次接入失败后,下次接入时提升的发射功率。 preamble 的实际发射功率 的计算公式为:
    PPRACH=min{PCMAX,c(i),PREAMBLE_RECEIVED_TARGET_POWER+PLc}_[dBm]PPRACH=min{PCMAX,c(i),PREAMBLE_RECEIVED_TARGET_POWER+PLc}_[dBm]
    其中,PCMAX,c(i)PCMAX,c(i)是 UE 在 PCell 的子帧 i 上所配置的最大输出功率,PLcPLc是 UE 通过测量 PCell的小区特定的参考信号得到的下行路径损耗。
    至此RA preamble的准备工作就绪,UE开始发送Preamble。
    发送了接入前导序列以后,UE需要监听PDCCH信道,是否存在eNB回复的RAR消息,(Random Access Response),RAR的时间窗是从UE发送了前导序列的子帧 + 3个子帧开始, 长度为Ra-ResponseWindowSize个子帧。 如果在此时间内没有接收到回复给自己的RAR, 就认为此次接入失败。
    如果初始接入过程失败,但是还没有达到最大尝试次数preambleTransMax,那么UE可以在上次发射功率的基础上, 功率提升powerRampingStep,来发送此次前导, 从而提高发送成功的机率。

    4.5,随机接入响应 (RAR)

    当eNB检测到UE发送的前导序列,就会在DL-SCH上发送一个响应,包含:检测到的前导序列的索引号、用于上行同步的时间调整信息、初始的上行资源分配(用于发送随后的MSG3),以及一个临时C-RNTI,此临时的C-RNTI将在步骤四(冲突解决)中决定是否转换为永久的C-RNTI。
    当Preamble发送完毕后,UE需要在k+3(k表示preamble发送的结束SF)的位置开始监听RAR的PDCCH,此PDCCH需要使用RA-RNTI才可解析出,RA-RNTI与UE发送前导序列的时频位置一一对应。UE和eNodeB可以分别计算出前导序列对应的RA-RNTI值。UE监听PDCCH信道以RA-RNTI解码得到RAR msg的位置信息,并解码相应的PDSCH信道得到RAR msg,如果RAR中前导序列索引与UE自己发送的前导序列相同,那么UE就采用RAR中的上行时间调整信息, 并启动相应的冲突调整过程。


    这里写图片描述]

    RAR的窗口大小配置在SIB2中,如下所示。


    这里写图片描述

    关于RAR的MAC PDU结构,一个MAC PDU包括一个头和0-n个MAC RAR。(Example of MAC PDU consisting of a MAC header and MAC RARs)。


    这里写图片描述

    MAC header的大小是可变的,它可能包好一个或多个 MAC PDU subheaders。除了backoff Indicator subheader外,每一个MAC PDU subheader对应一个MAC RAR。backoff Indicator subheader仅仅包含一次且只要包含肯定是MAC PDU subheaders中的第一个。其结构如下


    这里写图片描述

    BI实际上是指示了UE重传Preamble的等待时间范围。如果UE在规定的时间范围以内,没有收到任何RAR消息,或者RAR消息中的前导序列索引与自己的不符,则认为此次的前导接入失败。UE 需要推迟一段时间, 才能进行下一次的前导接入。推迟的时间范围,就由backoff indictor来指示,UE可以在0 到BackoffIndicator之间随机取值。这样的设计可以减少UE在相同时间再次发送前导序列的几率。
    而其他普通的MAC PDU subheaders由3个fields组成,分别是E/T/RAPID,如下图所示。


    这里写图片描述

    而MAC RAR由4个fileds组成,如下图所示。其中下右图是对应BL UEs and UEs in enhanced coverage in enhanced coverage level 2 or 3的情况,其他情况MAC RAR使用下左图格式。最后一个MAC RAR可能会发生填充。是否填充以及填充的长度是根据TB大小,MAC header的大小和RAR的数量计算得到的。


    这里写图片描述

    4.6,MSG3 发送 (RRC Connection Request)

    UE接收到RAR消息, 获得上行的时间同步和上行资源。但此时并不能确定RAR消息是发送给UE自己而不是发送给其他的UE的。由于UE的前导序列是从公共资源中随机选取的,因此, 存在着不同的UE在相同的时间-频率资源上发送相同的接入前导序列的可能性, 这样,他们就会通过相同的RA-RNTI接收到同样的RAR。而且,UE也无从知道是否有其他的UE在使用相同的资源进行随机接入。为此UE需要通过随后的MSG3 和MSG4消息, 来解决这样的随机接入冲突。
    MSG3是第一条基于上行调度,通过HARQ (Hybrid Automatic Repeat request),在PUSCH上传输的消息。其最大重传次数由maxHARQ-Msg3TX定义。在初始的随机接入中, MSG3中传输的是RRCConnectionRequest。如果不同的UE接收到相同的RAR消息,那么他们就会获得相同的上行资源,同时发送Msg3消息,为了区分不同的UE,在MSG3中会携带一个UE特定的ID,用于区分不同的UE。在初始接入的情况下, 这个ID可以是UE的S-TMSI(如果存在的话)或者随机生成的一个40 位的值(可以认为,不同UE随机生成相同的40 位值的可能性非常小)。
    UE在发完MSg3消息后就要立刻启动竞争消除定时器mac-ContentionResolutionTimer(而随后每一次重传消息3都要重启这个定时器),UE需要在此时间内监听eNodeB返回给自己的冲突解决消息。

    4.7,冲突解决消息

    如果在mac-ContentionResolutionTimer时间内,UE接收到eNodeB返回的ContentionResolution消息,并且其中携带的UE ID与自己在Msg3中上报给eNodeB的相符,那么UE就认为自己赢得了此次的随机接入冲突,随机接入成功。并将在RAR消息中得到的临时C-RNTI置为自己的C-RNTI。否则的话,UE认为此次接入失败, 并按照上面所述的规则进行随机接入的重传过程。
    值得注意的是, 冲突解决消息MSG4, 也是基于HARQ的。只有赢得冲突的UE才发送ACK值, 失去冲突或无法解码Msg4 的UE不发送任何反馈消息。

    展开全文
  • 更新的时候总是提示网络连接有问题,而我的网络连接是没有问题的,可以浏览各种网页,就是更新不了软件包![![图片说明](https://img-ask.csdn.net/upload/201510/28/1446038337_538106.jpg)图片说明]...
  • wpa_cli 命令连接网络过程

    万次阅读 2012-10-19 16:27:00
    我们假设已经安装并配置好wpa_supplicant 首先启动wpa_supplicant #cd /home/work/pkg_wifi (这是我自己的路径) ...#./wpa_supplicant -c /home/work/pkg_wifi -i wlan...此时已经连接网络 可以ping百度试一下
  • 安装Android 7.1虚拟机时,在设置时遇到了网络无法连接的问题,如图: 解决方法为: 返回之前选择语言的界面,依次点击A、B、C、D四个位置,即可跳过所有的设置步骤,直接进入主界面。 跳过设置步骤后,选择主屏幕...
  • 网络盒子连接电视过程及原理: 1机顶盒连网线(或无线) 2电视连机顶盒,方式有①黄色视频、红色音频线②串口线 3电视选择信号源,选择“视频”信号源 5这些都连通了,电视中会出现机顶盒的主界面 6所有的操作都在...
  • Android网络连接网络

    千次阅读 2013-03-10 17:58:35
    在Android中,ConnectivityManager类代表网络连接服务,它被用来监控网络连接状态,配置失效重连,并控制网络天线等。获取Connectivity Manager实例的方法是使用getSystemService,并指定Context.CONNECTIVITY_...
  • 第一分类:建立动态的网络 一、首先查看自己的虚拟机服务有没有开启,选择电脑里面的服务查看; 1.计算机点击右键选择管理 2.进入管理选择VM开头的服务如果没有开启的话就右键开启 二、虚拟机服务开启后就查看...
  • Qt 之处理 QNetworkAccessManager 网络连接超时

    万次阅读 热门讨论 2016-11-17 18:53:41
    简述在网络操作中,经常会由于各种原因引起网络连接超时,究竟何为网络连接超时? 网络连接超时:在程序默认的等待时间内没有得到服务器的响应 简述 超时原因 Qt 中的网络连接超时 如何处理超时 封装类 更多参考...
  • 判断设备是否已经连接网络,并且在连接网络的状态下判断是wifi无线连接还是GPRS手机网络连接,这样就可以在不同的网络连接下去调用不同的方法,处理不同的事情。这些功能都写在了下面的代码中了!请看主要代码如下:...
  • TCP建立连接和断开连接过程详解

    千次阅读 2016-10-08 09:22:51
    最近在看TCP这块知识的时候对TCP连接三次握手断开四次断开不是很了解...TCP是一个面向连接的服务,面向连接的服务是电话系统服务模式的抽象,每一次完整的数据传输都必须经过建立连接,数据传输和终止连接三个过程,TCP建立
  • 现在是互联网高速发展的时代,Android开发...这个小例子可以判断设备是否已经连接网络,并且在连接网络的状态下可以判断是wifi无线连接还是GPRS手机网络连接,这样就可以在不同的网络连接下去调用不同的方法,处理不同
  • 记录最近遇到的WIN10网络连接,地球仪图标的处理过程,由于花费了很多时间找方法,特总结如下: 一、使用环境 企业内部网络,电脑设置固定IP,设定IP后电脑可以正常上网,但是SVN会有异常和unity内游戏会显示网络...
  • 二、ubuntu 20.04的网络路径和大部分都不一样: Ubuntu20配置值静态ip时需要修改/etc/netplan下面 1-network-manager-all.yaml这个文件,该文件的原始内容为: 通过查询ip的命令:如下图: 根据相关信息...
  • 网络连接流程图

    千次阅读 2018-03-16 15:49:38
    这段时间稍微学习了一下网络连接的知识。 整个网络流程如下图,后续将针对每个步骤深入了解学习。
  • 连接神经网络

    万次阅读 2017-03-22 21:20:52
    连接神经网络 辅助阅读:TensorFlow中文社区教程 - 英文官方教程 代码见:full_connect.py Linear Model 加载lesson 1中的数据集 将Data降维成一维,将label映射为one-hot encoding def ...
  • 前面几节介绍了网络评分机制的运行流程,下面我们再次通过案例来梳理一下评分机制在使用过程中的体现。  用户原本在用数据上网,但是如果到了一个有WIFI的环境,并连接上了WIFI,此时用户的手机将会自动断开数据...
  • 监控正常网络连接断开

    千次阅读 2014-06-14 17:49:58
    TCP通信的两方,其中一方正常关闭(调用close函数或程序正常退出)正处于连接状态的TCP套接字,这时双方的TCP将完成协商关闭连接的四次握手,握手结束以后,另...整个过程就是正常网络连接断开的处理过程。 监控这
  • TCP建立连接和关闭连接过程

    千次阅读 2018-03-07 18:11:56
    先来一张图看看整个连接和关闭的过程: 各个状态的意义如下: LISTEN:侦听来自远方TCP端口的连接请求; SYN-SENT:在发送连接请求后等待匹配的连接请求; SYN-RECEIVED:在收到和发送一个连接请求后等待对...
  • 网络通信过程

    千次阅读 2019-04-18 17:40:48
    网络通信过程 1、2台电脑 如果两台电脑之间通过⽹线连接是可以直接通信的,但是需要提前 设置好ip地址以及⽹络掩码 2、使用集线器组成一个网络 广播的形式发送 3、使用交换机组成一个网络 当需要⼴播的时候...
  • 笔记本连接网络时断时续可用的解决方法之一。
  • TCP建立连接过程

    千次阅读 2018-08-03 16:13:59
    TCP建立连接过程 ...  在可靠的TCP网络通信中,客户端和服务器端通信建立连接过程可简单表述为三次握手(建立连接的阶段)和四次挥手(释放连接阶段),下图是这两个阶段的一个完整的表述: ...
  • 我的虚拟机有线网络连接方式是NAT方式。 解决方法: 导致这个问题的原因是你的主机当中有关虚拟机网络连接的服务被停止或者禁用了!右键点击我的电脑—>管理—>服务和应用程序—>服务,找到VMwamre NAT Service,...
  • socket(套接字)连接过程

    千次阅读 2016-01-22 17:06:40
    套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。 接下来介绍每个步骤的详细过程: 1.服务器监听:是服务器端套接字并不指定具体的客户端套接字,而是一直处于等待连接的状态,实时监控...
  • TCP建立连接和断开连接过程

    千次阅读 2017-08-24 16:32:45
    假设Client端发起中断连接请求,也就是发送FIN报文。 Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。 所以你先发送...
  • debian连接网络

    千次阅读 2015-10-01 23:48:42
    在虚拟机中安装了debian系统,发现无法连接网络,在网上找了很多... 设置过程:虚拟机主界面菜单->虚拟机->设置->硬件tab页->网络适配器->网络连接选择:桥接模式(B)。最后启动debian就可以上网了。 如下图所示。
  • HTTP请求的过程与TCP连接过程

    万次阅读 2016-01-05 10:23:50
    HTTP请求的过程与TCP连接过程   http请求的详细过程 HTTP是一个应用层的协议,在这个层的协议,是一种网络交互需要遵守的一种协议规范。 1、连接:当输入一个请求时,首先建立一个socket连接,因为socket是通过...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,001,223
精华内容 400,489
关键字:

网络的连接过程