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

来源:互联网 发布:linux vim命令模式 编辑:程序博客网 时间:2024/05/17 01:01

折腾了一个早上在原来的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;}