c++ 使用socket实现C/S端文件的下载传输
生活随笔
收集整理的這篇文章主要介紹了
c++ 使用socket实现C/S端文件的下载传输
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
首先是服務器端,大致說下流程:服務器創建線程去處理應答accept(),當接受到客戶端連接請求時,首先獲取要發送的指定的文件數據總大小給客戶端,接著就是循環讀取要發送的文件數據流向客戶端發送文件數據,每次都判斷循環讀取到的數據實際大小,當實際讀取到的數據總大小為0時,表示文件發送結束。下面是服務器server端實現:
聲明部分:
public:afx_msg void OnBnClickedButton1(); public:BOOL InitSocket(); //初始化并創建套接字static DWORD WINAPI ThreadProc(LPVOID lpParameter); //創建線程去執行服務器accept()實現部分:
void CSendFileServerDlg::OnBnClickedButton1() {// TODO: 在此添加控件通知處理程序代碼if (InitSocket()){GetDlgItem(IDC_EDIT1)->SetWindowText(_T("服務器開啟監聽。。。 \r\n"));//創建線程HANDLE hThread = CreateThread(NULL,0,ThreadProc,NULL,0,NULL);//關閉該接收線程句柄,釋放引用計數 CloseHandle(hThread);} }BOOL CSendFileServerDlg::InitSocket() {//加載套接字庫 WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD( 1, 1 );err = WSAStartup( wVersionRequested, &wsaData );if ( err != 0 ) {return FALSE;}if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {WSACleanup( );return FALSE;}//創建套接字//SOCKET m_socket=socket(AF_INET,SOCK_STREAM,0);m_socket=socket(AF_INET,SOCK_STREAM,0);if (m_socket == INVALID_SOCKET){AfxMessageBox(_T("套接字創建失敗!"));return FALSE;}SOCKADDR_IN addrSrv;addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(8099);err = bind(m_socket,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //綁定本地端口if (err==SOCKET_ERROR){closesocket(m_socket);AfxMessageBox(_T("綁定失敗!"));return FALSE;}listen(m_socket,5);//開啟監聽return TRUE;}DWORD WINAPI CSendFileServerDlg::ThreadProc(LPVOID lpParameter) {SOCKADDR_IN addrClient;int len = sizeof(SOCKADDR);while (true){SOCKET sockConn=accept(m_socket,(SOCKADDR*)&addrClient,&len);CString filename = _T("E:\\test.zip");HANDLE hFile;unsigned long long file_size = 0;char Buffer[1024];DWORD dwNumberOfBytesRead;hFile = CreateFile(filename,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);file_size = GetFileSize(hFile,NULL);send(sockConn,(char*)&file_size,sizeof(unsigned long long)+1,NULL);do {::ReadFile(hFile,Buffer,sizeof(Buffer),&dwNumberOfBytesRead,NULL);::send(sockConn,Buffer,dwNumberOfBytesRead,0);} while (dwNumberOfBytesRead);CloseHandle(hFile);}return 0; }如代碼所述? 每次發送單位是unsigned char[1024]大小(程序是char 應該為unsigned char[1024])所以就不存在網絡字節序問題也不用考慮大端小端什么的。
服務器端暫時不支持多客戶端并發訪問,后續可能會加上。。。
-------------------------------------------
下面是客戶端,同樣也大致說下客戶端流程,客戶端增加手動填寫Ip地址和端口號功能(端口號暫為8099)。以及下載傳輸文件數據進度條的顯示,和下面簡單的一些狀態顯示。客觀端由填寫的IP地址進行連接服務器操作,如果客戶端連接服務器成功的話直接就會獲取服務器端發送的要發送的文件數據的總大小,如果獲取文件總大小>0 則會循環往指定的路徑寫數據啦。此處循環寫文件結束標志,我是用每次實際寫的累加如果累計值等于從服務器端獲取的文件總大小的話表示下載文件數據成功,結束循環。大致是這樣一個過程。代碼實現:
客戶端聲明部分:
| public: ????afx_msg void?OnBnClickedButton1(); ????BOOL?InitSocket(); ????void?ConnectServer(); ????void?ConnectRecvFileData(DWORD?ip,int?port); private: ????CProgressCtrl *m_progress; //進度條 |
?進度條在OnInitDialog()里初始化:
BOOL CRecvFileClientDlg::OnInitDialog() {CDialog::OnInitDialog();// 將“關于...”菜單項添加到系統菜單中。// IDM_ABOUTBOX 必須在系統命令范圍內。ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != NULL){CString strAboutMenu;strAboutMenu.LoadString(IDS_ABOUTBOX);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// 設置此對話框的圖標。當應用程序主窗口不是對話框時,框架將自動// 執行此操作SetIcon(m_hIcon, TRUE); // 設置大圖標SetIcon(m_hIcon, FALSE); // 設置小圖標// TODO: 在此添加額外的初始化代碼 m_progress = (CProgressCtrl*)GetDlgItem(IDC_PROGRESS1); m_progress->SetPos(0); return TRUE; // 除非將焦點設置到控件,否則返回 TRUE }客戶端具體實現部分:
void CRecvFileClientDlg::OnBnClickedButton1() {// TODO: 在此添加控件通知處理程序代碼 ConnectServer();}BOOL CRecvFileClientDlg::InitSocket() {//加載套接字庫 WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD( 1, 1 );err = WSAStartup( wVersionRequested, &wsaData );if ( err != 0 ) {return FALSE;}if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {WSACleanup( );return FALSE;}return TRUE;}void CRecvFileClientDlg::ConnectRecvFileData(DWORD ip,int port) {unsigned long long file_size=0;SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);SOCKADDR_IN addrSrv;addrSrv.sin_addr.S_un.S_addr=htonl(ip);addrSrv.sin_port=ntohs(port);addrSrv.sin_family = AF_INET;//connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//recv(sockClient,(char*)&file_size,sizeof(unsigned long long)+1,NULL);if (!connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR))){GetDlgItem(IDC_SHOWINFO)->SetWindowText(_T(""));GetDlgItem(IDC_SHOWINFO)->SetWindowText(_T("連接服務器成功!\r\n"));recv(sockClient,(char*)&file_size,sizeof(unsigned long long)+1,NULL);unsigned short maxvalue = file_size; //此處不太穩妥 當數據很大時可能會出現異常m_progress->SetRange(0,maxvalue); if (file_size>0){ GetDlgItem(IDC_SHOWINFO)->SetWindowText(_T(""));GetDlgItem(IDC_SHOWINFO)->SetWindowText(_T("文件下載到本地 d:\\test.zip \r\n"));DWORD dwNumberOfBytesRecv=0;DWORD dwCountOfBytesRecv=0;char Buffer[1024];CString filename = _T("d:\\test.zip");HANDLE hFile;hFile = CreateFile(filename,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);do {m_progress->SetPos(dwCountOfBytesRecv);//更新進度條 dwNumberOfBytesRecv = ::recv(sockClient,Buffer,sizeof(Buffer),0);::WriteFile(hFile,Buffer,dwNumberOfBytesRecv,&dwNumberOfBytesRecv,NULL);dwCountOfBytesRecv += dwNumberOfBytesRecv; } while (file_size - dwCountOfBytesRecv);CloseHandle(hFile);GetDlgItem(IDC_SHOWINFO)->SetWindowText(_T(""));GetDlgItem(IDC_SHOWINFO)->SetWindowText(_T("文件接收完畢!\r\n"));AfxMessageBox(_T("文件接收完畢!"));//醒目可以注釋}else{AfxMessageBox(_T("獲取文件總大小失敗!"));}}else{AfxMessageBox(_T("連接服務器失敗、請確認IP地址或端口號!"));}closesocket(sockClient);//關閉套接字 }void CRecvFileClientDlg::ConnectServer() {if (InitSocket()){DWORD strIp =NULL;CString strPort = _T("");((CIPAddressCtrl*)GetDlgItem(IDC_IP))->GetAddress(strIp);GetDlgItem(IDC_PORT)->GetWindowText(strPort);if (strIp==NULL||strPort==""){AfxMessageBox(_T("Ip地址或Port端口號不能為空!"));}else{int port = atoi(strPort.GetBuffer(1));ConnectRecvFileData(strIp,port);}}} 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的c++ 使用socket实现C/S端文件的下载传输的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux系统编程之进程(七):syst
- 下一篇: C++ Socket编程步骤