1. 程式人生 > >基於Socket的檔案傳輸(使用CSocket類)

基於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型別的IPint型別的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檔案中的配置資訊(IPport),並根據此資訊建立Socket連線,傳送檔案。注意:伺服器端應該先單擊了“接受客戶端資料”,否則傳送失敗。

u單擊“接收”也會讀取ServerConfiguration.ini檔案中的配置資訊(IPport),並根據此資訊建立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;
}