udp接收并存储,速度太慢了。双线程有什么思路?

bo.cui 2009-11-07 10:36:04
我用UDP接受数据,不验证错误。
单线程,XP下,双核1.6的笔记本,包大小1024B(1KB),每收到32KB写一次硬盘。
接收速度大于30M小b/秒就丢包。。

怎么这么慢呀? 网卡只收能到100,单写硬盘也能到10以上。为何加到一起就慢到这程度了??

几乎同样算法的程序,linux下,包大小1024B,但是每收到一个包就知道写一下硬盘(就是1KB一写)。
理论上应该比xp慢的多(xp下1KB一写慢的没话说了),但是却几乎能到50Mb不丢包。。

但是开发必须用XP。
现在做了个XP下的多线程,还是没有linux下单线的快。 能到30M就不错了。。。没什么提升。

谁能帮忙说说是什么问题? 有什么解决办法?
我没怎么做过windos编程。 不知道是不是程序写的有问题。。。





多线程代码(vc2008):


#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <conio.h>

#pragma comment(lib,"Ws2_32.lib")

#define ALLOWERRORMAX 100 //允许的最大错误编号,大于此值的错误将导致程序终止
#define MAINSLEEPTIME 1000 //主线程错误检测间隔,毫秒

#define UDPPACKSIZE 1024 //UDP数据包大小
#define SOCKETWAITTIME 200 //网速速度过快时挂起等待时间,毫秒,测试用
#define DATABUFFSIZE 8192 //数据缓冲区大小,单位-UDPPACKSIZE
#define NETPORT 8000 //端口

int ERR_FLAG = 0; //错误标记
HANDLE TUWhEvent; //用于防止写入速度超过网络接受的事件
char (*DataBuff)[UDPPACKSIZE]; //数据缓冲区指针
//线程状态
struct ThreadAttr
{
unsigned long int Count;//计数器
int TEndFlag; //结束控制标记
int ErrFlag; //错误标记
};
volatile struct ThreadAttr TAUdpRec;
volatile struct ThreadAttr TAWriteDist;
FILE *TWriteDist_fdest; //数据写入位置
SOCKET TUdpRec_sUDP; //数据来源网络

void PrtErrorMessage(int err); //打印错误详情
int TcloseUdprec(void); //关闭udp线程
int TcloseWriteDist(void); //关闭写数据线程
void TUdpRec(void *Attr); //从UDP接受数据
void TWriteDist(void *Attr); //写入数据到内存


//主线程,控制线程
int main(void)
{

if((DataBuff = (char (*)[UDPPACKSIZE])malloc(DATABUFFSIZE*UDPPACKSIZE)) == NULL)//创建缓冲区
{
ERR_FLAG = 1;
PrtErrorMessage(ERR_FLAG);
return 0;
}

TUWhEvent = CreateEvent(NULL, TRUE, FALSE, NULL);//创建事件
//创建线程
_beginthread(TWriteDist, 0, NULL);
_beginthread(TUdpRec, 0, NULL);

//主线程
while(!_kbhit())
{
//一般错误
if(TAUdpRec.ErrFlag>ALLOWERRORMAX)
{
ERR_FLAG = TAUdpRec.ErrFlag;
PrtErrorMessage(ERR_FLAG);
}
if(TAWriteDist.ErrFlag>ALLOWERRORMAX)
{
ERR_FLAG = TAWriteDist.ErrFlag;
PrtErrorMessage(ERR_FLAG);
}
//致命错误
if(TAWriteDist.ErrFlag && TAUdpRec.ErrFlag<ALLOWERRORMAX)
{
ERR_FLAG = TAUdpRec.ErrFlag;
PrtErrorMessage(ERR_FLAG);
break;
}
if(TAWriteDist.ErrFlag && TAWriteDist.ErrFlag<ALLOWERRORMAX)
{
ERR_FLAG = TAWriteDist.ErrFlag;
PrtErrorMessage(ERR_FLAG);
break;
}
//PrtErrorMessage(ERR_FLAG);
Sleep(MAINSLEEPTIME);
}
//结束子线程
TcloseUdprec();
TcloseWriteDist();

printf("Client@> \n rec:%lu\n write:%lu\n\n", TAUdpRec.Count, TAWriteDist.Count);

free(DataBuff);
printf("Client@> Main Thread End.\n");
getchar();
getchar();
return 0;
}


//关闭udp线程
int TcloseUdprec(void)
{
TAUdpRec.TEndFlag = 1;
closesocket(TUdpRec_sUDP);
while(TAUdpRec.TEndFlag);

return TAUdpRec.ErrFlag;
}


//关闭写数据线程
int TcloseWriteDist(void)
{
TAWriteDist.TEndFlag = 1;
SetEvent(TUWhEvent);//使写入线程运行,以便进行必要的清理
while(TAWriteDist.TEndFlag);

return TAWriteDist.ErrFlag;
}


//从UDP接受数据
void TUdpRec(void *Attr)
{
WSADATA wsa;
struct sockaddr_in addr, from;
int SAddrLen = sizeof(struct sockaddr);
unsigned int RecvLen;//接收包长度

WSAStartup(0x101, &wsa);//载入合适的Winsock动态链接库
//初始化本地网络
TUdpRec_sUDP = socket(AF_INET, SOCK_DGRAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(NETPORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(TUdpRec_sUDP, (struct sockaddr*)&addr, sizeof(addr));//绑定到所有可用地址

TAUdpRec.Count = 0;
TAUdpRec.TEndFlag = 0;
TAUdpRec.ErrFlag = 0;
while(!TAUdpRec.TEndFlag)
{
if(TAUdpRec.Count-TAWriteDist.Count >= DATABUFFSIZE)
{
Sleep(SOCKETWAITTIME);
continue;
}
if((RecvLen = recvfrom(TUdpRec_sUDP, DataBuff[TAUdpRec.Count % DATABUFFSIZE], UDPPACKSIZE, 0, (struct sockaddr *)&from, &SAddrLen)) == SOCKET_ERROR)
{
if(!TAUdpRec.TEndFlag)
TAUdpRec.ErrFlag = 201;
else
break;//关闭socket时执行
}
TAUdpRec.Count++;
SetEvent(TUWhEvent);//当收到数据后置位事件,使写入线程运行
}
closesocket(TUdpRec_sUDP);
WSACleanup();//释放占用资源

printf("Client@> TUdpRec Thread End.\n");
TAUdpRec.TEndFlag = 0;
_endthread();
}


//写入数据到内存
void TWriteDist(void *Attr)
{
TAWriteDist.Count = 0;
TAWriteDist.TEndFlag = 0;//默认执行
TAWriteDist.ErrFlag = 0;
if((TWriteDist_fdest=fopen("UDP.DAT", "wb")) == NULL)
{
TAWriteDist.ErrFlag = 2;
_endthread();
}
fseek(TWriteDist_fdest, 0L, SEEK_SET);

while(1)
{
if(TAUdpRec.Count <= TAWriteDist.Count)
{
ResetEvent(TUWhEvent);//复位事件,使自己阻塞
}
WaitForSingleObject(TUWhEvent, INFINITE);//挂起,直到事件被置位
if(TAWriteDist.TEndFlag)
break;
fwrite(DataBuff[TAWriteDist.Count % DATABUFFSIZE], UDPPACKSIZE, 1, TWriteDist_fdest);
TAWriteDist.Count++;
}
//执行善后操作,存储当前已接收数据
// while(TAUdpRec.Count > TAWriteDist.Count)
// {
// fwrite(DataBuff[TAWriteDist.Count % DATABUFFSIZE], UDPPACKSIZE, 1, TWriteDist_fdest);
// TAWriteDist.Count++;
// }

fflush(TWriteDist_fdest);
fclose(TWriteDist_fdest);

printf("Client@> TWriteDist Thread End.\n");
TAWriteDist.TEndFlag = 0;
_endthread();
}


//打印错误详情,大于ALLOWERRORMAX的错误将导致程序终止
void PrtErrorMessage(int err)
{
switch(err)
{
case 0:
printf("Client@> run normal.\n");
break;
case 1:
printf("Client/Error@> malloc DataBuff unsuccessful.\n");
break;
case 2:
printf("Client/Error@> open udp.dat file unsuccessful.\n");
break;
case 201:
printf("Client/Warning@> recvfrom udp data exception.\n");
break;
default:
printf("Client/Warning@> undefine type.\n");
}
}


...全文
1065 43 打赏 收藏 转发到动态 举报
写回复
用AI写文章
43 条回复
切换为时间正序
请发表友善的回复…
发表回复
interlace2011 2011-08-10
  • 打赏
  • 举报
回复

void
interlace2011 2011-08-10
  • 打赏
  • 举报
回复
#include <stdio.h>
bo.cui 2009-11-08
  • 打赏
  • 举报
回复
还有特别谢谢

rulary
(小星星)



jackyjkchen
(jackyjkchen)

^^
bo.cui 2009-11-08
  • 打赏
  • 举报
回复
~~~

貌似我的板子的问题。。。

我的程序在一个工控板上。。

我也用PC测试了一下。 发送30M的文件 速度100基本不怎么丢。 但是发大的文件丢的还是比较多。

·~~~

现在的程序:
基本已经是
rulary
(小星星)
的思路了~~ 稍微不同一点而已~~

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <conio.h>

#pragma comment(lib,"Ws2_32.lib")

#define ALLOWERRORMAX 100 //允许的最大错误编号,大于此值的错误将导致程序终止
#define MAINSLEEPTIME 1000 //主线程错误检测间隔,毫秒

#define UDPPACKSIZE 1024 //UDP数据包大小
#define SOCKETWAITTIME 10 //网速速度过快时挂起等待时间,毫秒,测试用
#define DATABUFFSIZE 8192 //数据缓冲区大小,单位-UDPPACKSIZE
#define BUFFWRITESIZE 2048 //缓冲写(延时写入)大小,单位-UDPPACKSIZE
#define NETPORT 8000 //端口

int ERR_FLAG = 0; //错误标记
char (*DataBuff)[UDPPACKSIZE]; //数据缓冲区指针
//线程状态
struct ThreadAttr
{
unsigned long int Count;//计数器
int TEndFlag; //结束控制标记
int ErrFlag; //错误标记
};
volatile struct ThreadAttr TAUdpRec;
volatile struct ThreadAttr TAWriteDist;
FILE *TWriteDist_fdest; //数据写入位置
SOCKET TUdpRec_sUDP; //数据来源网络

void PrtErrorMessage(int err); //打印错误详情
int TcloseUdprec(void); //关闭udp线程
int TcloseWriteDist(void); //关闭写数据线程
void TUdpRec(void *Attr); //从UDP接受数据
void TWriteDist(void *Attr); //写入数据到内存


//主线程,控制线程
int main(void)
{

if((DataBuff = (char (*)[UDPPACKSIZE])malloc(DATABUFFSIZE*UDPPACKSIZE)) == NULL)//创建缓冲区
{
ERR_FLAG = 1;
PrtErrorMessage(ERR_FLAG);
return 0;
}

//创建线程
_beginthread(TWriteDist, 0, NULL);
_beginthread(TUdpRec, 0, NULL);

//主线程
while(!_kbhit())
{
//一般错误
if(TAUdpRec.ErrFlag>ALLOWERRORMAX)
{
ERR_FLAG = TAUdpRec.ErrFlag;
PrtErrorMessage(ERR_FLAG);
}
if(TAWriteDist.ErrFlag>ALLOWERRORMAX)
{
ERR_FLAG = TAWriteDist.ErrFlag;
PrtErrorMessage(ERR_FLAG);
}
//致命错误
if(TAWriteDist.ErrFlag && TAUdpRec.ErrFlag<ALLOWERRORMAX)
{
ERR_FLAG = TAUdpRec.ErrFlag;
PrtErrorMessage(ERR_FLAG);
break;
}
if(TAWriteDist.ErrFlag && TAWriteDist.ErrFlag<ALLOWERRORMAX)
{
ERR_FLAG = TAWriteDist.ErrFlag;
PrtErrorMessage(ERR_FLAG);
break;
}
//PrtErrorMessage(ERR_FLAG);
Sleep(MAINSLEEPTIME);
}
//结束子线程
TcloseUdprec();
TcloseWriteDist();

printf("Client@> \n rec:%lu\n write:%lu\n\n", TAUdpRec.Count, TAWriteDist.Count);

//free(DataBuff);
printf("Client@> Main Thread End.\n");
getchar();
getchar();
return 0;
}


//关闭udp线程
int TcloseUdprec(void)
{
TAUdpRec.TEndFlag = 1;
closesocket(TUdpRec_sUDP);
while(TAUdpRec.TEndFlag);

return TAUdpRec.ErrFlag;
}


//关闭写数据线程
int TcloseWriteDist(void)
{
TAWriteDist.TEndFlag = 1;
while(TAWriteDist.TEndFlag);//等待清理

return TAWriteDist.ErrFlag;
}


//从UDP接受数据
void TUdpRec(void *Attr)
{
WSADATA wsa;
struct sockaddr_in addr, from;
int SAddrLen = sizeof(struct sockaddr);
unsigned int RecvLen;//接收包长度

WSAStartup(0x101, &wsa);//载入合适的Winsock动态链接库
//初始化本地网络
TUdpRec_sUDP = socket(AF_INET, SOCK_DGRAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(NETPORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(TUdpRec_sUDP, (struct sockaddr*)&addr, sizeof(addr));//绑定到所有可用地址

TAUdpRec.Count = 0;
TAUdpRec.TEndFlag = 0;
TAUdpRec.ErrFlag = 0;
while(!TAUdpRec.TEndFlag)
{
if(TAUdpRec.Count-TAWriteDist.Count >= DATABUFFSIZE)//防止接受超越写入一圈
{
Sleep(SOCKETWAITTIME);
continue;
}
if((RecvLen = recvfrom(TUdpRec_sUDP, DataBuff[TAUdpRec.Count % DATABUFFSIZE], UDPPACKSIZE, 0, (struct sockaddr *)&from, &SAddrLen)) == SOCKET_ERROR)
{
if(!TAUdpRec.TEndFlag)
TAUdpRec.ErrFlag = 201;
else
break;//关闭socket时执行
}
TAUdpRec.Count++;
}
closesocket(TUdpRec_sUDP);
WSACleanup();//释放占用资源

printf("Client@> TUdpRec Thread End.\n");
TAUdpRec.TEndFlag = 0;
}

//写入数据到硬盘
void TWriteDist(void *Attr)
{
TAWriteDist.Count = 0;
TAWriteDist.TEndFlag = 0;//默认执行
TAWriteDist.ErrFlag = 0;
if((TWriteDist_fdest=fopen("UDP.DAT", "wb")) == NULL)
{
TAWriteDist.ErrFlag = 2;
_endthread();
}
fseek(TWriteDist_fdest, 0L, SEEK_SET);

while(1)
{
if(TAWriteDist.TEndFlag)
break;
if(TAUdpRec.Count <= TAWriteDist.Count)
{
continue;//不写,没有新数据
}
fwrite(DataBuff[TAWriteDist.Count % DATABUFFSIZE], UDPPACKSIZE, 1, TWriteDist_fdest);
TAWriteDist.Count++;
}
//执行善后操作,存储当前已接收数据
while(TAUdpRec.Count > TAWriteDist.Count)
{
fwrite(DataBuff[TAWriteDist.Count % DATABUFFSIZE], UDPPACKSIZE, 1, TWriteDist_fdest);
TAWriteDist.Count++;
}

fflush(TWriteDist_fdest);
fclose(TWriteDist_fdest);

printf("Client@> TWriteDist Thread End.\n");
TAWriteDist.TEndFlag = 0;
}


//打印错误详情,大于ALLOWERRORMAX的错误将导致程序终止
void PrtErrorMessage(int err)
{
switch(err)
{
case 0:
printf("Client@> run normal.\n");
break;
case 1:
printf("Client/Error@> malloc DataBuff unsuccessful.\n");
break;
case 2:
printf("Client/Error@> open udp.dat file unsuccessful.\n");
break;
case 201:
printf("Client/Warning@> recvfrom udp data exception.\n");
break;
default:
printf("Client/Warning@> undefine type.\n");
}
}
jernymy 2009-11-08
  • 打赏
  • 举报
回复
mark 一下
codesnail 2009-11-08
  • 打赏
  • 举报
回复
恩,应该是协议层的问题,如果底层的驱动有速度限制,上层应用优化提高的空间就很小了
luc_cj 2009-11-08
  • 打赏
  • 举报
回复
额,不懂的太多,向各位前辈致敬了
fblgzdq 2009-11-08
  • 打赏
  • 举报
回复
用缓存的说
比较好
xidianwk 2009-11-08
  • 打赏
  • 举报
回复
修改UDP默认的缓存大小。。。
anhongsen521 2009-11-08
  • 打赏
  • 举报
回复
jackyjkchen 2009-11-07
  • 打赏
  • 举报
回复
没有必要频繁写盘,可以用延时写入,数据累积到几M的时候再写,下载工具不都是这样的么
bo.cui 2009-11-07
  • 打赏
  • 举报
回复
//写入数据到内存
void TWriteDist(void *Attr)

更正:
//写入数据到硬盘
void TWriteDist(void *Attr)
bo.cui 2009-11-07
  • 打赏
  • 举报
回复
回楼上:
写线程只被置位一次,但是会写2个包。
因为只有写线程的速度等于接收线程,写线程才会自己阻塞自己。

while(1)
{
if(TAUdpRec.Count <= TAWriteDist.Count)//当写入磁盘计数等于接收计数
{
ResetEvent(TUWhEvent);//复位事件,使自己阻塞
}
WaitForSingleObject(TUWhEvent, INFINITE);//挂起,直到事件被置位
。。。。




说实在的 windows编程我也是现学现卖。 linux出身呀~~ 没办法 工作需要。。
wangqingshui531 2009-11-07
  • 打赏
  • 举报
回复
应该可以达到90多M/s,以前使用瑞士军刀在xp接受UDP包,确实可以达到接近100M的速度,不过当初是网络结构比较简单。

不知道清楚windows下多线程编程
问下如果:
接受线程先运行两次,那么相当于 SetEvent(TUWhEvent);//当收到数据后置位事件,使写入线程运行
对该事件置位两次,现在接收线程假设暂停。

写线程的“WaitForSingleObject(TUWhEvent, INFINITE);//挂起,直到事件被置位”是否被唤醒两次?

如果windows下的事件机制和linux下的signal机制一样信号不能重叠,事件只能被唤醒一次,忽略第二个置位事件,那么程序一定会发生丢包的。
bo.cui 2009-11-07
  • 打赏
  • 举报
回复
关键是怎么能快上来?
我的代码有没有什么问题??

怎么能传输速度到100Mb/s?? 能不能?
phpjspasp 2009-11-07
  • 打赏
  • 举报
回复
是不是网络的问题?楼主你的机器在以太网上?
  • 打赏
  • 举报
回复
接收速度大于30M小b/秒就丢包。。
理论上应该比xp慢的多(xp下1KB一写慢的没话说了),但是却几乎能到50Mb不丢包。。


这个貌似瓶颈已经不在你的代码了吧,应该是底层协议栈实现的问题吧。
rularys 2009-11-07
  • 打赏
  • 举报
回复
没有用过UDP,
我用楼主的程序(稍微改了些东西)自己试了一下,在局域网中Server端发送速度为85Mb/s(10MB/s)的情况下,发送102400KB的数据Client端能收到102300KB左右的数据,丢失了100个左右的包(每个包1KB大小),缓冲区从来没有满过,就是收包线程从来不用等待写盘线程。倒是没你说的100M丢20M这么严重。不过如果测试更大的数据可能不是这个情况。

在自己电脑上试的话,575Mb/s(71MB/s)的速度同样发送102400KB的数据,丢包数在50个以下;同样缓冲区没有报警过。
bo.cui 2009-11-07
  • 打赏
  • 举报
回复
我的磁盘是CF卡。。。

就那个速度呀。。 程序其实是在工控板子上运行。 什么V86的处理器

开始是用DOS写的。 后来发现速度不行。
就考虑DOS多线程,弄了个ERTOS 还是很慢。
才换的XP。。。 结果还是慢的要死

主频1G 应该不会丢这么多才对。。

关键是这个程序在电脑上传输也会丢包。。 50Mb都到不了。。
rularys 2009-11-07
  • 打赏
  • 举报
回复
[Quote=引用 27 楼 cuibo1123 的回复:]
rulary
(小星星)

可是我写的和你说的也差不多呀~~~

只是我没有动态扩充缓存
关键是我的内存太小,而且程序需要长时间运行,如果写入太慢,缓冲也不太适合。。 因为早晚的申请不来开始大量丢包。。而项目不允许这样的丢包。。。
[/Quote]

内存大小影响不是很大,你有1G以上的内存空间。我觉得磁盘IO效率再怎么低效,也不至于像楼主说的那样。楼主试试用映射文件。

我试过TCP传文件,在局域网中速度达到100Mbytes/s以上都能应付
加载更多回复(23)

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧