非ブロック、接続を選択し、RECVとのrecvfrom、送信、およびおよそMSG_NOSIGNAL、非接続プラグ家賃のコードで、説明のSendToにおけるLinuxのソケット通信]

送信機能におけるLinuxの例外メッセージMSG_NOSIGNAL

サーバーをシミュレートするために、受信側のプロセスサーバを終了するには、Ctrl + Cを使用してサーバーがダウンした場合には、プロセスの終了後のサービスソケットは、サーバーは、自然のプロセスをシャットダウンしますが、クライアント側でも予期せず閉じることが判明しました。

書き込みを送信し、MSG_NOSIGNALサイン、再コンパイル、実行し、割り込みサーバーを追加するための送信機能を変更し、この問題は非常にシックに解決されます

ネットワーク接続が失われたが、また、データを送信する際にLinuxでは、だけでなく、送信()の戻り値が反映されますが、また、異常のシステムにメッセージを送信し、廃棄をしないと、システムはBrokePipe外となり、そのため、プログラムを終了安定したサービスを提供するサーバーは、巨大な災害が発生します。このため、送信()関数はMSG_NOSIGNAL設定パラメータを続くことが、システムに定期的にメッセージを送信する送信()関数を禁止しています。

実際には、最初の送信時間後に再度お送り行うことがパイプラインの破裂信号を送信する(ESPIPE)エラーコード、およびシステムがプロセスを処理し、受信する信号(SIGPIPE)を送信しますときにエラーコードは、RSTを返さ受けるべき終了操作。実際には、プロセス内MSG_NOSIGNAL役割は、(SIGPIPE)信号を無視するように言われるべき

()関数を接続します

connect头文件:

        #include<sys/types.h>

        #include<sys/socket.h>

connect声明:

        int connect (int sockfd, struct sockaddr * serv_addr, int addrlen);
connect功能:

        使用套接字sockfd建立到指定网络地址serv_addr的socket连接,参数addrlen为serv_addr指向的内存空间大小,即sizeof(struct sockaddr_in)。

connect返回值:

        1)成功返回0,表示连接建立成功(如服务器和客户端是同一台机器上的两个进程时,会发生这种情况)

        2)失败返回SOCKET_ERROR,相应的设置errno,通过errno获取错误信息。常见的错误有对方主机不可达或者超时错误,也可能是对方主机没有进程监听对应的端口。

(接続非ブロックモード)接続をノンブロッキング

        ソケットは、非ブロッキングブロッキングモードでI / O操作を実行します。

        1)I / O操作が完了する前に、ブロックモードで、機能は、操作が待っていると、すぐに機能がここにブロックしますスレッドを返しません実行します。

        2)これに対して、非ブロッキングモードでは、ソケット関数は直ちに戻ります関係なく、I / Oの機能を実行し続けるスレッド完了する。

        ブロックモードにクライアントソケット記述子は、接続が確立または接続確立タイムアウトするまで(Linuxカーネルの接続にブロックします)(接続した場合、クライアントの呼び出しは、()はソケットサーバの接続を開始する接続タイムアウト制限がある75S、 Solirisの9は、多くの場合、数分に75S)であると考えられ、数分です。非ブロックモード、コール接続()関数はすぐに返した場合、接続がすぐに成功した(-1)を確立することができない場合は、errnoにEINPROGRESSに設定されている、TCP 3ウェイハンドシェイクは、この時点で続けています。この時点で、()を選択し呼び出す非ブロックが完了し接続するかどうかを検出することができます。タイムアウトが接続よりも短くすることができる指定されたタイムアウト期間を選択し、接続スレッドは、接続が遮断時に防止することができます。

()関数を選択

select头文件:

         #include<sys/time.h> 

         #include<sys/types.h> 

         #include<unistd.h>

select声明:

         int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval* timeout);

select功能:

         本函数用于确定一个或多个套接口的状态。对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息。

select参数:

         先说明两个结构体:
         第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(filedescriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作:

                    FD_ZERO(fd_set *) 清空集合

                    FD_SET(int ,fd_set*) 将一个给定的文件描述符加入集合之中

                    FD_CLR(int,fd_set*) 将一个给定的文件描述符从集合中删除

                    FD_ISSET(int ,fd_set* ) 检查集合中指定的文件描述符是否可以读写

         第二,struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。

         1) int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!

         2)fd_set * readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。

         3) fd_set * writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。

         4)fd_set * errorfds同上面两个参数的意图,用来监视文件错误异常。

         5) struct timeval * timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

判定ルールを選択します。

        1))(選択が0を返す場合

select()のタイムアウトでの表現は、タイムアウト時間内に接続を確立できませんでした、また、複数のタイムアウト、必要性がユーザーにタイムアウトエラーを返すようにする必要があり、テストのために()を再度選択し実行することができます。

        2)()の選択は、0より大きい値を返す場合

そして、検出された読み出しまたは書き込み可能なソケット記述子。バークレーは、選択するために、関係する二つの規則から実現し、非ブロックI / Oがあるされています。

        接続の確立がするとA)成功すると、ソケット記述子になり、書き込み可能(接続が確立され、ライト・バッファは無料ですが、書くことが可能です)

        B)接続が確立されると、エラーが発生したときに、ソケット記述子を読み取り可能及び書き込み可能の両方の読み取りおよび書き込み可能に起因する(ペンディングエラー)

        ソケット記述子を読み取ることがわかったり書き込まれたときにこのように、さらに可能に接続されて成功またはエラーを決定します。接続が設定された後、サーバーがクライアントにデータを送信しているここで分離されなければならないB)と、通常の状況に接続された別の領域は、この時間は、同じソケット記述子の両方読み書き可能なノンブロッキングを選択するように戻ります。

        □ためのUnix環境、getsockoptの記述子を呼び出すことによって検出することができます成功した接続または間違った(これは、Linux環境でテストし、無効であることが判明ブックが提供する「UNIXネットワークプログラミング」方式である)されていますのlinux次回、関係なく、ネットワークエラーが発生するかどうかの、常に0を返すのgetsockoptは戻りません-1。

               接続の確立に成功した場合A)はgetsockoptによって取得された(数sockfd、SOL_SOCKET、SO_ERROR、(チャー*)&エラー、およびLEN)エラー値が0になり

               接続エラーが発生した場合B)、errno値はETIMEDOUT等、接続エラーerrno値、例えばECONNREFUSEDに対応しています

        □テストと実績のある、決定のより効率的な方法は、それがLinux環境で有効です。

        エラーがEISCONNある場合、対応する戻り不良、接続再度呼び出さ、errnoに、ソケット接続が確立された表現、または接続が失敗したこと。

        次のことを試してみてください。一度選択した後、この瞬間ソケットは読ん記述または書き込み、再度接続を実行するために単語を見つけ、常にerrnoを変更しないEINPROGRESSで、この時間は、タイムアウトを増やす結果を選択同じです。戻り値を試すことを選択し、または戻り値は0であり、そしてerrnoに依然として接続+選択再度実行、EINPROGRESS(115)を接続したときの後、すなわち、接続状態が再び検出しました。この時、errnoがEISCONN(106)に設定され、成功を接続してください。

Linux下常见的socket错误码:

EACCES, EPERM:用户试图在套接字广播标志没有设置的情况下连接广播地址或由于防火墙策略导致连接失败。

EADDRINUSE 98:Address already in use(本地地址处于使用状态)

EAFNOSUPPORT 97:Address family not supported by protocol(参数serv_add中的地址非合法地址)

EAGAIN:没有足够空闲的本地端口。

EALREADY 114:Operation already in progress(套接字为非阻塞套接字,并且原来的连接请求还未完成)

EBADF 77:File descriptor in bad state(非法的文件描述符)

ECONNREFUSED 111:Connection refused(远程地址并没有处于监听状态)

EFAULT:指向套接字结构体的地址非法。

EINPROGRESS 115:Operation now in progress(套接字为非阻塞套接字,且连接请求没有立即完成)

EINTR:系统调用的执行由于捕获中断而中止。

EISCONN 106:Transport endpoint is already connected(已经连接到该套接字)

ENETUNREACH 101:Network is unreachable(网络不可到达)

ENOTSOCK 88:Socket operation on non-socket(文件描述符不与套接字相关)

ETIMEDOUT 110:Connection timed out(连接超时)

fcntlを使用して、方法 - ソケットはブロッキングモードと非ブロッキングモードに配置されています

ノンブロッキングモードを提供:

まずF_GETFLのfcntlのでフラグを取得し、そしてF_SETFLでフラグを設定| O_NONBLOCK。        

      フラグ= fcntlの(数sockfd、F_GETFL、0); //値の文書フラグを取得します。

      fcntlの(数sockfd、F_SETFL、旗| O_NONBLOCK); //非ブロックモードに設定します。

同時にデータを送受信する必要フラグMSG_DONTWAIT

      RECVで、のrecvfromフラグがMSG_DONTWAITに設定されている場合、のsendtoデータを送信します

ブロックモードに設定されています:

 

最初F_GETFLのfcntlのフラグを取得することはF_SETFLフラグ&〜O_NONBLOCKによって提供されます。     

     フラグ= fcntlの(数sockfd、F_GETFL、0); //値の文書フラグを取得します。

     fcntlの(数sockfd、F_SETFL、フラグ&〜O_NONBLOCK); //ブロックモードに設定します。            

同時に受信したデータを送信する場合、あなたはフラグをブロック使用する必要があります

        RECV、のrecvfromと送信、フラグが0に設定されているのsendtoデータに、デフォルトが遮断されます。  

機能を送る-MSG_NOSIGNAL

UINT flag = MSG_NOSIGNAL;//禁止 send() 函数向系统发送常消息。

ソケットが非ブロッキングモードに配置された後、数sockfd操作のたびには、非ブロッキングです。

非ブロッキングモード:

コネクト   

       0に戻ったときに= 0、すぐに、ソケットのリンクを作成

       それは-1を返すとき<0は、errnoにEINPROGRESS(処理中の現在のプロセスを表す)、そうでなければ失敗であるかどうかを決定する必要があります。

       例:以下は、選択されますか、ファイルディスクリプタは、fdのリンクが確立されているかどうかを監視し、

        第三ACKスリーウェイハンドシェイクが失われる可能性がありますが、クライアントは、リンクが確立されたと考えているので、成功、注意getsockoptの検証のリスナーの例を接続するかどうかを選択します。


//////////////////////////////////////////////////////////////////////
//
// func description	:	建立与TCP服务器的连接
//
//-----input------
// Parameters
//     	s 	 		: 	由socket函数返回的套接字描述符
//	   	servaddr 	:   指向套接字地址结构的指针
//     	addrlen		:  结构的长度
//------output------
// Return 
//     BOOL 		:	成功返回0,若出错则返回-1
//     不要把错误(EINTR/EINPROGRESS/EAGAIN)当成Fatal
//
BOOL SocketAPI::connect_ex(SOCKET s, const struct sockaddr* servaddr, UINT addrlen)
{
	DEBUG_TRY
    if (connect(s, servaddr, addrlen) == -1)
    {
        //LOGDEBUG("[SocketAPI::connect_ex] Error errno[%d] discription[%s]", errno, strerror(errno));
#if defined(__LINUX__)	
        switch(errno)
        {
            case EALREADY:      //#define EALREADY    114 /* Operation already in progress */
            case EINPROGRESS:   //#define EINPROGRESS 115 /* Operation now in progress */
            case EINTR:         //#define EINTR        4  /* Interrupted system call */
            case EAGAIN:        //#define EAGAIN      11  /* Try again */          
            {
                    //!alter by huyf:修改非阻塞connet处理流程
                    //建立connect连接,此时socket设置为非阻塞,connect调用后,无论连接是否建立立即返回-1,同时将errno(包含errno.h就可以直接使用)设置为EINPROGRESS, 
                    //表示此时tcp三次握手仍就进行,如果errno不是EINPROGRESS,则说明连接错误,程序结束。
                    return reconnect_ex(s, servaddr, addrlen) == 0 ? TRUE : FALSE;
                    //return TRUE;
                    //!alter end:修改非阻塞connet处理流程
            }            
            //!alter end:修改非阻塞connet处理流程
            //增加已经连接的处理,此处可以直接返回告之
            case EISCONN:   //#define EISCONN     106 /* Transport endpoint is already connected */
            {
                return TRUE;
            }
            //!alter end:修改非阻塞connet处理流程
            case ECONNREFUSED:
            case ETIMEDOUT:
            case ENETUNREACH:
            case EADDRINUSE:
            case EBADF:
            case EFAULT:
            case ENOTSOCK:
            default:
            {
                //LOGERROR("[SocketAPI::connect_ex] Is Error errno[%d] discription[%s]", errno, strerror(errno));
                break;
            }
        }//end of switch
#elif defined(__WINDOWS__)
        INT iErr = WSAGetLastError();
        switch(iErr)
        {
        case WSANOTINITIALISED: 
            {
                strncpy(Error, "WSANOTINITIALISED", ERROR_SIZE);
                break;
            }

        case WSAENETDOWN:
            { 
                strncpy(Error, "WSAENETDOWN", ERROR_SIZE);
                break;
            }
        case WSAEADDRINUSE: 
            { 
                strncpy(Error, "WSAEADDRINUSE", ERROR_SIZE);
                break;
            }
        case WSAEINTR: 
            { 
                strncpy(Error, "WSAEINTR", ERROR_SIZE);
                break;
            }
        case WSAEINPROGRESS: 
            { 
                strncpy(Error, "WSAEINPROGRESS", ERROR_SIZE);
                break;
            }
        case WSAEALREADY: 
            { 
                strncpy(Error, "WSAEALREADY", ERROR_SIZE);
                break;
            }
        case WSAEADDRNOTAVAIL: 
            { 
                strncpy(Error, "WSAEADDRNOTAVAIL", ERROR_SIZE);
                break;
            }
        case WSAEAFNOSUPPORT: 
            { 
                strncpy(Error, "WSAEAFNOSUPPORT", ERROR_SIZE);
                break;
            }
        case WSAECONNREFUSED: 
            { 
                strncpy(Error, "WSAECONNREFUSED", ERROR_SIZE);
                break;
            }
        case WSAEFAULT: 
            { 
                strncpy(Error, "WSAEFAULT", ERROR_SIZE);
                break;
            }
        case WSAEINVAL: 
            { 
                strncpy(Error, "WSAEINVAL", ERROR_SIZE);
                break;
            }
        case WSAEISCONN: 
            { 
                strncpy(Error, "WSAEISCONN", ERROR_SIZE);
                break;
            }
        case WSAENETUNREACH: 
            { 
                strncpy(Error, "WSAENETUNREACH", ERROR_SIZE);
                break;
            }
        case WSAENOBUFS: 
            { 
                strncpy(Error, "WSAENOBUFS", ERROR_SIZE);
                break;
            }
        case WSAENOTSOCK: 
            { 
                strncpy(Error, "WSAENOTSOCK", ERROR_SIZE);
                break;
            }
        case WSAETIMEDOUT: 
            { 
                strncpy(Error, "WSAETIMEDOUT", ERROR_SIZE);
                break;
            }
        case WSAEWOULDBLOCK: 
            { 
                strncpy(Error, "WSAEWOULDBLOCK", ERROR_SIZE);
                break;
            }
        default:
            {
                strncpy(Error, "UNKNOWN", ERROR_SIZE);
                break;
            }
        }//end of switch		
#endif
        return FALSE;
    }
    return TRUE;	
	DEBUG_CATCHF("SocketAPI::connect_ex");	
}


//////////////////////////////////////////////////////////////////////
//
// func description :   非阻塞套接字建立连接时未立即完成的检查(tcp三次握手阶段)
//
//-----input------
// Parameters
//      s         :   由socket函数返回的套接字描述符
//------output------
// Return 
//     BOOL         :   成功返回0,若出错则返回-1
//     不要把错误(EINTR/EINPROGRESS/EAGAIN)当成Fatal
//
int SocketAPI::reconnect_ex(SOCKET s, const struct sockaddr* servaddr, UINT addrlen)
{   
    //LOGDEBUG("[SocketAPI::reconnect_ex] Get The Connect Result By Select() Errno=[%d] Discription=[%s]", errno, strerror(errno));    
    //if (errno == EINPROGRESS)    
    //{    
        //int nTimes = 0; 
        int nRet = -1;   
        //while (nTimes++ < 5)    
        {    
            fd_set rfds, wfds;    
            struct timeval tv;
            FD_ZERO(&rfds);    
            FD_ZERO(&wfds);    
            FD_SET(s, &rfds);    
            FD_SET(s, &wfds);    
                
            /* set select() time out */    
            tv.tv_sec = 1;     
            tv.tv_usec = 0;
            /*
            2.源自Berkeley的实现(和Posix.1g)有两条与select和非阻塞IO相关的规则:
            A:当连接建立成功时,套接口描述符变成可写;
            B:当连接出错时,套接口描述符变成既可读又可写;
            注意:当一个套接口出错时,它会被select调用标记为既可读又可写;
            一种更有效的判断方法,经测试验证,在Linux环境下是有效的:
                再次调用connect,相应返回失败,如果错误errno是EISCONN,表示socket连接已经建立,否则认为连接失败。
            */    
            int nSelRet = select(s+1, &rfds, &wfds, NULL, &tv);    
            switch (nSelRet)    
            {    
                case -1:    //出错
                {
                    //LOGERROR("[SocketAPI::reconnect_ex] Select Is Error... nSelRet=[%d] Errno=[%d] Discription=[%s]", nSelRet, errno, strerror(errno)); 
                    nRet = -1; 
                }   
                break;    
                case 0:    //超时
                {
                    //LOGWARNING("[SocketAPI::reconnect_ex] Select Is Time Out... nSelRet=[%d] Errno=[%d] Discription=[%s]", nSelRet, errno, strerror(errno));     
                    nRet = -1; 
                }   
                break;    
                default:    //有数据过来
                {
                    //LOGDEBUG("[SocketAPI::reconnect_ex] nSelRet=[%d] Errno=[%d] Discription=[%s]", nSelRet, errno, strerror(errno));   
                    if (FD_ISSET(s, &rfds) || FD_ISSET(s, &wfds))    //判断可读或者可写
                    {    
                        #if 0 // not useable in linux environment, suggested in <<Unix network programming>>  SO_ERROR no used 
                            int errinfo, errlen;    
                            if (-1 == getsockopt(s, SOL_SOCKET, SO_ERROR, &errinfo, &errlen))    
                            {    
                                nRet = -1;  
                                LOGERROR("getsockopt return -1.\n");  
                                break;    
                            }    
                            else if (0 != errinfo)    
                            {      
                                nRet = -1;   
                                LOGERROR("getsockopt return errinfo = %d.\n", errinfo); 
                                break;    
                            }                                       
                            nRet = 0;  
                            LOGDEBUG("connect ok?\n");     
                        #else    
                            #if 1    
                                connect(s, servaddr, addrlen);      //再次连接来判断套接字状态    
                                if (errno == EISCONN)    
                                {    
                                    //LOGDEBUG("[SocketAPI::reconnect_ex] Reconnect Finished...nSelRet=[%d]", nSelRet);    
                                    nRet = 0;    
                                }    
                                else    
                                {      
                                    //LOGWARNING("[SocketAPI::reconnect_ex] Reconnect Failed...FD_ISSET(s, &rfds)=[%d] FD_ISSET(s, &wfds)=[%d] nSelRet=[%d] Errno=[%d] Discription=[%s]", FD_ISSET(s, &rfds) , FD_ISSET(s, &wfds), nSelRet, errno, strerror(errno));     
                                    nRet = -1;    
                                }    
                            #else    //test
                                char buff[2];    
                                if (read(s, buff, 0) < 0)    
                                {    
                                    LOGERROR("connect failed. errno = %d\n", errno);    
                                    nRet = errno;    
                                }    
                                else    
                                {    
                                    LOGDEBUG("connect finished.\n");    
                                    nRet = 0;    
                                }    
                            #endif    
                        #endif    
                    } 
                }
                break;   
            } 
        }   
    //}  
    return nRet;
}

 

RECV和のrecvfrom

       = 0戻り値は、ピアがこのリンクを閉鎖したことを示す、0であり、我々は近い(数sockfd)、この近いリンクを所有するべきです。

また、非同期操作がそう選択やファイルディスクリプタのトリガとイベントを行いますので。

       1は、あなたが選択し使用している場合、あなたはFD_CLR(数sockfd、FD_SET)を使用してください数sockfdもはや聞いて、削除されません。

       2あなたがepollをを使用している場合、システムは、それはもはや聞いてしまう数sockfdをクリアします。

       > 0戻り値は(バッファ)が0より大きいとはsizeof未満である場合、間違いを表すデータを読み出します。

(それが等しいのsizeof(バッファ)である場合、データはより大きくあり得る、読み取りを継続する必要があり、読んでなくてもよいです)

       戻り値が0未満である場合に<0、すなわち、-1に等しく、それは、決定された点です。

        errnoにある場合は1、EAGAINEまたはEWOULDBLOCK-は続きを読みます                                  

                一時的に読み込み可能なデータを全く示していない、あなたが読むために、またはファイルディスクリプタのその後の通知を待つかを選択し続けることができます。生成された(EAGAINE、EWOULDBLOCK

         理由:これは、マルチプロセス数sockfdで読み取ることができるが、プロセスはデータを読み取ることができる、他のプロセスが、当然のデータ(同様の衝撃グループ効果)、読んでいないだろう

         シングルプロセスも起こるかもしれません。このエラーのために、近く(数sockfd)なし。選択やファイルディスクリプタの下で次のトリガを待つことができ、

         続きを読みます。

         2、errnoがEINTR-ある場合続きを読みます

                表現は、あなたが読むことを続ける、または後続の通知のepollまたは選択を待つことができ、中断されました。

                そうでなければ、実際にデータを読み込むための失敗です。(この時点では、(数sockfd)の近くでなければなりません)

 

送信とはsendto      

        我々は完全に送信されない場合、我々は送信し続けることができ、全体の長さを送信することを知って、そのため、戻り値は、実際に送信される文字数です。

          <0-1戻り値 

私たちは、エラー番号を決定する必要があります。

                errnoにEAGAINEかEWOULDBLOCKであれば1、それは、現在のバッファがいっぱいになった、あなたが書き込みを続けることができ表し

                      またはバッファが存在する場合、それは、書き込み動作、頻繁に利用されている機能をトリガーする、フォローアップ通知のepollを待つかを選択します。  

                 2は、EINTRにerrnoをすれば、表現は中断され、あなたが書く、またはそれ以降の通知を待つか、ファイルディスクリプタを選択し続けることができます。

                       それとも本当に間違って、それはerrnoがEAGAINEまたはEWOULDBLOCKかEINTRないが、近いはずである。この時間(数sockfd)

          > = 0

 送信が所望の長さに等しい場合> = 0としない変速機の必要な長さに等しく、それは、送信が完了し、送信し続けるべきです。

おすすめ

転載: blog.csdn.net/Windgs_YF/article/details/94589497
おすすめ