2011-12-18 17:03
最近需要做網(wǎng)絡(luò)傳輸?shù)捻椖?,需要實現(xiàn)較大文件的傳輸。在網(wǎng)上收集了不少資料,但是各有各的做法,尤其是關(guān)于文件自動接收這一塊不太清楚。
經(jīng)過圖書館查閱后還是找到了一種解決辦法,雖然做的不太專業(yè),但是思路比較精簡、清晰,也希望能給大家尤其是剛學(xué)習(xí)socket套接字的人一些啟示。

對于套接字socket我其實也不太懂,并且一般資料都可以查找到,所以不交易累贅了,直接說如何實現(xiàn)文件的傳輸吧。
對于發(fā)送文件,有三步:發(fā)送文件長度,發(fā)送文件名,發(fā)送文件內(nèi)容。
關(guān)于發(fā)送文件內(nèi)容,又可以根據(jù)文件大小進(jìn)行直接傳輸和分塊傳輸,如果是分塊傳輸還需要多線程,否則會容易使程序失去響應(yīng)。
在這里其實我也有一個疑惑,就是通過CFileDialog類GetFileName函數(shù)獲取文件名,一般沒有問題,但是當(dāng)文件名很長(如大于60)時不能完整讀取,導(dǎo)致接收方無法判別文件類型。
所以保險起見,我就從GetFilePath中截取出了文件名。
- void CChatDlg::OnSend()
- {
- if(flag==-1)
- {
- MessageBox("處于未連接狀態(tài)");
- return;
- }
-
- CFileDialog dlg(TRUE);
- dlg.m_ofn.lpstrTitle="選擇圖片";
- dlg.m_ofn.lpstrFilter="All Files";
- CString path="";
- CString name="";
- if(dlg.DoModal()==IDOK)
- {
- path=dlg.GetPathName();
-
- }
- if(path=="")
- return ;
-
-
- int pos=0,start=0;
- while(1)
- {
- start=pos;
- pos=path.Find('\\',start+1);
- if(pos<0)
- break;
- }
- name=path.Right(path.GetLength()-start-1);
- file.Open(path,CFile::modeRead);
- dwlen=file.GetLength();
- m_se.SetRange32(0,dwlen);
- CString te;
- te.Format("%d",dwlen);
- if(flag==0)
- send(m_accept, te.GetBuffer(0), 10, 0);
- if(flag==1)
- send(m_local, te.GetBuffer(0), 10, 0);
- Sleep(500);
-
- if(flag==0)
- send(m_accept, name.GetBuffer(0), name.GetLength(), 0);
- if(flag==1)
- send(m_local, name.GetBuffer(0), name.GetLength(), 0);
- Sleep(500);
-
-
- if(dwlen<=1024*1024)
- {
- if(date!=NULL)
- {
- delete []date;
- date=NULL;
- }
- date=new char[dwlen];
- memset(date,0,dwlen);
- file.Read(date,dwlen);
- file.Close();
- int n=0;
- if(flag==0)
- n=send(m_accept, date, dwlen, 0);
- if(flag==1)
- n=send(m_local, date, dwlen, 0);
- m_se.SetPos(n);
- CString ss;
- ss.Format("%d %d",dwlen,n);
- ss+="發(fā)送完成";
- MessageBox(ss);
- delete []date;
- date=NULL;
- }
- else
- {
-
- p_thread=AfxBeginThread(SEND,this,0,0,0,NULL);
- }
- }
注解中的什么圖片的是錯誤的,是可以傳輸任意格式的文件的。大家可以看到我提取文件名的過程繁瑣,原因我
上面一段已經(jīng)說明了,也希望大家更好的解決方法。其中flag的值時表示是服務(wù)端還是客戶端,0-服務(wù)端,1-客戶端,集合在了一個程序中。
注意:::
其中的Sleep(500)的延時是因為為了讓接受方有足夠多的時間獲取文件長度和文件名關(guān)鍵信息,如果不延時,接收方會先接受到文件內(nèi)容后接收到文件長度和文件名,順序是相反的,具體原因我也沒弄清楚,希望大家指教。
最后是關(guān)于一個線程的函數(shù),具體如下:
- UINT SEND(LPVOID pThreadParam)
- {
- CChatDlg *dlg=(CChatDlg *)pThreadParam;
- if(date!=NULL)
- {
- delete []date;
- date=NULL;
- }
- int total=0,len=0;
- while(total<dwlen)
- {
- if(dwlen-total>=1024*100)
- {
- date=new char[1024*100];
- memset(date,0,1024*100);
- file.Read(date,1024*100);
- if(dlg->flag==0)
- len=send(dlg->m_accept, date, 1024*100, 0);
- if(dlg->flag==1)
- len=send(dlg->m_local, date, 1024*100, 0);
- }
- else
- {
- date=new char[dwlen-total];
- memset(date,0,dwlen-total);
- file.Read(date,dwlen-total);
- if(dlg->flag==0)
- len=send(dlg->m_accept, date, dwlen-total, 0);
- if(dlg->flag==1)
- len=send(dlg->m_local, date, dwlen-total, 0);
- }
- Sleep(1);
- total+=len;
- dlg->m_se.SetPos(total);
- delete []date;
- date=NULL;
- }
- file.Close();
- CString ss;
- ss.Format("%d",total);
- ss+="發(fā)送完成";
- MessageBox(dlg->m_hWnd,ss,"提示",MB_OK);
-
- DWORD exit=0;
-
- BOOL ret=GetExitCodeThread(dlg->p_thread->m_hThread,&exit);
- if(exit==STILL_ACTIVE)
- {
- dlg->p_thread->ExitInstance();
- dlg->p_thread=NULL;
- }
-
- return 0;
- }
線程的好處是不會讓程序失去響應(yīng),而且對于大型文件傳輸來說這是必須的。
其次,關(guān)于文件的接受,就是一個注冊的OnSocket中的FD_READ,通過這個方式設(shè)置WSAAsyncSelect模型的。
- int nRet = WSAAsyncSelect(m_local, m_hWnd, WM_SOCKET, FD_ACCEPT|FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE);
- if (nRet != 0)
- {
- TRACE("設(shè)置WSAAsyncSelect模型失敗");
- }
-
關(guān)于OnSocket 函數(shù)完整如下,處理FD_READ|FD_CONNECT|FD_ACCEPT等消息
- void CChatDlg::OnSocket(WPARAM wParam,LPARAM lParam)
- {
- int nError = WSAGETSELECTERROR(lParam);
- int nEvent = WSAGETSELECTEVENT(lParam);
- SOCKET sock = wParam;
- switch (nEvent)
- {
- case FD_ACCEPT:
- {
-
- closesocket(m_accept);
- sockaddr_in sockAddr;
- int nAddrSize = sizeof(sockaddr_in);
- m_accept = accept(sock, (sockaddr*)&sockAddr, &nAddrSize);
- WSAAsyncSelect(m_accept, m_hWnd, WM_SOCKET, FD_CLOSE|FD_READ);
- state=true;
- flag=0;
- m_connect.EnableWindow(FALSE);
- break;
- }
- case FD_READ:
- {
- switch(flag1)
- {
- case 1:
- memset(buffer,0,10);
- recv(sock, buffer, 10, 0);
- m_length=atoi(buffer);
- UpdateData(false);
- flag1=2;
- m_get.SetRange32(0,m_length);
- break;
- case 2: recv(sock, filename, 200, 0);
- flag1=3;
- f.Open(filename,CFile::modeCreate|CFile::modeWrite);
- break;
- case 3:if(temp!=NULL)
- {
- delete [] temp;
- temp=NULL;
- }
- temp=new char[1024*56];
- memset(temp,0,1024*56);
- x=recv(sock,temp,1024*56,0);
- f.Write(temp,x);
- total+=x;
- m_get.SetPos(total);
- delete []temp;
- temp=NULL;
- if(total>=m_length)
- {
- f.Close();
- memset(filename,0,200);
- flag1=1;
- CString sd; sd.Format("%d",total);sd+="接受完成";
- MessageBox(sd);
- total=x=0;
- }
- break;
- }
- }
- break;
- case FD_CLOSE:
- {
- if(flag==0)
- closesocket(m_accept);
- state= FALSE;
- flag=-1;
- m_connect.EnableWindow(TRUE);
- break;
- }
- case FD_CONNECT:
- {
- if(nError == 0)
- {
- state= TRUE;
- flag=1;
- MessageBox("連接成功");
- }
- break;
- }
-
- }
- }
其中接受就在FD_READ消息中,flag1-1接受文件長度,flag1-2接受文件名,flag1-3接受文件內(nèi)容。
當(dāng)然整個函數(shù)太長了,本來應(yīng)該一些語句應(yīng)該設(shè)置成一些函數(shù)的,那樣也更直觀。
這基本上是這個工程的全部了,還是比較簡潔的。在同一臺電腦上測試結(jié)果是:732577734字節(jié)共耗時114秒,平均6.12M/s,也不是體太慢。
整個項目還是有不少弊端的,有疑惑的可以相互交流討論。
|