关于提高UDP发送效率的方法
UDP的发送效率和什么因素有关呢?
直观觉得,UDP的切包长越大,应该发送效率越高(最长为65536)。可是依据实际測试和在网上查到的资料的结果,包长度为1024为发送效率最高。
这样的结果让人感到疑惑,为什么是1024这样的奇怪的值呢?为什么不是MTU(最小发送单元)的长度(即1500-28)呢?
后来调查发现,Windows的网络底层,默认UDP分片长度为1024时,走的是高速通道模式,详细如何的高速通道?没有再继续深入研究。
通过改动以下的注冊表能够加大1024.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters\FastSendDatagramThreshold
而且须要改动网卡注冊表的MTU与上面的值一致,详细注冊表项例如以下所看到的:
HKEY_LOCAL_MACHINE\\SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces\MTU
改动以上注冊表值的演示样例代码:
// 改动本地UDP包发送长度并依据网络MTU确定实际UDP发送的包长度
// <span style="font-family: Arial, Helvetica, sans-serif;">lenPacket【out】:包长度,</span><span style="font-family: Arial, Helvetica, sans-serif;">bIsToRestartComputer【out】:是否重新启动计算机(当改动了注冊表,则须要重新启动有效)</span>
<pre name="code" class="cpp"><span style="font-family:Arial, Helvetica, sans-serif;">// 返回:TRUE(成功),FALSE(失败)</span>
BOOL SetMaxEfficencyUDPPacketLength(INT &lenPacket, BOOL &bIsToRestartComputer) { // 初始化 lenPacket = MAX_SUPER_DISPLAY_UDP_LENGTH; // 设置【HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters】 // 加入FastSendDatagramThreshold=1500 if (!CUtil::IsKeyExist(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\AFD\\Parameters"), _T("FastSendDatagramThreshold"))) { // 假设写入失败,则依照发送 if (!CUtil::WriteKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\AFD\\Parameters"), _T("FastSendDatagramThreshold"), MTU_DEFAULT)) { lenPacket = NOT_SET_FAST_SEND_DATAGRAME_UDP_LENGTH; return FALSE; } bIsToRestartComputer = TRUE; } // 校验值 else { INT iValue = 0; // 获取键值 if (CUtil::ReadKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\AFD\\Parameters"), _T("FastSendDatagramThreshold"), iValue)) { // 假设键值不是MTU默认值,则改动 if (iValue != MTU_DEFAULT) { // 假设写入失败,则依照发送 if (!CUtil::WriteKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\AFD\\Parameters"), _T("FastSendDatagramThreshold"), MTU_DEFAULT)) { lenPacket = NOT_SET_FAST_SEND_DATAGRAME_UDP_LENGTH; return FALSE; } bIsToRestartComputer = TRUE; } } } // 设置【HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces】 // 改动MTU=1500 // 假设不存在,则返回 HKEY hMainKey = NULL; LONG lRetCode = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces"),0,KEY_READ,&hMainKey); if(lRetCode != ERROR_SUCCESS) { return TRUE; } // 遍历全部网口,改动MTU DWORD dwIndex = 0; TCHAR swzSubKey[MAX_PATH] = _T(""); DWORD dwNameLen = MAX_PATH; while(ERROR_SUCCESS == ::RegEnumKeyEx(hMainKey, dwIndex, swzSubKey, &dwNameLen, NULL, NULL, NULL, NULL)) { // 构造子键全路径 CString strFullSubKey = _T("SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces\\"); strFullSubKey += swzSubKey; // 改动MTU if (CUtil::IsKeyExist(HKEY_LOCAL_MACHINE, strFullSubKey, _T("MTU"))) { INT iValue = 0; // 获取键值 if (CUtil::ReadKey(HKEY_LOCAL_MACHINE, strFullSubKey, _T("MTU"), iValue)) { // 假设键值不是MTU默认值,则改动 if (iValue != MTU_DEFAULT) { // 假设写入失败,则依照发送 if (!CUtil::WriteKey(HKEY_LOCAL_MACHINE, strFullSubKey, _T("MTU"), MTU_DEFAULT)) { lenPacket = NOT_SET_FAST_SEND_DATAGRAME_UDP_LENGTH; return FALSE; } bIsToRestartComputer = TRUE; } } } // 重置缓存 memset(swzSubKey, 0, sizeof(swzSubKey)); dwNameLen = MAX_PATH; // 下一个子项 dwIndex ++; } ::RegCloseKey(hMainKey); return TRUE; }
可是须要注意的是,改动此值须要确保小于或者等于整个网络路径的MTU,如何检查整个网络的MTU呢?能够通过运行以下指令获取:
ping -f -n 1 -l 1472 192.168.0.2
当中,1472为发送的包长度,假设运行结果为0,表示可以发送;可以继续提高1472,否则减少;直至获取最大值。
详细代码例如以下所看到的:
// 获取网络MTU // ulDestIP【in】:目标IP // 返回:MTU UINT GetLanMTU(ULONG ulDestIP) { // 初始化 UINT lenPacket = NOT_SET_FAST_SEND_DATAGRAME_UDP_LENGTH; // 指令缓存 CHAR szCmdBuf[MIDDLE_BUF_LENGTH]; memset(szCmdBuf, 0, sizeof(szCmdBuf)); // 构造指令 sprintf_s(szCmdBuf, MIDDLE_BUF_LENGTH, "ping -f -n 1 -l 1472 %s", inet_ntoa(*(struct in_addr *)&ulDestIP)); // 运行指令 INT iRet = system(szCmdBuf); // 假设MTU是 if (iRet == 0) { lenPacket = MAX_SUPER_DISPLAY_UDP_LENGTH; }else { CUtil::OutputConsoleLogString("LAN's MTU isn't 1500"); } return lenPacket; }