大家好:
小弟现在在做一个项目,做一个应用程序(windows),可以和一个文件服务器(linux)进行通讯,文件服务器也是公司定制的,应用程序和服务器之间定制了一些通讯接口,如GetDir(获取文件夹),GetFileSize(获取文件大小)等等,这些接口是基于HTTP协议的,每次通讯结束后,服务器就关闭接口,也就是说不是永久连接。
我是基于阻塞模式的,为了不影响界面的刷新,我把通讯的过程做成了一个thread,数据收到以后通过回调函数在通知界面,界面再做刷新。就是下面的伪码。
sendrecventry()
{
send();
recv();
/**/
postdatatoui();
return 0;/*线程返回*/
}
GetDir()
{
int handle = createthread(0,0,sendrecventry,0);
} GetFileSize()
{
int handle = createthread(0,0,sendrecventry,0);
}
请各位兄弟帮忙看一下这样的方法是否合理?
小弟现在在做一个项目,做一个应用程序(windows),可以和一个文件服务器(linux)进行通讯,文件服务器也是公司定制的,应用程序和服务器之间定制了一些通讯接口,如GetDir(获取文件夹),GetFileSize(获取文件大小)等等,这些接口是基于HTTP协议的,每次通讯结束后,服务器就关闭接口,也就是说不是永久连接。
我是基于阻塞模式的,为了不影响界面的刷新,我把通讯的过程做成了一个thread,数据收到以后通过回调函数在通知界面,界面再做刷新。就是下面的伪码。
sendrecventry()
{
send();
recv();
/**/
postdatatoui();
return 0;/*线程返回*/
}
GetDir()
{
int handle = createthread(0,0,sendrecventry,0);
} GetFileSize()
{
int handle = createthread(0,0,sendrecventry,0);
}
请各位兄弟帮忙看一下这样的方法是否合理?
你的方式很标准,是常用架构,没啥问题从服务器提供的接口走HTTP协议来看,服务器支持短连接并发响应
所以,你没有必要用“固定子线程”来束缚自己不管是多线程,还是异步模式,本质目的都是为了实现业务流的并发操作
你使用子线程来处理阻塞操作,说明你不愿意主线程被阻塞,你希望主线程能有更高的业务响应能力
那么,你的主线程的流程逻辑就应该是:它随时可以开子线程去干活
而不需要再去关心子线程的状态、工作进度,更不应该去参与子线程的调度管理
否则,你还不如不用子线程,直接在主线程中将IO请求队列化之后使用一个定时器来分时响应IO和UI请求
hi,fangle6688,您讲的“你没有必要用“固定子线程”来束缚自己 “,是什么意思呀,能不能再详细的讲一下,谢谢!
下面是我简单的调用过程:
我的过程是调用getfilelist(),这个函数里面创建一个线程获取了file list以后,传给ui,线程销毁(return 0),ui把数据刷新,再选择其中一个文件,调用getfilesize(),里面创建一个一个线程获取文件的大小,然后再传递给ui,ui显示出正确的文件大小。我现在已经开始在VC上编码测试了
recv();这个地方如果是阻塞时可以的。其实你的程序我觉得不好控制的就是,你GetDir()和GetFileSize()都发东西出去了,但GetDir()返回的时候是否就是在处理GetDir发送的接受,还有就是GetFileSize()也一样计算他们都接收跟处理到他们要的东西,谁先谁后没办法判断。还有就是你不去发的时候,你什么东西都接收不到。个人愚见,欢迎拍砖。
sendrecventry()
{
SOCKET sd = socket();
char req[100] = {0};
char rsp[100] = {0};
send(sd,req);
recv(sd,rep);
/**/
postdatatoui();
return 0;/*线程返回*/
}
里面的send和recv都是阻塞操作,当然里面会做一些更复杂的判断,比如发送或者接受失败,或者服务器关闭了socket,然后我就会给ui线程返回错误的报告,ui会显示连接失败等提示信息,如果接收数据成功,会把正确的数据发送给UI,UI再显示正确的信息。
用信号量控制一个专门的子线程进行IO,那是针对异步才有意义的
而你的子线程中使用的是同步IO,就应该保持多个子线程并发当业务繁忙的时候,例如同时有2笔业务要处理用“专门一个线程去处理数据”,你的主线程必须等待子线程完成一笔业务后再告诉它开始下一笔业务而用子线程并发处理的流程呢?
第一个子线程完成了send,开始recv时,第二个子线程的send函数就已经开始工作了用你自己的方案,你的程序的业务处理能力要比“专门一个线程去处理数据”高出至少100%
既然服务器有并发处理能力(HTTP接口肯定支持并发),你有什么理由要自我设限“每一笔业务必须等上一笔业务完成后才开始提交”呢???
你首先需要考虑的,是使用异步IO替代同步IO再者,如果你的业务非常繁忙(每分钟1000笔以上的业务)你才需要开始考虑“固定线程”的IOCP架构
//lock;
HANDLE m_hMutex=CreateSemaphore(NULL,0,1,NULL); //参数1可以是你要并发的线程数
DOWRD Lock(){
return WaitForSingleObject(m_hMutex);
}
//UnLock
bool UnLock(){
return ReleaseMutex(m_hMutex);
}
/**********************************************//Thread Rev() 主体
while(
Lock();
recv(); //阻塞不阻塞都可以。控制方法在Lock上。
/*按需要的UnLock()*/
)GetDir() {
Send();
Unlock(); //按需要UnLock
} GetFileSize()
{
Send();
Unlock(); //按需要UnLock
} 发送个人感觉没多大关系,线不现成都可以,当然如果你有很好的发送控制的话,是需要线程的配合的。这样你可以在任何地方用 UnLock() 控制你线程的步调,也不用担心不断的创建线程牺牲的CPU跟内存。
越来越画蛇添足了
缺点是并发响应能力有限(不适合30+的并发环境)
#include "winsock2.h"
#include "iostream.h"
#include "stdio.h"
#define SEND_LEN 1024
#define RECV_LEN 1024
struct LINK {
char name[15];
char url[100];
char host[100];
char ip[20];
};
int FormatRequestHeader(char *req,int reqlen,char *url, char *host)
{
int len = 0;
if(0 == req)
return -1;
memset((char*)req,0,reqlen);
strcpy(req,"GET ");
strcat(req,url);
strcat(req," HTTP/1.1");
strcat(req,"\r\n");
strcat(req,"Host:");
strcat(req," ");
strcat(req,host);
strcat(req,"\r\n");
strcat(req,"Accept:*/*");
strcat(req,"\r\n");
strcat(req,"User-Agent:Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)");
strcat(req,"\r\n");
strcat(req,"Connection:Keep-Alive");
strcat(req,"\r\n");
strcat(req,"\r\n");
len = strlen((char*)req);
req[len] = '\0';
return 0;
}
DWORD WINAPI ThreadProc (LPVOID pParam)
{
char req[SEND_LEN] = {0};
char rcv[RECV_LEN] = {0};
SOCKET sd;
struct sockaddr_in server_addr = {0};
int ret = 0;
FILE *fp = 0;
LINK *p;
cout <<"线程开始运行\r\n"<<endl;
p = (LINK*)pParam;
fp = fopen(p->name,"wb");
if(fp == 0)
return -1;
sd=socket(PF_INET,SOCK_STREAM,0);
if(sd==INVALID_SOCKET)
{
cout <<"socket()函数出错"<<endl;
return -1;
}
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(80);
server_addr.sin_addr.s_addr = inet_addr(p->ip);
if(connect(sd,(struct sockaddr*)&server_addr,sizeof(server_addr))!=0)
{
closesocket(sd);
cout<<"connect()函数执行失败!"<<endl;
return FALSE;
}
else
{
cout<<"Connect 服务器成功"<<endl;
}
FormatRequestHeader((char*)req,SEND_LEN,p->url,p->host);
ret = send(sd, (char*)req, strlen((char*)req), 0);
if(ret == -1)
{
cout<<"发送数据失败\r\n"<<endl;
closesocket(sd);
return -1;
}
cout <<"发送了"<<ret<<"字节的数据"<<endl;
while(1)
{
ret = recv(sd,rcv,RECV_LEN,0);
if(ret > 0)
{
cout <<"接收了"<<ret<<"字节的数据"<<endl;
//cout <<"数据"<<rcv<<endl;
fwrite((char*)rcv,1,ret,fp);
memset((char*)rcv,0,RECV_LEN);
}
else if (ret == -1)
{
break;
}
else if (ret == 0)
{
break;
}
}
fclose(fp);
closesocket(sd);
cout <<"数据接收完毕over\r\n"<<endl;
return 0;
}
int main()
{
int err;
WORD wVersion;
WSADATA WSAData; LPDWORD threadid1 = 0;
LPDWORD threadid2 = 0;
LPDWORD threadid3 = 0;
LPDWORD threadid4 = 0; LINK link1 = {"52rd_1.html","/","www.52rd.com","211.144.68.54"};
LINK link2 = {"52rd_2.html","/","www.52rd.com","211.144.68.54"};
LINK link3 = {"52rd_3.html","/","www.52rd.com","211.144.68.54"};
LINK link4 = {"52rd_4.html","/","www.52rd.com","211.144.68.54"}; HANDLE hThread1;
HANDLE hThread2;
HANDLE hThread3;
HANDLE hThread4; wVersion=MAKEWORD(2,0);
err=WSAStartup(wVersion,&WSAData);
if(err!=0)
{
cout<<"无法加载Socket库."<<endl;
return -1;
}
if(LOBYTE( WSAData.wVersion ) != 2)
{
cout<<"无法找到合适Socket库."<<endl;
WSACleanup();
return -1;
}
cout<<"找到了合适的Socket库."<<endl; hThread1 = CreateThread(NULL,0,ThreadProc,&link1,0,threadid1 );
hThread2 = CreateThread(NULL,0,ThreadProc,&link2,0,threadid2 );
hThread3 = CreateThread(NULL,0,ThreadProc,&link3,0,threadid3 );
hThread4 = CreateThread(NULL,0,ThreadProc,&link4,0,threadid4 ); cout<<"主线程开始运行"<<endl;
for(int i=0;;i++){}
return 0;
}
//调试程序的时候加上ws2_32.lib这个库
所遇到的问题
当只生成hThread1 的时候,52rd_1.html(80kb)生成正确,当生成hThread1 和hThread2的时候,大部分也是正确的,只是偶尔52rd_1.html大小为0,52rd_2.html为160kb,当生成四个线程的时候,经常出现一个文件为0,有一个文件是160kb,另外两个文件分别是80kb。
我的疑问是:ThreadProc是可重入得函数,写文件的时候也是分别写到各自的文件,怎么会出现把一个文件的内容写到另一个文件里面去。是不是不同同时写文件。请兄弟们帮我看一下,谢谢!
主线程开始运行
线程2开始运行
线程3开始运行
线程4开始运行线程1 Connect服务器成功
线程4 Connect服务器成功
线程4 Connect服务器成功线程1 发送了133个字节到服务器
线程4 Connect服务器成功
线程1 发送了133个字节到服务器线程3 Connect服务器成功
线程4 Connect服务器成功
线程1 发送了133个字节到服务器线程3 Connect服务器成功
线程2 Connect服务器成功
线程4 发送了133个字节到服务器线程3 发送了133个字节到服务器
线程2 发送了133个字节到服务器接收数据完毕 over
接收数据完毕 over
接收数据完毕 over我觉得很奇怪,线程怎么会重复连接服务器和发送命令呢?是不是把参数传递给ThreadProc的时候,里面保存的还是以前的数据呢?按理说这种情况不会发生的呀,因为四个线程共用一个线程入口函数,但是有四个镜像调用呀?各位兄弟有什么想法给小弟说一下,谢谢!
#include "winsock2.h"
#include "iostream.h"
#include "stdio.h"
#define SEND_LEN 1024
#define RECV_LEN 1024
struct LINK {
char name[15];
char url[100];
char host[100];
char ip[20];
FILE *fp ;
};
int FormatRequestHeader(char *req,int reqlen,char *url, char *host)
{
int len = 0;
if(0 == req)
return -1;
memset((char*)req,0,reqlen);
strcpy(req,"GET ");
strcat(req,url);
strcat(req," HTTP/1.1");
strcat(req,"\r\n");
strcat(req,"Host:");
strcat(req," ");
strcat(req,host);
strcat(req,"\r\n");
strcat(req,"Accept:*/*");
strcat(req,"\r\n");
strcat(req,"User-Agent:Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)");
strcat(req,"\r\n");
strcat(req,"Connection:Keep-Alive");
strcat(req,"\r\n");
strcat(req,"\r\n");
len = strlen((char*)req);
req[len] = '\0';
return 0;
}
DWORD WINAPI ThreadProc (LPVOID pParam)
{
char req[SEND_LEN] = {0};
char rcv[RECV_LEN] = {0};
SOCKET sd;
struct sockaddr_in server_addr = {0};
int ret = 0;
FILE *fp = 0;
LINK *p;
cout <<"线程开始运行\r\n" <<endl;
p = (LINK*)pParam;
//fp = fopen(p->name,"wb");
//if(fp == 0)
// return -1;
sd=socket(PF_INET,SOCK_STREAM,0);
if(sd==INVALID_SOCKET)
{
cout <<"socket()函数出错" <<endl;
return -1;
}
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(80);
server_addr.sin_addr.s_addr = inet_addr(p->ip);
if(connect(sd,(struct sockaddr*)&server_addr,sizeof(server_addr))!=0)
{
closesocket(sd);
cout <<"connect()函数执行失败!" <<endl;
return FALSE;
}
else
{
cout <<"Connect 服务器成功" <<endl;
}
FormatRequestHeader((char*)req,SEND_LEN,p->url,p->host);
ret = send(sd, (char*)req, strlen((char*)req), 0);
if(ret == -1)
{
cout <<"发送数据失败\r\n" <<endl;
closesocket(sd);
return -1;
}
cout <<"发送了" <<ret <<"字节的数据" <<endl;
while(1)
{
ret = recv(sd,rcv,RECV_LEN,0);
if(ret > 0)
{
cout <<"接收了" <<ret <<"字节的数据" <<endl;
//cout <<"数据" <<rcv <<endl;
fwrite((char*)rcv,1,ret,p->fp);
memset((char*)rcv,0,RECV_LEN);
}
else if (ret == -1)
{
break;
}
else if (ret == 0)
{
break;
}
}
fclose(p->fp);
closesocket(sd);
cout <<"数据接收完毕over\r\n" <<endl;
return 0;
}
int main()
{
int err;
WORD wVersion;
WSADATA WSAData; FILE *fp1 = 0;
FILE *fp2 = 0;
FILE *fp3 = 0;
FILE *fp4 = 0;
FILE *fp5 = 0;
FILE *fp6 = 0;
FILE *fp7 = 0;
FILE *fp8 = 0;
FILE *fp9 = 0; LPDWORD threadid1 = 0;
LPDWORD threadid2 = 0;
LPDWORD threadid3 = 0;
LPDWORD threadid4 = 0; LPDWORD threadid5 = 0;
LPDWORD threadid6 = 0;
LPDWORD threadid7 = 0;
LPDWORD threadid8 = 0;
LPDWORD threadid9 = 0;
LINK link1 = {"52rd_1.html","/","www.52rd.com","211.144.68.54",0};
//LINK link2 = {"52rd_2.html","/","www.52rd.com","211.144.68.54",0};
LINK link3 = {"52rd_3.html","/","www.52rd.com","211.144.68.54",0};
LINK link4 = {"52rd_4.html","/","www.52rd.com","211.144.68.54",0}; LINK link5 = {"52rd_5.html","/","www.52rd.com","211.144.68.54",0};
//LINK link6 = {"52rd_6.html","/","www.52rd.com","211.144.68.54",0};
//LINK link6 = {"wubai.mp3","http://www.92bbs.net/bbs/attachment/Mon_0611/83_72628_05be619a11f352f.mp3","www.92bbs.net","218.83.175.155",0};
LINK link7 = {"52rd_7.html","/","www.52rd.com","211.144.68.54",0};
LINK link8 = {"52rd_8.html","/","www.52rd.com","211.144.68.54",0};
LINK link2 = {"1.mp3","http://www.honghejt.com/gh/upfile/other/2007720164337336.mp3","www.honghejt.com","218.63.105.86",0};
LINK link6 = {"1.mp3","http://dream.giggletang.com/content/mp3/lovesong1990.mp3","www.giggletang.com","59.42.244.223",0};
LINK link9 = {"1.mp3","http://www.honghejt.com/gh/upfile/other/2007720164337336.mp3","www.honghejt.com","218.63.105.86",0};
fp1 = fopen((char*)"52rd_1.html","wb");
if(fp1 == 0)
return -1;
fp2 = fopen((char*)"1.mp3","wb");
if(fp2 == 0)
return -1;
fp3 = fopen((char*)"52rd_3.html","wb");
if(fp3 == 0)
return -1;
fp4 = fopen((char*)"52rd_4.html","wb");
if(fp4 == 0)
return -1; fp5= fopen((char*)"52rd_5.html","wb");
if(fp5 == 0)
return -1;
fp6 = fopen((char*)"1990.mp3","wb");
if(fp6 == 0)
return -1;
fp7 = fopen((char*)"52rd_7.html","wb");
if(fp7 == 0)
return -1;
fp8 = fopen((char*)"52rd_8.html","wb");
if(fp8 == 0)
return -1;
fp9 = fopen((char*)"2009.mp3","wb");
if(fp9 == 0)
return -1;
link1.fp = fp1;
link2.fp = fp2;
link3.fp = fp3;
link4.fp = fp4; link5.fp = fp5;
link6.fp = fp6;
link7.fp = fp7;
link8.fp = fp8;
link9.fp = fp9; HANDLE hThread1;
HANDLE hThread2;
HANDLE hThread3;
HANDLE hThread4;
HANDLE hThread5;
HANDLE hThread6;
HANDLE hThread7;
HANDLE hThread8;
HANDLE hThread9; wVersion=MAKEWORD(2,0);
err=WSAStartup(wVersion,&WSAData);
if(err!=0)
{
cout <<"无法加载Socket库." <<endl;
return -1;
}
if(LOBYTE( WSAData.wVersion ) != 2)
{
cout <<"无法找到合适Socket库." <<endl;
WSACleanup();
return -1;
}
cout <<"找到了合适的Socket库." <<endl; hThread1 = CreateThread(NULL,0,ThreadProc,&link1,0,threadid1 );
hThread2 = CreateThread(NULL,0,ThreadProc,&link2,0,threadid2 );
hThread3 = CreateThread(NULL,0,ThreadProc,&link3,0,threadid3 );
hThread4 = CreateThread(NULL,0,ThreadProc,&link4,0,threadid4 ); hThread5 = CreateThread(NULL,0,ThreadProc,&link5,0,threadid5 );
hThread6 = CreateThread(NULL,0,ThreadProc,&link6,0,threadid6 );
hThread7 = CreateThread(NULL,0,ThreadProc,&link7,0,threadid7 );
hThread8 = CreateThread(NULL,0,ThreadProc,&link8,0,threadid8 );
hThread9 = CreateThread(NULL,0,ThreadProc,&link9,0,threadid9 ); cout <<"主线程开始运行" <<endl;
for(int i=0;;i++){}
return 0;
}
代码写的比较乱,请大家见谅
fopen,fwirte是不安全的线程函数,最好使用window提供的API函数,请参考一下
http://topic.csdn.net/t/20040427/11/3012546.html#
fopen等函数是C运行库的函数,是单线程的,所以要在VC工程中做一下设置,Createthread最好用_beginthread函数,新的代码就不贴了,虽然这个工程很简单,希望对后面的人有帮助!