当前位置:首页 > 芯闻号 > 充电吧
[导读]c++ 使用socket实现C/S端文件的下载传输首先是服务器端,大致说下流程:服务器创建线程去处理应答accept(),当接受到客户端连接请求时,首先获取要发送的指定的文件数据总大小给客户端,接着就

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("服务器开启监听。。。 rn"));

        //创建线程
        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()里初始化:

按 Ctrl+C 复制代码
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
}
按 Ctrl+C 复制代码
客户端具体实现部分:

按 Ctrl+C 复制代码
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("连接服务器成功!rn"));

       

        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 rn"));

            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("文件接收完毕!rn"));
            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++语言里面const修饰的才算是一个真正的常量,在 c 语言中 const 可以说是个“冒牌货”。为什么会这样?其实是 c++ 编译器对 const 进行了加强,当 c++ 编译器遇到常量声明时,不会像 c...

关键字: c++ C语言 const

返回函数的引用去初始化一个新的引用这个和前面一样,都是不会产生副本,但是现在是用返回值去初始化一个引用声明c,也就是说这时候变成了变量temp的别名,在c的生命周期内temp是一直有效的,这样做完全可以。

关键字: c++ 返回值 引用声明

在进行socket通信开发时,一般会用到TCP或UDP这两种传输层协议,UDP(User Datagram Protocol)是一种面向无连接的协议,在数据发送前,不需要提前建立连接,它可以更高效地传输数据,但可靠性无法...

关键字: socket TCP UDP

对等实体通常是指计算机网络体系结构中处于相同层次的信息单元。

关键字: 网络协议 IP 套接字

套接字是通信的基石,是支持TCP/IP协议的路通信的基本操作单元。可以将套接字看作不同主机间的进程进行双间通信的端点,它构成了单个主机内及整个网络间的编程界面。套接字存在于通信域中,通信域是为了处理一般的线程通过套接字通...

关键字: 套接字 TCP IP

在实际应用中,编程人员通常需要自己编制底层网络应用程序来实现网上的底层通信,如具体实现IP多播通信的功能。编制底层网络应用程序通常要借助于网络数据通信编程接口,而在不同的操作系统中所提供的网络编程接口是有所不同的,如在M...

关键字: 多播 套接字 Winsock

C++是一种面向对象的高级程序设计语言,是C语言的超集。

关键字: c++ C语言

相关阅读:《eBPF技术应用云原生网络实践系列之kubernetes网络》背景介绍Kubernetes中的网络功能,主要包括POD网络,service网络和网络策略组成。其中POD网络和网络策略,都是规定了模型,没有提供...

关键字: service socket

相关阅读:《eBPF技术应用云原生网络实践系列之kubernetes网络》背景介绍Kubernetes中的网络功能,主要包括POD网络,service网络和网络策略组成。其中POD网络和网络策略,都是规定了模型,没有提供...

关键字: service socket

socketfd长什么样子?什么是socketfd?粗糙的来讲,就是网络fd,比如我们最常见的C/S客户端服务端的编程模式,就是网络通信的一种方式。撇开底层和协议细节,网络通信和文件读写从接口上有本质区别吗?其实没啥区别...

关键字: socket
关闭
关闭