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);
            
        }
        
    }
    

}
复制代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用WiFi p2p传输文件C++代码实现,需要使用`<winsock2.h>`库: ```cpp #include <winsock2.h> #include <windows.h> #include <iostream> #pragma comment(lib, "ws2_32.lib") #define PORT 8888 // 自定义口号 int main(int argc, char* argv[]) { // 初始化Winsock2 WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cout << "Failed to initialize Winsock2!" << std::endl; return 1; } // 创建socket SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (serverSocket == INVALID_SOCKET) { std::cout << "Failed to create socket!" << std::endl; WSACleanup(); return 1; } // 绑定口 sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(PORT); serverAddr.sin_addr.s_addr = INADDR_ANY; if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cout << "Failed to bind port!" << std::endl; closesocket(serverSocket); WSACleanup(); return 1; } // 监听连接请求 if (listen(serverSocket, 5) == SOCKET_ERROR) { std::cout << "Failed to listen on socket!" << std::endl; closesocket(serverSocket); WSACleanup(); return 1; } // 接受连接 sockaddr_in clientAddr; int addrLen = sizeof(clientAddr); SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &addrLen); if (clientSocket == INVALID_SOCKET) { std::cout << "Failed to accept connection!" << std::endl; closesocket(serverSocket); WSACleanup(); return 1; } // 接收文件名 char fileName[MAX_PATH]; int fileNameLen = recv(clientSocket, fileName, MAX_PATH, 0); fileName[fileNameLen] = '\0'; std::cout << "Received file name: " << fileName << std::endl; // 打开文件 FILE* file = fopen(fileName, "wb"); if (!file) { std::cout << "Failed to open file for writing!" << std::endl; closesocket(clientSocket); closesocket(serverSocket); WSACleanup(); return 1; } // 接收文件内容 char buffer[1024]; int bytesReceived; while ((bytesReceived = recv(clientSocket, buffer, 1024, 0)) > 0) { fwrite(buffer, sizeof(char), bytesReceived, file); } std::cout << "File received successfully!" << std::endl; // 关闭socket文件 fclose(file); closesocket(clientSocket); closesocket(serverSocket); WSACleanup(); return 0; } ``` 以上代码实现了一个文件接收,需要在WiFi p2p连接之后启动。可以通过以下方式来编译和运行: ```bash $ g++ -o receiver.exe receiver.cpp -lws2_32 $ ./receiver.exe ``` 接下来是文件发送的代码实现: ```cpp #include <winsock2.h> #include <windows.h> #include <iostream> #pragma comment(lib, "ws2_32.lib") #define PORT 8888 // 自定义口号 #define IP "192.168.49.1" // 接收的IP地址 int main(int argc, char* argv[]) { // 初始化Winsock2 WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cout << "Failed to initialize Winsock2!" << std::endl; return 1; } // 创建socket SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (clientSocket == INVALID_SOCKET) { std::cout << "Failed to create socket!" << std::endl; WSACleanup(); return 1; } // 连接到接收 sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(PORT); serverAddr.sin_addr.s_addr = inet_addr(IP); if (connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cout << "Failed to connect to server!" << std::endl; closesocket(clientSocket); WSACleanup(); return 1; } // 发送文件名 char fileName[MAX_PATH] = "test.txt"; // 待发送的文件名 int fileNameLen = strlen(fileName); if (send(clientSocket, fileName, fileNameLen, 0) == SOCKET_ERROR) { std::cout << "Failed to send file name!" << std::endl; closesocket(clientSocket); WSACleanup(); return 1; } // 打开文件 FILE* file = fopen(fileName, "rb"); if (!file) { std::cout << "Failed to open file for reading!" << std::endl; closesocket(clientSocket); WSACleanup(); return 1; } // 发送文件内容 char buffer[1024]; int bytesRead; while ((bytesRead = fread(buffer, sizeof(char), 1024, file)) > 0) { if (send(clientSocket, buffer, bytesRead, 0) == SOCKET_ERROR) { std::cout << "Failed to send file data!" << std::endl; fclose(file); closesocket(clientSocket); WSACleanup(); return 1; } } std::cout << "File sent successfully!" << std::endl; // 关闭socket文件 fclose(file); closesocket(clientSocket); WSACleanup(); return 0; } ``` 以上代码实现了一个文件发送,需要在WiFi p2p连接之后启动。可以通过以下方式来编译和运行: ```bash $ g++ -o sender.exe sender.cpp -lws2_32 $ ./sender.exe ``` 其中,`IP`需要修改为接收的IP地址,`fileName`需要修改为待发送的文件名。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值