mfc socket发送和接收数据和文件

折腾了一个早上在原来的kinect程序上写一个远程发送图片功能,把截图用socket发送出去,才实现windows下两程序的对话。
主要参考了两篇博客,鞠躬感谢两位作者:

http://blog.csdn.net/u010477528/article/details/41680425
http://www.cnblogs.com/wainiwann/archive/2012/05/22/socket.html

server服务器端

此处把原来的kinect程序当成服务器端,改写了一下界面,加了一个远程连接按钮和发送输入框和发送按钮。

这里写图片描述

添加头文件 #include “winsock2.h”
SOCKET listen_sock;
SOCKET sock;

添加socket线程处理函数

UINT Server_Th(LPVOID p)
{
    WSADATA wsaData;

    WORD wVersion;

    wVersion = MAKEWORD(2, 2);

    WSAStartup(wVersion, &wsaData);

    SOCKADDR_IN local_addr;
    SOCKADDR_IN client_addr;
    int iaddrSize = sizeof(SOCKADDR_IN);
    int res;
    char msg[1024];
    CopentestDlg * dlg = (CopentestDlg *)AfxGetApp()->GetMainWnd();
    local_addr.sin_family = AF_INET;
    local_addr.sin_port = htons(5150);
    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if ((listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {

        dlg->pEdit->ReplaceSel(_T("创建监听失败\r\n"));
    }
    if (bind(listen_sock, (struct sockaddr*) &local_addr, sizeof(SOCKADDR_IN)))
    {
        dlg->pEdit->ReplaceSel(_T("绑定错误\r\n"));
    }
    listen(listen_sock, 1);
    if ((sock = accept(listen_sock, (struct sockaddr *)&client_addr, &iaddrSize)) == INVALID_SOCKET)
    {
        dlg->pEdit->ReplaceSel(_T("accept 失败\r\n"));
    }
    else
    {
        CString port;
        int temp = ntohs(client_addr.sin_port);
        port.Format(_T("%d"), temp);
        //port.Format("%d", int(ntohs(client_addr.sin_port)));
        dlg->pEdit->ReplaceSel(_T("已连接来自:") + CString(inet_ntoa(client_addr.sin_addr)) + _T("  端口:") + port+"\r\n");
    }

    //接收数据  
    while (1)
    {
        if ((res = recv(sock, msg, 1024, 0)) == -1)
        {
            dlg->pEdit->ReplaceSel(_T("失去连接\r\n"));
            break;
        }
        else
        {
            msg[res] = '\0';
            dlg->pEdit->ReplaceSel(_T("client:" + CString(msg)) + "\r\n");
        }
    }
    return 0;
}

修改初始化部分OnInitDialog(),添加代码

此处是用来获取本机地址及初始化socket

    AfxBeginThread(&Server_Th, 0); //初始化socket
    send_edit = (CEdit *)GetDlgItem(IDC_ESEND);

    send_edit->SetFocus();

    char name[80];
    CString IP;
    hostent * pHost;
    WSADATA wsData;
    ::WSAStartup(MAKEWORD(2, 2), &wsData);
    //获得主机名  
    if (gethostname(name, sizeof(name)))
    {
        pEdit->ReplaceSel(_T("无法获取本机地址"));
        return TRUE;
    }

    pHost = gethostbyname(name);//获得主机结构  
    IP = inet_ntoa(*(in_addr *)pHost->h_addr);
    pEdit->ReplaceSel(_T("本机地址")+IP+ "\r\n");

发送文字消息

发送文字消息的处理比较简单,就是获取字符转成char *后发送

void CopentestDlg::OnBnClickedConnect()
{
    CString str;
    char * msg;
    send_edit->GetWindowText(str);
    USES_CONVERSION; //cstring 转char*
    msg = T2A(str);
    if (send(sock, msg, strlen(msg), 0) == SOCKET_ERROR)
    {
        pEdit->ReplaceSel(_T("发送失败\r\n"));
    }

    else if (str == "")
    {
        AfxMessageBox(_T("请输入信息"));
    }
    else
    {
        pEdit->ReplaceSel(_T("server:") + str + _T("\r\n"));//消息上屏,清空输入,并重获焦点  
        send_edit->SetWindowText(_T(""));
        send_edit->SetFocus();
    }
}

远程连接发送图片

远程连接按钮主要是用来发送从kinect截图的图片,这里主要思想是先告诉客户端“我要发送图片啦”,客户端收到讯息后进入准备接收图片的状态,然后服务器发送图片文件大小,客户端收到图片大小后,根据图片大小循环接收图片。

void CopentestDlg::OnBnClickedRemote()
{
    HANDLE hFile;
    hFile = CreateFile(CString(colorImagePath.c_str()), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    unsigned long long file_size = 0;
    file_size = GetFileSize(hFile, NULL);
    char buffer[BUFFER_SIZE];
    const char * str= "准备发送图片";
    //发送准备发送命令
    memset(buffer, 0, BUFFER_SIZE);
    strncpy(buffer, str, strlen(str));
    if (send(sock, buffer, strlen(str), 0) == SOCKET_ERROR)
    {
        pEdit->ReplaceSel(_T("发送失败\r\n"));
        return;
    }
    else
    {
        pEdit->ReplaceSel(_T("server:准备发送图片\r\n"));//消息上屏,清空输入,并重获焦点 
    }
    //发送图片长度
    memset(buffer, 0, sizeof(buffer));
    memcpy(buffer, &file_size, sizeof(file_size) + 1);
    if (send(sock, buffer, sizeof(file_size) + 1, 0) == SOCKET_ERROR)
    {
        pEdit->ReplaceSel(_T("发送失败\r\n"));
        return;
    }
    else
    {
        pEdit->ReplaceSel(_T("开始发送图片\r\n"));//消息上屏,清空输入,并重获焦点 
    }
    memset(buffer, 0, sizeof(buffer));
    DWORD dwNumberOfBytesRead;
    do
    {
        ::ReadFile(hFile, buffer, sizeof(buffer), &dwNumberOfBytesRead, NULL);
        ::send(sock, buffer, dwNumberOfBytesRead, 0);
    } while (dwNumberOfBytesRead);
    CloseHandle(hFile);
    pEdit->ReplaceSel(_T("成功发送图片\r\n"));
}

客户端

这里的客户端是重新写了一个mfc程序。界面是这样的

这里写图片描述

初始化函数进行一些基本的控件获取和设置,这里把ip填成服务器端获取的ip就可以了

// TODO: 在此添加额外的初始化代码
    edit_show = (CEdit *)GetDlgItem(IDC_ESHOW);
    edit_send = (CEdit *)GetDlgItem(IDC_ESEND);
    btn_conn = (CButton *)GetDlgItem(IDC_BCONNECT);
    edit_ip = (CEdit *)GetDlgItem(IDC_EIP);

    edit_ip->SetWindowText(_T("192.168.31.1"));

    if (!AfxSocketInit())
    {
        AfxMessageBox(_T("失败"));
        return FALSE;
    }

连接按钮

点击连接后,就可以创建socket并连接到服务器端,然后开启接收线程

void CsocketClientDlg::OnBnClickedBconnect()
{
    WSADATA wsaData;
    SOCKADDR_IN server_addr;

    WORD wVersion;

    wVersion = MAKEWORD(2, 2);

    WSAStartup(wVersion, &wsaData);

    CString ip;
    edit_ip->GetWindowText(ip);//取得服务器的IP地址  

    USES_CONVERSION; //cstring 转char*
    server_addr.sin_addr.s_addr = inet_addr(T2A(ip));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(5150);

    if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {
        edit_show->ReplaceSel(_T("创建socket失败\r\n"));
    }
    if (connect(sock, (struct sockaddr *) &server_addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
    {
        edit_show->ReplaceSel(_T("连接失败\r\n"));
    }
    else
    {
        edit_show->ReplaceSel(_T("连接成功\r\n"));
        AfxBeginThread(&Recv_Th, 0);//开启接收线程
        btn_conn->EnableWindow(FALSE);//按钮变灰  
    }
}

消息发送按钮

与服务器的消息发送类似

// TODO: 在此添加控件通知处理程序代码  
    CString str;
    char * msg;
    edit_send->GetWindowText(str);
    USES_CONVERSION; //cstring 转char*
    msg = T2A(str);
    if (send(sock, msg, strlen(msg), 0) == SOCKET_ERROR)
    {
        Update(_T("发送失败"));
    }
    else if (str == "")
    {
        AfxMessageBox(_T("请输入信息"));
    }
    else
    {
        Update(_T("client:") + str);//消息上屏,清空输入,并重获焦点  
        edit_send->SetWindowText(_T(""));
        edit_send->SetFocus();
    }

接收数据和图片

这部分就比较复杂了,主要是要区分一下图片接收部分和普通消息的接收部分。通过判断服务器发送的命令是否是“准备发送图片”来进入接收图片循环。根据图片大小接收图片。

UINT Recv_Th(LPVOID p)
{
    int res;
    char msg[1024];
    unsigned long long file_size = 0; //文件的大小
    CString str;
    CsocketClientDlg * dlg = (CsocketClientDlg *)AfxGetApp()->GetMainWnd();
    dlg->Update(_T("Initialization"));
    const char * flag = "准备发送图片";
    const char * filename = "./images/cut.png";
    char buffer[BUFFER_SIZE];
    while (1)
    {
        if ((res = recv(sock, msg, 1024, 0)) == -1)
        {
            dlg->Update(_T("失去连接"));
            return 0;
        }
        else
        {
            msg[res] = '\0';
            if (strcmp(msg,flag)==0) {
                dlg->Update(_T("服务器要发送图片了,准备接收"));

                if ((res = recv(sock, (char*)&file_size, sizeof(unsigned long long) + 1, NULL)) == -1)
                {
                    dlg->Update(_T("失去连接"));
                    return 0;
                }
                else {
                    unsigned short maxvalue = file_size;    //此处不太稳妥 当数据很大时可能会出现异常
                    dlg->Update(_T("开始接收图片"));
                    HANDLE hFile;
                    hFile = CreateFile(CString(filename), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
                    DWORD dwNumberOfBytesRecv = 0;
                    DWORD dwCountOfBytesRecv = 0;
                    memset(buffer, 0, BUFFER_SIZE);
                    //接收图片
                    do
                    {
                        dwNumberOfBytesRecv = ::recv(sock, buffer, sizeof(buffer), 0);
                        ::WriteFile(hFile, buffer, dwNumberOfBytesRecv, &dwNumberOfBytesRecv, NULL);
                        dwCountOfBytesRecv += dwNumberOfBytesRecv;
                    } while (file_size - dwCountOfBytesRecv);
                    CloseHandle(hFile);
                    dlg->Update(_T("文件接收成功"));
                }
            }
            else
            {
                dlg->Update(_T("server:") + CString(msg));
            }
        }
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值