基於Socket的檔案傳輸(使用CSocket類)
本軟體使用MFC採用面向物件的方法實現了基於Socket的檔案傳輸。這是原來研究生課程的結課作業,實現了Socket的傳送和接收,以及讀取ini配置檔案等操作。使用了CSocket類
以下是當時結課作業 的正文:
一.軟體特點如下:
1.採用了多執行緒的方法,檔案傳輸時使用AfxBeginThread()開啟新執行緒
void CClientsockDlg::OnBnClickedSend()
{
pThreadSend = AfxBeginThread(Thread_Send,this);/
}
檔案的傳送和接收都開起了新執行緒
UINTThread_Send(LPVOID lpParam)
{
程式碼略…
}
2.支援從配置檔案configuration.ini中獲取伺服器引數
採用GetPrivateProfileString()和GetPrivateProfileInt()分別獲取位於ServerConfiguration.ini檔案中的String型別的IP和int型別的port
CString IP;
int port;
GetPrivateProfileString
(L"ServerConfiguration",L"IP",L"沒有讀取到資料!",IP.GetBuffer(10),10,L".\\configuration.ini");
port=GetPrivateProfileInt(L"ServerConfiguration",L"port",0,L".\\configuration.ini");
3.採用了面向物件的設計方式,功能之間按模組劃分
MFC本身具有良好的面向物件的特性,本程式嚴格按照MFC框架結構編寫程式碼,每個按鈕對應一個功能函式,降低了程式碼之間的耦合性,有利於程式的擴充套件和複用。
void CServersockDlg::OnBnClickedChoose()
void CServersockDlg::OnBnClickedSend()
void CServersockDlg::OnBnClickedRecvdata()
void CServersockDlg::OnBnClickedAbout()
void CServersockDlg::OnBnClickedWriteini()
4.採用了CSocket類,程式碼相對更簡單
CSocket類是MFC框架對socket程式設計中的winsockAPI的封裝,因此通過這個類管理收發資料更加便利。程式碼也跟那個既簡單易懂。
//建立
if(!Clientsock.Socket())
{
CString str;
str.Format(_T("Socket建立失敗:%d"),GetLastError());
AfxMessageBox(str);
}
//連線
if(!Clientsock.Connect(IP,port))
{
CString str;
str.Format(_T("Socket連線失敗:%d"),GetLastError());
AfxMessageBox(str);
}
else
{
AfxMessageBox(_T("Socket連線成功"));
程式碼略…
//傳送
while(nSize<FindFileData.nFileSizeLow)
{
szBuff = new char[1024];
memset(szBuff,0x00,1024);
nSend =file.Read(szBuff,1024);
Clientsock.Send(szBuff,nSend);//傳送資料
nSize += nSend;
}
file.Close();
delete szBuff;
Clientsock.Close();
(dlg->GetDlgItem(IDC_SEND))->EnableWindow(TRUE);
AfxMessageBox(_T("檔案傳送成功"));
dlg->SetDlgItemTextW(IDC_FILEPATHNAME,_T(""));
}
return 0;
5.支援資料在伺服器與客戶端之間雙向傳輸
本程式不但可以從客戶端往伺服器端傳檔案,而且可以從伺服器端往客戶端傳檔案。
但是互傳檔案的方式並不是完全相同的。
伺服器端不管是接收檔案還是傳送檔案始終是對繫結的埠進行監聽。
//繫結
if(!Serversock.Bind(port))
{
CString str;
str.Format(_T("Socket繫結失敗: %d"),GetLastError());
AfxMessageBox(str);
}
//監聽
if(!Serversock.Listen(10))
{
CString str;
str.Format(_T("Socket監聽失敗:%d"),GetLastError());
AfxMessageBox(str);
}
客戶端不管是接收檔案還是傳送檔案始終是進行連線。
if(!Clientsock.Connect(IP,port))
{
CString str;
str.Format(_T("Socket連線失ì敗:%d"),GetLastError());
AfxMessageBox(str);
}
else
{
略…
6.完全圖形化操作介面
二.軟體使用說明
客戶端主介面如圖所示:
u單擊“選擇檔案”彈出檔案對話方塊,選擇一個要傳送的檔案,同時儲存檔案的路徑。
u單擊“傳送”則會讀取ServerConfiguration.ini檔案中的配置資訊(IP和port),並根據此資訊建立Socket連線,傳送檔案。注意:伺服器端應該先單擊了“接受客戶端資料”,否則傳送失敗。
u單擊“接收”也會讀取ServerConfiguration.ini檔案中的配置資訊(IP和port),並根據此資訊建立Socket連線,接收檔案。注意:伺服器端應該先選擇了向客戶端傳送的檔案,並單擊了“傳送”,否則接受失敗。
u單擊“讀取配置檔案”,會從ServerConfiguration.ini檔案中讀取配置資訊,並以可編輯的文字形式顯示出來,修改完後,單擊“寫入配置檔案”,會將修改後的資訊儲存到配置檔案中。
u單擊“關於”可以瞭解到軟體相關資訊。
u程式碼註釋裡有更詳細的說明
伺服器端主介面如圖所示
u 單擊“接受客戶端資料”,開始監聽客戶端的連結。
u 單擊“選擇檔案”彈出檔案對話方塊,選擇一個要傳送的檔案,同時儲存檔案的路徑。
u 單擊“傳送”則會讀取ServerConfiguration.ini檔案中的配置資訊(port),並監聽對應埠,準備傳送檔案。注意:客戶端選擇“接收”以後才能傳送成功。
u 單擊“讀取配置檔案”,會從ServerConfiguration.ini檔案中讀取配置資訊,並以可編輯的文字形式顯示出來,修改完後,單擊“寫入配置檔案”,會將修改後的資訊儲存到配置檔案中。但是伺服器的IP是不可以修改的,它是在程式開始執行時從伺服器所在機器的網絡卡上獲取的。
u 單擊“關於”可以瞭解到軟體相關資訊。
u 程式碼註釋裡有更詳細的說明
在此附上客戶端使用CSocket發起連線的程式碼
//----------------------------傳送檔案的執行緒------------------------------
UINT Thread_Send(LPVOID lpParam)
{
CClientsockDlg *dlg=(CClientsockDlg *)lpParam;
(dlg->GetDlgItem(IDC_SEND))->EnableWindow(FALSE);
CSocket Clientsock; //definition socket.
if(!AfxSocketInit())
{
AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
}
CString IP;
int port;
GetPrivateProfileString(L"ServerConfiguration",L"IP",L"沒有讀取到資料!",IP.GetBuffer(100),100,L".\\configuration.ini");
port=GetPrivateProfileInt(L"ServerConfiguration",L"port",0,L".\\configuration.ini");
//建立
if(!Clientsock.Socket())
{
CString str;
str.Format(_T("Socket建立失敗: %d"),GetLastError());
AfxMessageBox(str);
}
//連線
// if(!Clientsock.Connect(_T("127.0.0.1"),8088))
if(!Clientsock.Connect(IP,port))
{
CString str;
str.Format(_T("Socket連線失敗: %d"),GetLastError());
AfxMessageBox(str);
}
else
{
AfxMessageBox(_T("Socket連線成功"));
WIN32_FIND_DATA FindFileData;
CString strPathName; //定義用來儲存傳送檔案路徑的CString物件
dlg->GetDlgItemTextW(IDC_FILEPATHNAME,strPathName);
FindClose(FindFirstFile(strPathName,&FindFileData));
Clientsock.Send(&FindFileData,sizeof(WIN32_FIND_DATA));
CFile file;
if(!file.Open(strPathName,CFile::modeRead|CFile::typeBinary))
{
AfxMessageBox(_T("檔案不存在"));
return 1;
}
UINT nSize = 0;
UINT nSend = 0;
char *szBuff=NULL;
//傳送
while(nSize<FindFileData.nFileSizeLow)
{
szBuff = new char[1024];
memset(szBuff,0x00,1024);
nSend = file.Read(szBuff,1024);
Clientsock.Send(szBuff,nSend);//傳送資料
nSize += nSend;
}
file.Close();
delete szBuff;
Clientsock.Close();
(dlg->GetDlgItem(IDC_SEND))->EnableWindow(TRUE);
AfxMessageBox(_T("檔案傳送成功"));
dlg->SetDlgItemTextW(IDC_FILEPATHNAME,_T(""));
}
return 0;
}
以及伺服器端使用CSocket監聽的程式碼:
//----------------------------監聽檔案的執行緒------------------------------
UINT Thread_Func(LPVOID lpParam) //接收檔案的執行緒函式
{
CServersockDlg *dlg = (CServersockDlg *)lpParam; //獲取對話方塊指標
(dlg->GetDlgItem(IDC_RECVDATA))->EnableWindow(FALSE);
if(!AfxSocketInit())
{
AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
}
CString IP;
int port;
GetPrivateProfileString(L"ServerConfiguration",L"IP",L"沒有讀取到資料!",IP.GetBuffer(100),100,L".\\configuration.ini");
port=GetPrivateProfileInt(L"ServerConfiguration",L"port",0,L".\\configuration.ini");
char errBuf[100]={0};// 臨時快取
SYSTEMTIME t; //系統時間結構
CFile logErrorfile;
if(!logErrorfile.Open(_T("logErrorfile.txt"),CFile::modeCreate|CFile::modeReadWrite))
{
return 1;
}
CSocket Serversock;
CSocket Clientsock;
//建立
if(!Serversock.Socket())
{
CString str;
str.Format(_T("Socket建立失敗: %d"),GetLastError());
AfxMessageBox(str);
}
BOOL bOptVal = TRUE;
int bOptLen = sizeof(BOOL);
Serversock.SetSockOpt(SO_REUSEADDR,(void *)&bOptVal,bOptLen,SOL_SOCKET);
//繫結
if(!Serversock.Bind(port))
{
CString str;
str.Format(_T("Socket繫結失敗: %d"),GetLastError());
AfxMessageBox(str);
}
//監聽
if(!Serversock.Listen(10))
{
CString str;
str.Format(_T("Socket監聽失敗: %d"),GetLastError());
AfxMessageBox(str);
}
GetLocalTime(&t);
sprintf_s(errBuf,"伺服器已經啟動...正在等待接收檔案...\r\n時間:%d年%d月%d日 %2d:%2d:%2d \r\n",t.wYear,t.wMonth,t.wDay,
t.wHour,t.wMinute,t.wSecond);
int len = strlen(errBuf);
logErrorfile.Write(errBuf,len);
AfxMessageBox(_T("啟動成功等待接收檔案"));
while(1)
{
//AfxMessageBox(_T("伺服器啟動成功..."));
if(!Serversock.Accept(Clientsock)) //等待接收
{
continue;
}
else
{
WIN32_FIND_DATA FileInfo;
Clientsock.Receive(&FileInfo,sizeof(WIN32_FIND_DATA));
CFile file;
file.Open(FileInfo.cFileName,CFile::modeCreate|CFile::modeWrite);
//AfxMessageBox(FileInfo.cFileName);
int length = sizeof(FileInfo.cFileName);
logErrorfile.Write(FileInfo.cFileName,length);
//Receive檔案的資料
UINT nSize = 0;
UINT nData = 0;
char *szBuff=NULL;
while(nSize<FileInfo.nFileSizeLow)
{
szBuff = new char[1024];
memset(szBuff,0x00,1024);
nData=Clientsock.Receive(szBuff,1024);
file.Write(szBuff,nData);
nSize+=nData;
}
delete szBuff;
Serversock.Close();
Clientsock.Close();
file.Close();
(dlg->GetDlgItem(IDC_RECVDATA))->EnableWindow(TRUE);
sprintf_s(errBuf,"檔案接收成功...\r\n時間:%d年%d月%d日 %2d:%2d:%2d \r\n",t.wYear,t.wMonth,t.wDay,
t.wHour,t.wMinute,t.wSecond);
int len = strlen(errBuf);
logErrorfile.Write(errBuf,len);
//AfxMessageBox(_T("檔案接收成功..."));
break;
}
}
return 0;
}