VC驿站

 找回密码
 加入驿站

QQ登录

只需一步,快速开始

搜索
查看: 694|回复: 4

[求助] 多客户端如何并行传大文件

[复制链接]
69_avatar_middle
在线会员 发表于 2016-4-17 23:10:10 | 显示全部楼层 |阅读模式
5驿站币
目前实现到的是服务器根据客户端的数量开线程,每个线程可以和一定数量的客户端建立通联关系。
现在有一个比较大的问题,就是并行传大型文件。
也不知道叫并行合不合适,我就先这么叫吧。目前我采用和客户端通信采用的是select,小数据的通信因为传输时间短,看不出什么问题。但是要传文件了,比如1G的电影,select选择一个socket并开始传输电影数据,就卡在这里了,除非电影传完,不然该线程的其他客户端依旧没法和服务器通信。虽然用了select,看起来好像可以多客户端同时通信,但本质上完全还是单线程模式的通信。不知道大家有什么想法或者建议能够参考的。如果有相关代码如果方便分享的话,感激不尽。


代码内容是实现的一个服务器(一个服务器对多客户端),代码写的比较少,代码风格比较丑,见谅。
  1. #include < WINSOCK2.H>
  2. #pragma comment( lib, "WS2_32" )
  3. #include<stdio.h>

  4. #define RECV_PORT 80                        //监听端口
  5. #define BUFSIZE 1024                        //缓冲区大小
  6. #define NUM_CLIENT 1                        //每个线程能接受的客户端最大个数
  7. #define NUM_THREAD 10                        //线程最大个数
  8. #define NUM_METHOD 3                        //功能数个数
  9. typedef int (*pFun)(char *,char *,SOCKET);
  10. typedef struct
  11. {
  12.         HANDLE hThread;
  13.         unsigned int ThreadId;
  14.         SOCKET client[NUM_CLIENT];
  15.         int count;
  16. }List;

  17. int Init_server(int n,SOCKET *sock);
  18. int Connect();
  19. int Finish();

  20. int Create_Socket(SOCKET &c);
  21. int Recv_Data(SOCKET c,char *data);
  22. int Send_Data(SOCKET c,char *data);
  23. int if_Create_Thread(void);

  24. int Trans_Data(int n,int i);
  25. int PutFile(char *path,char *name,SOCKET c);
  26. int GetFile(char *path,char *name,SOCKET c);
  27. int GetContent(char *path,char *name ,SOCKET c);
  28. DWORD ThreadProc(LPVOID lpParam);//线程函数,lpParam表示线程是第lpParam个
  29. void Create_Thread(void);

  30. char *Method[NUM_METHOD]={"GetFile","PutFile","GetContent"};
  31. pFun pMethod[NUM_METHOD]={PutFile,GetFile,GetContent};

  32. List list_thread[NUM_THREAD]={0};
  33. SOCKET sock_listen;
  34. HANDLE hThread;
  35. int count_client=0,count_thread=0;


  36. int main()
  37. {
  38.         if(Init_server(1,NULL))
  39.                 return 0;
  40.         Connect();
  41.         Finish();
  42.         return 0;
  43. }

  44. int Init_server(int n,SOCKET *sock)//socket、bing、listen
  45. {
  46.         SOCKET sock_fd;
  47.         SOCKADDR_IN addr_fd;
  48.         WSADATA WSAData;
  49.         BOOL flag;
  50.         if(n==1)
  51.         if(WSAStartup(MAKEWORD(2,2),&WSAData)!=0)
  52.         {
  53.                 printf("error WSAStartup :%d\n",WSAGetLastError());
  54.                 return 1;
  55.         }
  56.         if(Create_Socket(sock_fd))
  57.                 return 1;

  58.         addr_fd.sin_family=AF_INET;
  59.         addr_fd.sin_addr.S_un.S_addr=inet_addr("192.168.3.6");

  60.         if(n==1)//监听最初的80端口
  61.                 addr_fd.sin_port=htons(RECV_PORT);
  62.         else //建立连接后给每个客户端创建的端口  绑定到0端口上,系统会自动分配一个端口
  63.                 addr_fd.sin_port=htons(0);
  64.         flag=TRUE;
  65.         if(setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag)!=0))
  66.         {
  67.                 printf("error setsockopt(sock_listen) :%d\n",WSAGetLastError());
  68.                 return 1;
  69.         }
  70.         if(bind(sock_fd,(struct sockaddr FAR *)&addr_fd,sizeof(addr_fd))==SOCKET_ERROR)
  71.         {
  72.                 printf("error bind(sock_listen) :%d\n",WSAGetLastError());
  73.                 return 1;
  74.         }
  75.         if(listen(sock_fd,NUM_CLIENT)<0)
  76.         {
  77.                 printf("error listen(sock_listen) :%d\n",WSAGetLastError());
  78.                 return 1;
  79.         }
  80.         if(n==1)
  81.                 sock_listen=sock_fd;
  82.         else
  83.                 *sock=sock_fd;
  84.         return 0;
  85. }

  86. int Connect()
  87. {
  88.         int i=0;
  89.         printf("Now waiting for client......\n");
  90.         while(1)
  91.         {
  92.                
  93.                 if(if_Create_Thread())
  94.                 {
  95.                         Create_Thread();
  96.                         count_thread++;
  97.                 }
  98.                 Sleep(1000);
  99.         }
  100.         return 0;
  101. }

  102. int Finish()
  103. {
  104.         int i,j;
  105.         for(i=0;i<NUM_THREAD;i++)
  106.         {
  107.                 if(list_thread[i].ThreadId!=0)
  108.                 {
  109.                         for(j=0;j<NUM_CLIENT;j++)
  110.                         {
  111.                                 if(list_thread[i].client[j]!=0)
  112.                                         closesocket(list_thread[i].client[j]);
  113.                         }
  114.                         CloseHandle(list_thread[i].hThread);
  115.                 }
  116.         }
  117.         return 0;
  118. }

  119. int if_Create_Thread(void)
  120. {
  121.         if(count_thread*NUM_CLIENT>count_client)
  122.                 return 0;
  123.         else
  124.                 return 1;
  125. }
  126. void Create_Thread(void)
  127. {
  128. //        int *p=(int *)malloc(sizeof(int));
  129. //        *p=count_thread;
  130. /*        if((hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProc,NULL,0,NULL))==false)
  131.         {
  132.                 printf("error CreatdThread %d\n",n);
  133.                 return ;
  134.         }*/
  135.         ThreadProc(NULL);        
  136. }

  137. int if_Accept(unsigned int n)
  138. {
  139.         int i,j;
  140.         for(i=0;i<NUM_THREAD;i++)
  141.         {
  142.                 if(list_thread[i].ThreadId!=0)
  143.                         if(list_thread[i].ThreadId!=n&&list_thread[i].count<NUM_CLIENT)
  144.                                 return 0;
  145.                         else if(list_thread[i].ThreadId==n&&list_thread[i].count==NUM_CLIENT)
  146.                                 return 0;
  147.                         else if(list_thread[i].ThreadId==n&&list_thread[i].count<NUM_CLIENT)
  148.                                 return 1;
  149.         }
  150.         return 0;
  151. }
  152. void Accept(unsigned int n,SOCKET *c)
  153. {
  154.         SOCKET sock_temp,sock_temp2;
  155.         SOCKADDR_IN addr_temp;
  156.         char data[BUFSIZE]={0};
  157.         int i,j,len_addr;
  158.         len_addr=sizeof(SOCKADDR_IN);
  159.         //获得新的监听socket
  160.         Init_server(2,&sock_temp2);
  161.         //获得新的监听端口
  162.         len_addr=sizeof(addr_temp);
  163.         getsockname(sock_temp2,(sockaddr *)&addr_temp,&len_addr);
  164.         sprintf(data,"%d",addr_temp.sin_port);

  165.         sock_temp=accept(sock_listen,(struct sockaddr FAR *)&addr_temp,&len_addr);
  166.         Send_Data(sock_temp,data);
  167.         closesocket(sock_temp);

  168.         for(i=0;i<NUM_THREAD;i++)
  169.                 if(list_thread[i].ThreadId==n)
  170.                         for(j=0;j<NUM_CLIENT;j++)
  171.                         {
  172.                                 if(list_thread[i].client[j]==0)
  173.                                 {
  174.                                         list_thread[i].client[j]=sock_temp2;
  175.                                         count_client++;
  176.                                         list_thread[i].count++;
  177.                                         c[j]=sock_temp2;
  178.                                         break;
  179.                                 }                                
  180.                         }
  181. }

  182. DWORD ThreadProc(LPVOID lpParam)//n表示线程是第n个
  183. {
  184.         int i,n,ret,num,len,Max_sock,flag[NUM_CLIENT]={0};
  185.         char data[BUFSIZE];
  186.         unsigned int ThreadId;
  187.         SOCKET sock_temp[NUM_CLIENT]={0},sock_temp_listen;
  188.         SOCKADDR_IN addr_temp;
  189.         fd_set fdr;
  190.         timeval time_sock;
  191.         time_sock.tv_sec=5;
  192.         time_sock.tv_usec=00;

  193.         //获得新的监听sock
  194.         Init_server(2,&sock_temp[0]);

  195.         //获得新的监听端口
  196.         len=sizeof(addr_temp);
  197.         getsockname(sock_temp[0],(sockaddr *)&addr_temp,&len);
  198.         sprintf(data,"%d",addr_temp.sin_port);

  199.         //获得当前线程ID
  200.         ThreadId=GetCurrentThreadId();

  201.         //选定thread位置  n确定以后不变
  202.         for(n=0;n<NUM_THREAD;n++)
  203.         {
  204.                 if(list_thread[n].count==0&&list_thread[n].ThreadId==0)
  205.                 {
  206.                         list_thread[n].hThread=hThread;
  207.                         list_thread[n].ThreadId=ThreadId;
  208.                         list_thread[n].count++;
  209.                         count_client++;
  210.                         list_thread[n].client[0]=sock_temp[0];
  211.                         break;
  212.                 }
  213.         }

  214.         //通知客户端新端口
  215.         sock_temp_listen=accept(sock_listen,(sockaddr *)&addr_temp,&len);        
  216.         Send_Data(sock_temp_listen,data);
  217.         closesocket(sock_temp_listen);
  218.         
  219.         while(1)
  220.         {
  221. //                Sleep(2000);
  222.                 num=0;
  223.                 printf("线程 %d 监听中,hahahahaha......\n",ThreadId);
  224.                 for(Max_sock=0,i=0;i<NUM_CLIENT;i++)
  225.                         Max_sock=Max_sock>list_thread[n].client[i]?Max_sock:list_thread[n].client[i];
  226.                 FD_ZERO(&fdr);
  227.                 for(i=0;i<NUM_CLIENT;i++)
  228.                         if(list_thread[n].client[i])
  229.                         {
  230.                                 FD_SET(list_thread[n].client[i],&fdr);
  231.                                 num++;
  232.                         }
  233.                 if(if_Accept(ThreadId))
  234.                         FD_SET(sock_listen,&fdr);
  235.                 else
  236.                         if(num==0)
  237.                                 continue;
  238.                 ret=select(Max_sock+1,&fdr,NULL,NULL,&time_sock);
  239.                 switch(ret)
  240.                 {
  241.                 case -1:printf("select error :%d\n",WSAGetLastError());break;
  242.                 case 0: break;
  243.                 default:
  244.                         {
  245.                                 for(i=0;i<NUM_CLIENT;i++)
  246.                                         if(FD_ISSET(list_thread[n].client[i],&fdr)>0)
  247.                                                 if(flag[i]==1)//是否需要accept =1标示不需要
  248.                                                 {
  249.                                                         if(Trans_Data(n,i))
  250.                                                         {
  251.                                                                 closesocket(list_thread[n].client[i]);
  252.                                                                 list_thread[n].client[i]=0;
  253.                                                                 closesocket(sock_temp[i]);
  254.                                                                 sock_temp[i]=0;
  255.                                                                 count_client--;        
  256.                                                                 list_thread[n].count--;
  257.                                                                 flag[i]=0;
  258.                                                         }
  259.                                                 }
  260.                                                 else
  261.                                                 {                                                        
  262.                                                         list_thread[n].client[i]=accept(sock_temp[i],(sockaddr *)&addr_temp,&len);
  263.                                                         if(list_thread[n].client[i]==INVALID_SOCKET)
  264.                                                         {
  265.                                                                 list_thread[n].client[i]=sock_temp[i];
  266.                                                                 printf("error accept : %d\n",WSAGetLastError());
  267.                                                         }
  268.                                                         else
  269.                                                         {
  270.                                                                 flag[i]++;
  271.                                                         }
  272.                                                 }
  273.                                 if(FD_ISSET(sock_listen,&fdr)>0)
  274.                                         if(if_Accept(ThreadId))
  275.                                                 Accept(ThreadId,sock_temp);
  276.                                 break;
  277.                         }
  278.                 }

  279.         }
  280. }

  281. int Trans_Data(int n,int i)
  282. {
  283.         SOCKET c;
  284.         char data[BUFSIZE];
  285.         int ret;
  286.         c=list_thread[n].client[i];
  287.         ret=Recv_Data(c,data);
  288.         if(ret==1)
  289.         {
  290.                 return 1;
  291.         }
  292.         for(int i=0;i<NUM_METHOD;i++)
  293.         {
  294.                 if(strstr(data,Method[i])!=NULL)
  295.                 {
  296.                         if(strcmp(data,Method[i])==0)
  297.                                 pMethod[i](NULL,NULL,c);//默认参数
  298.                         else
  299.                         {
  300.                                 pMethod[i](strchr(data,' ')+1,strrchr(data,' ')+1,c);//指定参数
  301.                         }
  302.                         break;
  303.                 }
  304.         }
  305.         return 0;
  306. }

  307. int Create_Socket(SOCKET &c)
  308. {
  309.         c=socket(AF_INET,SOCK_STREAM,0);
  310.         if(c==INVALID_SOCKET)
  311.         {
  312.                 printf("error socket(create) :%d\n",WSAGetLastError());
  313.                 return 1;
  314.         }
  315.         return 0;
  316. }
  317. int Recv_Data(SOCKET c,char *data)
  318. {
  319.         int ret;
  320.         memset(data,0,BUFSIZE);
  321.         ret=recv(c,data,BUFSIZE,0);
  322.         if(ret<=0)
  323.         {
  324.                 printf("error recv : %d\n",WSAGetLastError());
  325.                 return 1;
  326.         }
  327.         if(ret<BUFSIZE)
  328.                 printf("received data(%d字节):\n%s\n\n",ret,data);
  329.         return 0;
  330. }
  331. int Send_Data(SOCKET c,char *data)
  332. {
  333.         int ret;
  334.         ret=send(c,data,BUFSIZE,0);
  335.         if(ret==-1)
  336.                 printf("error send : %d\n",WSAGetLastError());
  337.         return 0;
  338. }

  339. int GetFile(char *path,char *name,SOCKET c)
  340. {
  341.         HANDLE hFile;
  342.         int len;
  343.         char fname[BUFSIZ],data[BUFSIZE];
  344.         if(path!=NULL&&name!=NULL)
  345.                 sprintf(fname,"D:\\temp\\server\\%s",name);
  346.         else
  347.                 sprintf(fname,"D:\\temp\\server\\test.unk");
  348.         Recv_Data(c,data);
  349.         if(strcmp(data,"over")==0)
  350.         {
  351.                 printf("目标文件打开失败\n");
  352.                 return 1;
  353.         }        
  354.         hFile = CreateFile(fname,                // 要打开的文件名
  355.                 GENERIC_WRITE,                                // 以读方式打开
  356.                 0,                                                        // 可共享读
  357.                 NULL,                                                // 默认安全设置
  358.                 CREATE_ALWAYS,                                // 只打开已经存在的文件
  359.                 FILE_ATTRIBUTE_NORMAL,                // 常规文件属性
  360.                 NULL);
  361.         if(hFile==INVALID_HANDLE_VALUE)
  362.         {
  363.                 printf("打开文件%s失败:%d\n",fname,GetLastError());
  364.                 Send_Data(c,"over");
  365.                 return 1;
  366.         }
  367.         Send_Data(c,"ok");
  368.         while(1)
  369.         {
  370.                 Recv_Data(c,data);
  371.                 if(strcmp(data,"over")==0)
  372.                         break;
  373.                 SetFilePointer(hFile,0,0,FILE_END);
  374.                 if(!WriteFile(hFile,data,1024,(LPDWORD)&len,NULL))
  375.                 {
  376.                         printf("写文件失败:%d\n",GetLastError());
  377.                 }
  378. /*                else
  379.                 {
  380.                         printf("写文件成功,写入%d字节。\n",len);
  381.                 }*/
  382.         }
  383.         CloseHandle(hFile);
  384.         return 0;
  385. }
  386. int PutFile(char *path,char *name,SOCKET c)
  387. {
  388.         HANDLE hFile;
  389.         int len;
  390.         char fname[BUFSIZ],data[BUFSIZE];
  391.         if(path!=NULL&&name!=NULL)
  392.                 sprintf(fname,"%s\\%s",path,name);
  393.         else
  394.                 sprintf(fname,"D:\\temp\\server\\server.jpg");
  395.         hFile = CreateFile(fname,           // 要打开的文件名
  396.                 GENERIC_READ,                           // 以读方式打开
  397.                 FILE_SHARE_READ,                   // 可共享读
  398.                 NULL,                      // 默认安全设置
  399.                 OPEN_EXISTING,             // 只打开已经存在的文件
  400.                 FILE_ATTRIBUTE_NORMAL,     // 常规文件属性
  401.                 NULL);  
  402.         if(hFile==INVALID_HANDLE_VALUE)
  403.         {
  404.                 printf("打开文件%s失败:%d\n",fname,GetLastError());
  405.                 Send_Data(c,"over");
  406.                 return 1;
  407.         }
  408.         Send_Data(c,"ok");
  409.         Recv_Data(c,data);
  410.         if(strcmp(data,"over")==0)
  411.         {
  412.                 printf("对方文件打开失败\n");
  413.                 return 1;
  414.         }
  415.         while(1)
  416.         {
  417.                 if(!ReadFile(hFile,                //读文件的句柄
  418.                         data,                                //存储读取的文件内容
  419.                         1024,                                //读的大小(字节)
  420.                         (LPDWORD)&len,                //实际读取的大小
  421.                         NULL))                                //不使用Overlapped
  422.                 {
  423.                         printf("读文件错误:%d\n",GetLastError());
  424.                         break;
  425.                 }
  426.                 Send_Data(c,data);
  427.                 if(len<BUFSIZE)
  428.                         break;
  429.         }
  430.         Sleep(100);
  431.         Send_Data(c,"over");
  432.         CloseHandle(hFile);
  433.         return 0;
  434. }
  435. int GetContent(char *path,char *name ,SOCKET c)
  436. {
  437.         WIN32_FIND_DATA FindFileData;
  438.         HANDLE hListFile;
  439.         CHAR szFilePath[MAX_PATH];
  440.         char data[BUFSIZE];
  441.         //构造代表子目录和文件夹路径的字符串,使用通配符“*”
  442.         if(path!=NULL)
  443.                 lstrcpy(szFilePath, path);        
  444.         else
  445.                 lstrcpy(szFilePath,"D:\\temp\\server");
  446.         //注释的代码可以用于查找所有以“.txt结尾”的文件。
  447.         //lstrcat(szFilePath, "\\*.txt");
  448.         lstrcat(szFilePath, "\\*");//字符串拼接  这个是目录下的所有文件
  449.         //查找第一个文件/目录,获得查找句柄
  450.         hListFile = FindFirstFile(szFilePath,&FindFileData);
  451.         //判断句柄
  452.         if(hListFile==INVALID_HANDLE_VALUE)
  453.         {
  454.                 printf("错误:%d",GetLastError());
  455.                 Send_Data(c,"over");
  456.                 return 1;
  457.         }
  458.         
  459.         do
  460.         {
  461.                 /*        如果不想显示代表本级目录和上级目录的“.”和“..”,
  462.                         可以使用注释部分的代码过滤。
  463.                 if(lstrcmp(FindFileData.cFileName,TEXT("."))==0||
  464.                         lstrcmp(FindFileData.cFileName,TEXT(".."))==0)
  465.                 {
  466.                         continue;
  467.                 }
  468.                 */
  469.                 //打印文件名、目录名
  470.                 printf("%s\t\t",FindFileData.cFileName);
  471.                 memset(data,0,sizeof(data));
  472.                 sprintf(data,"%s\t\t",FindFileData.cFileName);
  473.                 Send_Data(c,data);
  474.                 //判断文件属性,加密文件或文件夹
  475.                 if(FindFileData.dwFileAttributes&FILE_ATTRIBUTE_ENCRYPTED)
  476.                 {
  477.                         printf("<加密> ");
  478.                         memset(data,0,sizeof(data));
  479.                         sprintf(data,"<加密> ");
  480.                         Send_Data(c,data);
  481.                 }
  482.                
  483.                 //判断文件属性,隐藏文件或文件夹
  484.                 if(FindFileData.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN)
  485.                 {
  486.                         printf("<隐藏> ");
  487.                         memset(data,0,sizeof(data));
  488.                         sprintf(data,"<隐藏> ");
  489.                         Send_Data(c,data);
  490.                 }
  491.                 //判断文件属性,目录
  492.                 if(FindFileData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
  493.                 {
  494.                         printf("<DIR> ");
  495.                         memset(data,0,sizeof(data));
  496.                         sprintf(data,"<DIR> ");
  497.                         Send_Data(c,data);
  498.                 }
  499.                 //读者可根据文件属性表中的内容自行添加判断文件属性。
  500.                 printf("\n");
  501.                 Send_Data(c,"\n");
  502.         }
  503.         while(FindNextFile(hListFile, &FindFileData));
  504.         Sleep(100);
  505.         Send_Data(c,"over");
  506.         return 0;
  507. }

复制代码





上一篇:求大神指教一下
下一篇:ClistCtrl问题求助
77_avatar_middle
online_vip 发表于 2016-4-17 23:32:23 | 显示全部楼层
好长的代码。。。我得研读研读先

评分

参与人数 1热心值 +1 收起 理由
69_avatar_small Four_glass + 1

查看全部评分

22_avatar_middle
在线会员 发表于 2016-4-18 16:17:53 | 显示全部楼层
你这样做真的好么,建议你花时间学习IOCP。
http://www.cnblogs.com/lancidie/archive/2011/12/19/2293773.html

评分

参与人数 1驿站币 +1 热心值 +1 收起 理由
69_avatar_small Four_glass + 1 + 1

查看全部评分

69_avatar_middle
ico_lz  楼主| 发表于 2016-4-18 16:26:04 | 显示全部楼层
sunflover 发表于 2016-4-18 16:17
你这样做真的好么,建议你花时间学习IOCP。
http://www.cnblogs.com/lancidie/archive/2011/12/19/2293773 ...

谢谢,等会就看。
想问一下,我这么提问有什么问题?
62_avatar_middle
在线会员 发表于 2016-4-18 18:11:59 | 显示全部楼层
传输大文件,你可以考虑分段传输和断点续传。

评分

参与人数 1驿站币 +1 热心值 +1 收起 理由
69_avatar_small Four_glass + 1 + 1

查看全部评分

您需要登录后才可以回帖 登录 | 加入驿站 qq_login

本版积分规则

关闭

站长提醒上一条 /2 下一条

QQ|小黑屋|手机版|VC驿站 ( 辽ICP备09019393号tongdun|网站地图wx_jqr

GMT+8, 2019-3-19 06:55

Powered by Discuz! X3.4

© 2009-2019 cctry.com

快速回复 返回顶部 返回列表