版权声明:本文为博主原创文章,转载时请务必注明本文地址, 禁止用于任何商业用途, 否则会用法律维权。 https://blog.csdn.net/stpeace/article/details/44162349

说明: 1. 本文的讨论和实验都以Windows为例, 其实在linux上也大同小异。

2. 在第一次写此博文时, 我对某些地方有一些误解, 现予以更正, 对文章结构做了较大调整,也欢迎大家提出质疑。

3. 在做实验玩代码的时候, 意料之中地发现腾讯QQ也在玩心跳, 不清楚具体怎么实现的, 但有点意思哈

很多网友都问过一个类似这样的问题: tcp连接ok后,网络如果断了, 怎么检测断网对于这个问题, 我曾经给出了一个比较武断的定论: 我说, 断网断电后, tcp是死连接, 客户端和服务端无法感知,必须借助心跳机制。后来, 经过了更多的详细实验和深入思考, 我发现,事实并非完全如此。

tcp通道建立后, 如果断网断电, 两侧是否会有感知呢? 其实, 这个问题取决于我们的网络结构, 下面, 我以如下网络结构为例进行详细说明。 网络结构为:

先说说这幅图, 总体来说, 应该还算比较性感。 其中, pc1做客户端, ip地址是192.168.1.101, pc2做服务端, ip地址是192.168.1.102, 都是dhcp接入的.   请注意: 在做实验的过程中, 每次实验后, 都要关闭服务端和客户端, 且要回复拆掉的线, 断掉的电, 免得影响下次做实验。

确保网络连接良好, 我们来看pc2服务端程序:

  1.  
    #include <stdio.h>
  2.  
    #include <winsock2.h> // winsock接口
  3.  
    #pragma comment(lib, "ws2_32.lib") // winsock实现
  4.  
     
  5.  
    int main()
  6.  
    {
  7.  
    WORD wVersionRequested; // 双字节,winsock库的版本
  8.  
    WSADATA wsaData; // winsock库版本的相关信息
  9.  
     
  10.  
    wVersionRequested = MAKEWORD(1, 1); // 0x0101 即:257
  11.  
     
  12.  
     
  13.  
    // 加载winsock库并确定winsock版本,系统会把数据填入wsaData中
  14.  
    WSAStartup( wVersionRequested, &wsaData );
  15.  
     
  16.  
     
  17.  
    // AF_INET 表示采用TCP/IP协议族
  18.  
    // SOCK_STREAM 表示采用TCP协议
  19.  
    // 0是通常的默认情况
  20.  
    unsigned int sockSrv = socket(AF_INET, SOCK_STREAM, 0);
  21.  
     
  22.  
    SOCKADDR_IN addrSrv;
  23.  
     
  24.  
    addrSrv.sin_family = AF_INET; // TCP/IP协议族
  25.  
    addrSrv.sin_addr.S_un.S_addr = inet_addr("0.0.0.0"); // socket对应的IP地址
  26.  
    addrSrv.sin_port = htons(8888); // socket对应的端口
  27.  
     
  28.  
    // 将socket绑定到某个IP和端口(IP标识主机,端口标识通信进程)
  29.  
    bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
  30.  
     
  31.  
    // 将socket设置为监听模式,5表示等待连接队列的最大长度
  32.  
    listen(sockSrv, 5);
  33.  
     
  34.  
     
  35.  
    // sockSrv为监听状态下的socket
  36.  
    // &addrClient是缓冲区地址,保存了客户端的IP和端口等信息
  37.  
    // len是包含地址信息的长度
  38.  
    // 如果客户端没有启动,那么程序一直停留在该函数处
  39.  
    SOCKADDR_IN addrClient;
  40.  
    int len = sizeof(SOCKADDR);
  41.  
    unsigned int sockConn = accept(sockSrv,(SOCKADDR*)&addrClient, &len);
  42.  
     
  43.  
    while(1); // 卡住
  44.  
     
  45.  
    closesocket(sockConn);
  46.  
    closesocket(sockSrv);
  47.  
    WSACleanup();
  48.  
     
  49.  
    return 0;
  50.  
    }

我们再看pc1客户端程序:

  1.  
    #include <winsock2.h>
  2.  
    #include <stdio.h>
  3.  
    #pragma comment(lib, "ws2_32.lib")
  4.  
     
  5.  
    #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR, 4)
  6.  
     
  7.  
    // tcp keepalive结构体
  8.  
    typedef struct tcp_keepalive
  9.  
    {
  10.  
    u_long onoff;
  11.  
    u_long keepalivetime;
  12.  
    u_long keepaliveinterval;
  13.  
    }TCP_KEEPALIVE;
  14.  
     
  15.  
    // 通信的socket
  16.  
    SOCKET sockClient = 0;
  17.  
     
  18.  
    // 监测线程
  19.  
    DWORD WINAPI monitorThread(LPVOID pM)
  20.  
    {
  21.  
    while(1)
  22.  
    {
  23.  
    char szRecvBuf[10] = {0};
  24.  
    int nRet = recv(sockClient, szRecvBuf, 1, MSG_PEEK); // 注意, 最后一个参数必须是MSG_PEEK, 否则会影响主线程接收信息
  25.  
    if(nRet <= 0) // 实际上, 等于0表示服务端主动关闭通信socket
  26.  
    {
  27.  
    printf("监测到啦: nRet is %d\n", nRet);
  28.  
    closesocket(sockClient);
  29.  
    break;
  30.  
    }
  31.  
     
  32.  
    Sleep(200);
  33.  
    }
  34.  
     
  35.  
    return 0;
  36.  
    }
  37.  
     
  38.  
    int main()
  39.  
    {
  40.  
    WORD wVersionRequested;
  41.  
    WSADATA wsaData;
  42.  
    wVersionRequested = MAKEWORD(1, 1);
  43.  
     
  44.  
    WSAStartup( wVersionRequested, &wsaData );
  45.  
    sockClient = socket(AF_INET, SOCK_STREAM, 0);
  46.  
     
  47.  
    SOCKADDR_IN addrSrv;
  48.  
    addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.1.102");
  49.  
    addrSrv.sin_family = AF_INET;
  50.  
    addrSrv.sin_port = htons(8888);
  51.  
    connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
  52.  
     
  53.  
    // 开启监测线程
  54.  
    HANDLE handle = CreateThread(NULL, 0, monitorThread, NULL, 0, NULL);
  55.  
     
  56.  
    while(1); // 卡住
  57.  
     
  58.  
    CloseHandle(handle);
  59.  
    closesocket(sockClient);
  60.  
    WSACleanup();
  61.  
     
  62.  
    return 0;
  63.  
    }

下面, 我们来做几组实验:

实验一:

先启动服务端, 再启动客户端, 建立tcp连接。  用netstat -nao | findstr 8888查看两侧的socket状态, 发现是已经建立连接了。

情形1:

断掉下行网线2, 用netstat -nao | findstr 8888查看两侧的socket状态, 发现客户端socket状态变了, 且“监测到啦: nRet is -1”打印, 但服务端的socket状态没有变化。 这说明:客户端有感知, 但服务端没有感知。 此时, 服务端是死连接。

情形2:

断掉下行网线3, 用netstat -nao | findstr 8888查看两侧的socket状态, 发现客户端socket状态未变, 且没有“监测到啦: nRet is -1”打印, 但服务端的socket状态有变化。 这说明:客户端没有感知, 但服务端有感知。此时, 客户端是死连接。

情形3:

断掉路由器上行网线1, 用netstat -nao | findstr 8888查看两侧的socket状态, 发现客户端socket状态未变, 且没有“监测到啦: nRet is -1”打印, 且服务端的socket状态也没有变化。 而且这个时候, tcp连接并不是死连接, 还是活的, 还可以正常通信。 有意思的是, 此时, 我pc1上的QQ和pc2上的QQ过了一段时间都各自断了, 说明腾讯QQ客户端也有心跳机制。注意,
pc1上的QQ和pc2上的QQ不直接通信哈。

情形4:

断掉路由器电源4, 用netstat -nao | findstr 8888查看两侧的socket状态, 发现客户端socket状态变化, 且有“监测到啦: nRet is -1”打印, 服务端的socket状态也变化。 说明这个时候, 客户端有感知, 服务端也有感知, 两侧都不存在死连接。

情形5:

直接对pc1的电源线5进行断电(当然, 也要把笔记本pc1的电源拔出来才算数),客户端肯定就没了啊。 此时, 服务端socket状态并没有变化, 说明服务端是没有感知的, 服务端是死连接。

情形6:

直接对pc2的电源线6进行断电(当然, 也要把笔记本pc2的电源拔出来才算数),服务端肯定就没了啊。 此时,客户端socket状态并没有变化, 且没有“监测到啦: nRet is -1”打印, 说明客户端是没有感知的, 客户端是死连接。

我们看一下, 除了情形3外, tcp的正常连接都受到了影响, 而且死连接无法感知, 这显然不符合我们的期望。 那么, 怎么检测tcp死连接呢?  这就是本文要深入讨论的话题------心跳机制

首先自然会问: 什么是心跳机制? 为什么需要心跳机制? 怎么来实现它? 在本文中, 我会和大家一起来学习一下。

想一下, 当tcp连接被破坏后, 如果是死连接了, 服务端和客户端怎样才能知道信息能不能到达对方呢? 很自然的想法是, 不断地给对方发探测信号, 看有没有回应, 这就是心跳机制的直白原理。 所谓的心跳即是数据包, 发心跳就是一方向另一方发送的数据包, 不断地发送, 如果收不到回应, 那么就有理由认为是tcp连接出了问题。 那为什么要叫心跳呢? 你摸一下你的心, 你看它是不是均匀在跳? 理解了吧, 均匀发出去的数据包就类似于均匀的心跳信号。 所以, 我要说: 心跳就是(探测性的)数据包。

到此为主, 我们算是搞懂了什么是心跳机制, 为什么需要心跳机制这两个问题。

下面, 我们会更深入地讨论心跳机制, 并在最后会写个带心跳机制的客户端程序来实战感受一下。

从原理上来讲, 服务端的心跳机制和客户端的心跳机制完全一致, 而且彼此独立。 服务端的心跳只能用来检测服务端的死连接, 客户端的心跳只能检测客户端的死连接。

由于服务端和客户端的心跳原理是基本一致的, 所以为了简便起见, 我们仅仅在客户端启用心跳机制, 然后让客户端去检测一下死连接。

虽然我们说心跳就是数据包, 且我们也可以抓包看到, 但其实这个包的报文段是不含有任何数据的, 因此, 即使你用recv函数, 也不会接收到什么值, 也就是说,如果没有应用层数据通信的话, 即使有循环心跳发送接收, recv也会阻塞在那里, 静静地等待。

既然说到心跳, 我们就不得不说说心跳发送的频率, 根据RFC的定义, TCP/IP协议栈需要等待的默认时间间隔是2小时。 但是, 对于大多数应用程序来说说, 2个小时后才能检测到死连接又有什么意义呢? 我就不明白了, RFC的作者难道傻么 为什么要定义这么长的一个时间? 翻阅资料后才得知: 原来, RFC作者是为了弱化用户使用心跳机制。关于心跳机制,
一直存在这么两派争论, 支持派:可以简化应用程序的设计, 让客户端或者服务端检测到断网。 反对派:心跳机制浪费了带宽, 而且可能会拆掉某个相对良好的tcp连接/通道。

好吧, 现在要解决问题, 要检测死连接, 我们还是要继续介绍心跳机制, 好在, 是有接口可以改变心跳参数的。 让我稍微有点不太乐意的是: 为什么心跳机制检测死连接后, 不指定一个回调的函数接口呢? 不过, 也没关系, 既然你不提供, 那我就开个线程来检测。

当客户端将心跳发给服务端后, 眼巴巴地期望得到服务端的反馈, 如果没有收到反馈, 协议栈自然有理由认为客户端是死连接了(于是, 客户端会发RST包重置链接, 也就是说, 这链接时无效的了),则之后客户端的任何I/O操作或者待处理的I/O操作都将失败。 所以, 自然可以用recv去检测啊, 用recv函数去偷窥接收的内核缓冲区中的数据,
如果反馈-1, 那就表明通信断了(请注意, 实际上, 在此处,recv函数的目的不是为了去获取数据, 也不是为了去探测什么数据, 而是简单地执行一个io操作, 一旦启动心跳机制,协议栈检测到网络异常后,io操作就会自然失败。之所以选择recv, 并把最后一个参数置为MSG_PEEK,  是因为我们要找到一个不影响主线程通信的io操作函数 )
。 顺便说一句, 之前说过, 如果服务端主动关闭通信的socket, 客户端的recv函数会返回0, 所以, 综合起来说, 为了检测出连接的异常,
我们用<=0进行判断。

也啰嗦不少了, 下面给出带有心跳机制的客户端代码吧(说明, 在本文中, 我们认为检测监测是同义词):

  1.  
    #include <winsock2.h>
  2.  
    #include <stdio.h>
  3.  
    #pragma comment(lib, "ws2_32.lib")
  4.  
     
  5.  
    #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR, 4)
  6.  
     
  7.  
    // tcp keepalive结构体
  8.  
    typedef struct tcp_keepalive
  9.  
    {
  10.  
    u_long onoff;
  11.  
    u_long keepalivetime;
  12.  
    u_long keepaliveinterval;
  13.  
    }TCP_KEEPALIVE;
  14.  
     
  15.  
    // 通信的socket
  16.  
    SOCKET sockClient = 0;
  17.  
     
  18.  
    // 监测线程
  19.  
    DWORD WINAPI monitorThread(LPVOID pM)
  20.  
    {
  21.  
    while(1)
  22.  
    {
  23.  
    char szRecvBuf[10] = {0};
  24.  
    int nRet = recv(sockClient, szRecvBuf, 1, MSG_PEEK); // 注意, 最后一个参数必须是MSG_PEEK, 否则会影响主线程接收信息
  25.  
    if(nRet <= 0) // 实际上, 等于0表示服务端主动关闭通信socket
  26.  
    {
  27.  
    printf("监测到啦: nRet is %d\n", nRet);
  28.  
    closesocket(sockClient);
  29.  
    break;
  30.  
    }
  31.  
     
  32.  
    Sleep(200);
  33.  
    }
  34.  
     
  35.  
    return 0;
  36.  
    }
  37.  
     
  38.  
    int main()
  39.  
    {
  40.  
    WORD wVersionRequested;
  41.  
    WSADATA wsaData;
  42.  
    wVersionRequested = MAKEWORD(1, 1);
  43.  
     
  44.  
    WSAStartup( wVersionRequested, &wsaData );
  45.  
    sockClient = socket(AF_INET, SOCK_STREAM, 0);
  46.  
     
  47.  
     
  48.  
    // 启用tcp keepalive机制
  49.  
    #if 1
  50.  
    // 设置SO_KEEPALIVE
  51.  
    int iKeepAlive = 1;
  52.  
    int iOptLen = sizeof(iKeepAlive);
  53.  
    setsockopt(sockClient, SOL_SOCKET, SO_KEEPALIVE, (char *)&iKeepAlive, iOptLen);
  54.  
     
  55.  
    TCP_KEEPALIVE inKeepAlive = {0, 0, 0};
  56.  
    unsigned long ulInLen = sizeof(TCP_KEEPALIVE);
  57.  
    TCP_KEEPALIVE outKeepAlive = {0, 0, 0};
  58.  
    unsigned long ulOutLen = sizeof(TCP_KEEPALIVE);
  59.  
    unsigned long ulBytesReturn = 0;
  60.  
     
  61.  
    // 设置心跳参数
  62.  
    inKeepAlive.onoff = 1; // 是否启用
  63.  
    inKeepAlive.keepalivetime = 1000; // 在tcp通道空闲1000毫秒后, 开始发送心跳包检测
  64.  
    inKeepAlive.keepaliveinterval = 500; // 心跳包的间隔时间是500毫秒
  65.  
     
  66.  
    /*
  67.  
    补充上面的"设置心跳参数":
  68.  
    当没有接收到服务器反馈后,对于不同的Windows版本,客户端的心跳尝试次数是不同的,
  69.  
    比如, 对于Win XP/2003而言, 最大尝试次数是5次, 其它的Windows版本也各不相同。
  70.  
    当然啦, 如果是在Linux上, 那么这个最大尝试此时其实是可以在程序中设置的。
  71.  
    */
  72.  
     
  73.  
     
  74.  
    // 调用接口, 启用心跳机制
  75.  
    WSAIoctl(sockClient, SIO_KEEPALIVE_VALS,
  76.  
    &inKeepAlive, ulInLen,
  77.  
    &outKeepAlive, ulOutLen,
  78.  
    &ulBytesReturn, NULL, NULL);
  79.  
    #endif
  80.  
     
  81.  
    SOCKADDR_IN addrSrv;
  82.  
    addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.1.102");
  83.  
    addrSrv.sin_family = AF_INET;
  84.  
    addrSrv.sin_port = htons(8888);
  85.  
    connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
  86.  
     
  87.  
    // 开启监测线程
  88.  
    HANDLE handle = CreateThread(NULL, 0, monitorThread, NULL, 0, NULL);
  89.  
     
  90.  
    while(1); // 卡住
  91.  
     
  92.  
    CloseHandle(handle);
  93.  
    closesocket(sockClient);
  94.  
    WSACleanup();
  95.  
     
  96.  
    return 0;
  97.  
    }

我们重做实验一, 也就是如下的实验二:

先启动服务端, 再启动有心跳机制的客户端, 建立tcp连接。  用netstat -nao | findstr 8888查看两侧的socket状态, 发现是已经建立连接了。

情形1:

断掉下行网线2, 用netstat -nao | findstr 8888查看两侧的socket状态, 发现客户端socket状态变了, 且“监测到啦: nRet is -1”打印, 但服务端的socket状态没有变化。 这说明:客户端有感知, 但服务端没有感知。 此时, 服务端是死连接。 (因为服务端没有心跳, 所以还是检测不了服务端的死连接)

情形2:

断掉下行网线3, 用netstat -nao | findstr 8888查看两侧的socket状态, 发现客户端socket状态变了, 且有“监测到啦: nRet is -1”打印, 且服务端的socket状态有变化。 这说明:客户端的心跳感知到了死连接, 而且服务端地自己本身的异常也是有感知的(不是借助心跳机制)。

情形3:

断掉上行网线1, 用netstat -nao | findstr 8888查看两侧的socket状态, 发现客户端socket状态未变, 且没有“监测到啦: nRet is -1”打印, 且服务端的socket状态也没有变化。 而且这个时候, tcp连接并不是死连接, 还是活的, 还可以正常通信。 (此时, 通信ok, 没有死连接,  所以心跳机制不会检测到什么死连接)。 有意思的是, 此时, 我pc1上的QQ和pc2上的QQ过了一段时间都各自断了,
说明腾讯QQ客户端也有心跳机制。注意, pc1上的QQ和pc2上的QQ不直接通信哈。

情形4:

断掉路由器电源4, 用netstat -nao | findstr 8888查看两侧的socket状态, 发现客户端socket状态变化, 且有“监测到啦: nRet is -1”打印, 服务端的socket状态也变化。 说明这个时候, 客户端有感知, 服务端也有感知, 两侧都不存在死连接。 (不要心跳机制都能检测到啊, 何况有了心跳机制)

情形5:

直接对pc1的电源线5进行断电(当然, 也要把笔记本pc1的电源拔出来才算数),客户端肯定就没了啊。 此时, 服务端socket状态并没有变化, 说明服务端是没有感知的, 服务端是死连接。(因为服务端没有心跳, 所以还是检测不了服务端死连接)

情形6:

直接对pc2的电源线6进行断电(当然, 也要把笔记本pc2的电源拔出来才算数),服务端肯定就没了啊。 此时,客户端socket状态有变化, 且有“监测到啦: nRet is -1”打印, 说明客户端的心跳对死连接是有感知的。

看来, 心跳机制确实生效了, 以上介绍的主要是tcp协议栈自身提供的心跳机制, 当然, 我们也可以自己在应用层写写自己的心跳机制, 代码会相对复杂一些, 但灵活度也会更大。 从作用上来讲, 殊途同归。总之, 借助心跳机制, 可以检测到tcp连接的异常

最后, 我们来简要说说另外一种网络结构, 假设把pc1和pc2直接用网线相连, 建立起世界最小局域网,  并形成tcp连接。如果在客户端和服务端都没有心跳机制,那么实验结果如下

1. 如果断掉其中的网线, 客户端和服务端都没有感知。

2. 客户端突然断电, 则服务端没有感知。

3.服务端突然断电, 则客户端没有感知。

有兴趣的朋友可以验证一下上述结果。

好了, 心跳机制的介绍到此为止。 未来, 路漫漫, 但必将继续勇敢前行!

---------------------

本文来自 stpeace 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/stpeace/article/details/44162349?utm_source=copy

心跳机制tcp keepalive的讨论、应用及“断网”、"断电"检测的C代码实现(Windows环境下)的更多相关文章

  1. TCP连接探测中的Keepalive和心跳包. 关键字: tcp keepalive, 心跳, 保活

    1. TCP保活的必要性 1) 很多防火墙等对于空闲socket自动关闭 2) 对于非正常断开, 服务器并不能检测到. 为了回收资源, 必须提供一种检测机制. 2. 导致TCP断连的因素 如果网络正常 ...

  2. C#之实现Scoket心跳机制

    C#之实现Scoket心跳机制 标签: UnityC#TCPSocket心跳 2017-05-17 09:58 1716人阅读 评论(0) 收藏 举报  分类: Unity(134)  C#(6)  ...

  3. TCP长连接与短连接、心跳机制

    1. TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是需要三次 ...

  4. TCP心跳 | TCP keepAlive(转)

    应用层对于每个socket采用如下函数来开启 keepalive机制,其参数将采用系统上述配置. setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&a ...

  5. [转]Android TCP长连接 心跳机制及实现

    背景知识 智能手机上的长连接心跳和在Internet上的长连接心跳有什么不同 Android系统的推送和iOS的推送有什么区别 几种推送的实现方式 协议 1XMPP简介 2 MQTT简介 3移动端消息 ...

  6. 【MINA】心跳机制

    列上两篇好文章 http://www.cnblogs.com/pricks/p/3832882.html http://blog.csdn.net/cruise_h/article/details/1 ...

  7. Java: server/client 心跳机制实现 示例

    心跳机制 心跳机制是定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制. 大部分CS的应用需要心跳机制.心跳机制一般在Server和Client都要实现,两者实现原理 ...

  8. 连接管理 与 Netty 心跳机制

    一.前言 踏踏实实,动手去做,talk is cheap, show me the code.先介绍下基础知识,然后做个心跳机制的Demo. 二.连接 长连接:在整个通讯过程,客户端和服务端只用一个S ...

  9. TCP长连接保持连接状态TCP keepalive设置

    如有转载,请注明出处:http://blog.csdn.net/embedded_sky/article/details/42077321 作者:super_bert@csdn 对于TCP长连接保活是 ...

随机推荐

  1. 无鼠标Windows操作

    1.常用tab键,方便跳转 2.打开软件方式:  1.win+1,2,3...依序打开任务栏图标.常用软件可以放在这里: 2.创建quickStart文件夹,配置路径,将所有要用到的功能都放在这里.( ...

  2. Golang 实现简单的滚动读取文本更新

    这个小程序要实现的效果,简单地说,就是将目标文件的内容读取输出到终端,并且目标文件并不是静态的,而是随时会添加新的内容.我们的目标就是一旦目标文件添加了新的内容,就把它读取出来并且显示到终端上. 实现 ...

  3. mysql设置时区方法

    set global time_zone = '+2:00'; ##修改mysql全局时区 set time_zone = '+2:00'; ##修改当前会话时区 flush privileges; ...

  4. NOI十连测 第五测 T1

    #include<cstdio> #include<cstring> #include<cmath> #include<iostream> #inclu ...

  5. setCentralWidget就可以把Qwidget设置为QMainWindow的主窗口

    前面说的return app.exec() 这句话是用来使程序进入事件循环,除了直接递交的事件外,所有的事件都要在这个循环中被一层一层的分发,最后找到相应的处理函数来处理事件. 顶级窗口和顶级窗口是存 ...

  6. JavaMail API 概述

    JavaMail API提供了一种与平台无关和协议独立的框架来构建邮件和消息应用程序. JavaMail API提供了一组抽象类定义构成一个邮件系统的对象.它是阅读,撰写和发送电子信息的可选包(标准扩 ...

  7. Android 音视频开发(一) : 通过三种方式绘制图片

    版权声明:转载请说明出处:http://www.cnblogs.com/renhui/p/7456956.html 在 Android 音视频开发学习思路 里面,我们写到了,想要逐步入门音视频开发,就 ...

  8. MySQL Transaction--查看未提交事务执行的SQL

    未提交事务 长期未提交事务,指开启事务后,长时间未向MySQL发出SQL执行请求或事务处理(COMMIT/ROLLBACK)请求,在系统表`information_schema`.`INNODB_TR ...

  9. Genius ACM

    题解: 发现匹配一定会选最大和最小匹配,确定左右端点之后nlogn排序后算 比较容易想到二分 最坏情况每次1个 $n^2*(logn)^2$ 没错暴力的最差复杂度是$n^2*logn$的 发现长度与次 ...

  10. thinkphp设置默认访问的模块

    在index.php中加入(可以省略Home,直接写控制器和方法访问) define('BIND_MODULE','Home'); 在config.php里边加入 'MODULE_ALLOW_LIST ...