博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c++ 使用socket实现C/S端文件的下载传输
阅读量:5159 次
发布时间:2019-06-13

本文共 7099 字,大约阅读时间需要 23 分钟。

首先是服务器端,大致说下流程:服务器创建线程去处理应答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);                    }            }    }

转载于:https://www.cnblogs.com/azraelly/archive/2012/06/07/2541086.html

你可能感兴趣的文章
使用 Printf via SWO/SWV 输出调试信息
查看>>
.net 分布式架构之分布式锁实现(转)
查看>>
吴恩达机器学习笔记 —— 3 线性回归回顾
查看>>
Bouncy Castle内存溢出
查看>>
多线程_java多线程环境下栈信息分析思路
查看>>
机器学习数学【1】
查看>>
Problem E: Automatic Editing
查看>>
Java数组排序
查看>>
SpringBoot 使用 MyBatis 分页插件 PageHelper 进行分页查询
查看>>
《DSP using MATLAB》Problem 6.17
查看>>
微信公众平台开发实战Java版之如何网页授权获取用户基本信息
查看>>
一周TDD小结
查看>>
(三)建筑物多边形化简系列——去除冗余点
查看>>
Spring Boot Oauth2缓存UserDetails到Ehcache
查看>>
sizeof与strlen的用法
查看>>
2017 ICPCECPC 邀请赛 F,D,E, I 题解
查看>>
Linux 下常见目录及其功能
查看>>
python Termux Android 开发介绍
查看>>
开源框架中常用的php函数
查看>>
Java语法糖初探(三)--变长参数
查看>>