以太网(Ethernet)数据帧的长度必须在46-1500字节之间,这是由以太网的物理特性决定的.
这个1500字节被称为链路层的MTU(最大传输单元).
但这并不是指链路层的长度被限制在1500字节,其实这这个MTU指的是链路层的数据区.
并不包括链路层的首部和尾部的18个字节.
因为IP数据报的首部为20字节,所以IP数据报的数据区长度最大为1480字节.
又因为UDP数据报的首部8字节,所以UDP数据报的数据区最大长度为1472字节.
鉴于Internet上的标准MTU值为576字节,所以我建议在进行Internet的UDP编程时.
网络延迟测试程序
服务器端:
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("192.168.1.101");
addr.sin_port = htons(7000);
int len = sizeof(addr);
struct sockaddr_in addr_server;
addr_server.sin_family = AF_INET;
addr_server.sin_port = htons(8000);
addr_server.sin_addr.s_addr = inet_addr("192.168.1.101");
SOCKET serversocket = socket(AF_INET,SOCK_DGRAM,0);
bind(serversocket,(struct sockaddr*)&addr_server,sizeof(addr_server));
struct sockaddr_in addr_from;
int fromlen = sizeof(addr_from);
while(1){
char frombuff[1024] = "\0";
printf("等待客户端输入信息!");
if(recvfrom(serversocket,frombuff,sizeof(frombuff),0,(struct sockaddr*)&addr_from,&fromlen) == SOCKET_ERROR){
printf("服务端接受有错误!!");
}
printf("客户端输入的是:%s\n",frombuff);
//Sleep(5000);
if(sendto(serversocket,frombuff,sizeof(frombuff),0,(struct sockaddr*)&addr,fromlen) == SOCKET_ERROR){
printf("服务端发送错误!!");
}
}
closesocket(serversocket);
WSACleanup();
return 0;
}
客户端:
#include <stdio.h>
#include <winsock2.h>
#include <time.h>
#pragma comment(lib,"ws2_32.lib")
int main(void)
{
WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
SOCKET mySocket;
mySocket = socket(AF_INET,SOCK_DGRAM,0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("192.168.1.101");
addr.sin_port = htons(8000);
int len = sizeof(addr);
struct sockaddr_in addr_client;
addr_client.sin_family = AF_INET;
addr_client.sin_port = htons(7000);
addr_client.sin_addr.s_addr = inet_addr("192.168.1.101");
SOCKET clientsocket = socket(AF_INET,SOCK_DGRAM,0);
bind(clientsocket,(struct sockaddr*)&addr_client,sizeof(addr_client));
while(1){
printf("请输入一段字符串用户测试延迟:");
char buff[1024] = "\0";
scanf("%s",buff);
LARGE_INTEGER t1,t2,feq;
QueryPerformanceFrequency(&feq);//每秒跳动次数
if(sendto(clientsocket,buff,sizeof(buff),0,(struct sockaddr *)&addr,len) == SOCKET_ERROR) {
printf("发送错误!!!");
}
QueryPerformanceCounter(&t1);//测前跳动次数
if(recvfrom(clientsocket,buff,sizeof(buff),0,(struct sockaddr*)&addr,&len) == SOCKET_ERROR) {
printf("接受错误!!!");
}
QueryPerformanceCounter(&t2);//测后跳动次数
printf("从服务端返回:%s\n",buff);
printf("--->>时间延迟:%f秒\n",((double)t2.QuadPart-(double)t1.QuadPart)/((double)feq.QuadPart));
}
closesocket(mySocket);
WSACleanup();
return 0;
}
主要就是用了QueryPerformanceFrequency(&feq)高精度性能计时器的频率函数和QueryPerformanceCounter(&t1)函数,
QueryPerformanceFrequency() - 基本介绍
类型:Win32API
原型:BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
作用:返回硬件支持的高精度计数器的频率。
返回值:非零,硬件支持高精度计数器;零,硬件不支持,读取失败。
QueryPerformanceFrequency() - 技术特点供WIN9X使用的高精度定时器:QueryPerformanceFrequency()和QueryPerformanceCounter(),要求计算机从硬件上支持高精度定时器。
函数的原形是:
BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
BOOL QueryPerformanceCounter (LARGE_INTEGER *lpCount);
数据类型LARGEINTEGER既可以是一个作为8字节长的整数,也可以是作为两个4字节长的整数的联合结构,其具体用法根据编译器是否支持64位而定。该类型的定义如下:
typeef union _ LARGE_INTEGER
{
struct
{
DWORD LowPart;
LONG HighPart;
};
LONGLONG QuadPart;
} LARGE_INTEGER;
在定时前应该先调用QueryPerformanceFrequency()函数获得机器内部计时器的时钟频率。接着在需要严格计时的事件发生前和发生之后分别调用QueryPerformanceCounter(),利用两次获得的计数之差和时钟频率,就可以计算出事件经历的精确时间。测试函数SLEEP(100)的精确持续时间方法:
LARGE_INTEGER litmp;
LONGLONG qt1,qt2;
double dft,dff,dfm;
QueryPerformanceFrequency(&litmp);//获得时钟频率
dff=(double)litmp.QuadPart;
QueryPerformanceCounter(&litmp);//获得初始值
qt1=litmp.QuadPart;Sleep(100);
QueryPerformanceCounter(&litmp);//获得终止值
qt2=litmp.QuadPart;
dfm=(double)(qt2-qt1);
dft=dfm/dff;//获得对应的时间值
需要注意的是DFT计算的结果单位是秒。