美文网首页
2018-03-11

2018-03-11

作者: 黄文俊_ | 来源:发表于2018-03-11 22:03 被阅读0次

    网络聊天室(MFC编程)

    本应用是一款简单的模拟qq聊天应用.主要分为服务器端与客户端

    服务器select端:

    客户端client:

    服务器端代码如下:


    select.cpp:

    // select.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include "dataHandle.h"#include#pragma comment(lib, "ws2_32")

    void SockInit();

    int _tmain(int argc, _TCHAR* argv[])

    {

    SockInit();

    SOCKET lisSock = socket(AF_INET, SOCK_STREAM, 0);

    sockaddr_in addr;

    addr.sin_family = AF_INET;

    addr.sin_port = htons(8090);

    addr.sin_addr.S_un.S_addr = ADDR_ANY;

    bind(lisSock, (sockaddr*)&addr, sizeof(sockaddr));

    listen(lisSock, 5);

    fd_set fds;

    FD_ZERO(&fds);

    FD_SET(lisSock, &fds);  //将监听套接字加入数组中

    timeval timeout;

    timeout.tv_sec = 5;

    timeout.tv_usec = 0;

    while (1)

    {

    fd_set tmSet = fds;  //临时数组

    //移除掉没有事件发生的套接字

    // SELECT 在编程的过程中,经常会遇到许多阻塞的函数,

    //好像read和网络编程时使用的recv, recvfrom函数都是阻塞的函数,

    //当函数不能成功执行的时候,程序就会一直阻塞在这里,无法执行下面的代码。

    //这是就需要用到非阻塞的编程方式,使用select函数就可以实现非阻塞编程。

    select(0, &tmSet, 0, 0, &timeout);

    SOCKET s[5];

    int j = 0;

    //剩下的都是有事件发生的套接字

    for (int i = 0; i < tmSet.fd_count; i++)

    {

    if (tmSet.fd_array[i] == lisSock) //如果是监听监听套接字

    {

    SOCKET cliSock = accept(lisSock, 0, 0);

    printf("新连接.\n");

    FD_SET(cliSock, &fds);  //将已连接的套接字加入数组中

    }

    else

    {

    char recvBuf[1024] = { 0 };

    int recvLen = recv(tmSet.fd_array[i], recvBuf, 1024, 0);

    if (recvLen > 0)

    {

    //处理数据

    HandleData(recvBuf,recvLen);

    printf("%s\n",recvBuf);

    }

    else  //错误发生,或者客户端断开连接

    {

    printf("客户已断开\n");

    FD_CLR(tmSet.fd_array[i], &fds);  //从数组中移除已端口的客户端

    }

    }

    }

    }

    return 0;

    }

    void SockInit()

    {

    WORD wVersionRequested;

    WSADATA wsaData;

    int err;

    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);

    if (err != 0) {

    return;

    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)

    {

    WSACleanup();

    return;

    }

    }


    dataHandle.h 用于服务器接收到不同类型消息处理

    #pragma once#include//处理收到的数据

    bool HandleData(const char* recvData,int len);

    dataHandle.cpp

    #include "stdafx.h"

    #include "dataHandle.h"

    #include "netStruct.h"

    //处理收到的数据

    bool HandleData(const char* recvData,int len)

    {

    //printf("%s : %d", recvData, len);

    short msgID = *(short*)recvData;

    switch (msgID)

    {

    case TALKALL_MSGID:

    {

    //...给所有人的消息

    const MSG_TALKALL* takAll = (MSG_TALKALL*)recvData;

    printf("群聊消息: %s\n", takAll->Content);

    }

    break;

    case TALKONE_MSGID:

    {

    //给某个人的消息

    const MSG_TALKONE* takOne = (MSG_TALKONE*)recvData;

    printf("私聊消息: %s : %s\n", takOne->userName, takOne->Content);

    //takOne.useName

    }

    break;

    case LOGIN_MSGID:

    //登陆消息

    break;

    //.........

    case FILEINFO_MSGID:

    {

    const MSG_SENDFILEINFO *fileInfo = (MSG_SENDFILEINFO*)recvData;

    printf("文件路径:%s ,文件大小:%d\n", fileInfo->fileName, fileInfo->fileSize);

    }

    break;

    case FILE_MSGID:

    {

    const MSG_SENDFILE *file = (MSG_SENDFILE*)recvData;

    printf("文件内容:%s\n", file->fileBuf);

    }

    break;

    default:

    break;

    }

    return true;

    }


    netStruct.h 传输的信息结构体

    #pragma once

    #define TALKALL_MSGID 10001

    #define TALKONE_MSGID 10002

    #define LOGIN_MSGID 10003

    #define SENDPIC_MSGID 10004

    #define FILEINFO_MSGID 10005

    #define FILE_MSGID 10006

    //给所有人发的消息

    struct MSG_TALKALL

    {

    short msgID;

    char Content[1024];

    };

    //给某人发的消息

    struct MSG_TALKONE

    {

    short msgID;

    char userName[32];

    char Content[1024];

    };

    struct MSG_SENDFILEINFO {

    short msgID;

    char fileName[256];

    int fileSize;

    };

    struct MSG_SENDFILE {

    short msgID;

    int bufID;

    short bufSize;

    char fileBuf[1024];

    };


    客户端中

    client.cpp

    BOOL CclientApp::InitInstance(){

    // 如果一个运行在 Windows XP 上的应用程序清单指定要

    // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,

    //则需要 InitCommonControlsEx()。  否则,将无法创建窗口。

    INITCOMMONCONTROLSEX InitCtrls;

    InitCtrls.dwSize = sizeof(InitCtrls);

    // 将它设置为包括所有要在应用程序中使用的

    // 公共控件类。

    InitCtrls.dwICC = ICC_WIN95_CLASSES;

    InitCommonControlsEx(&InitCtrls);

    CWinApp::InitInstance();

    AfxEnableControlContainer();

    // 创建 shell 管理器,以防对话框包含

    // 任何 shell 树视图控件或 shell 列表视图控件。

    CShellManager *pShellManager = new CShellManager;

    // 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题

    CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));

    // 标准初始化

    // 如果未使用这些功能并希望减小

    // 最终可执行文件的大小,则应移除下列

    // 不需要的特定初始化例程

    // 更改用于存储设置的注册表项

    // TODO: 应适当修改该字符串,

    // 例如修改为公司或组织名

    SetRegistryKey(_T("应用程序向导生成的本地应用程序"));

    AfxSocketInit();//网络初始化

    clientSock sock;

    sock.Create();

    bool conRet = sock.Connect(_T("127.0.0.1"), 8090);

    if (conRet == false) {

    int errCode = GetLastError();

    CString err;

    err.Format(_T("连接服务器失败 : %d,请重试!"), errCode);

    MessageBox(0, err, _T("错误"), MB_OK);

    return FALSE;

    }

    Login log;//登录界面

    INT_PTR res = log.DoModal();

    if (res == IDOK) {

    CclientDlg dlg(sock,log.userName);//发送信息界面,将socket传入对话框界面,以发信信息

    m_pMainWnd = &dlg;

    INT_PTR nResponse = dlg.DoModal();

    if (nResponse == IDOK)

    {

    // TODO: 在此放置处理何时用

    //  “确定”来关闭对话框的代码

    }

    else if (nResponse == IDCANCEL)

    {

    // TODO: 在此放置处理何时用

    //  “取消”来关闭对话框的代码

    }

    else if (nResponse == -1)

    {

    TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");

    TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");

    }

    // 删除上面创建的 shell 管理器。

    if (pShellManager != NULL)

    {

    delete pShellManager;

    }

    #ifndef _AFXDLL

    ControlBarCleanUp();

    #endif

    // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,

    //  而不是启动应用程序的消息泵。

    }


    客户端登录界面:

    在文字框输入文字,并发送出去代码如下:

    void CclientDlg::OnBnClickedOk()

    {

    // TODO: 在此添加控件通知处理程序代码

    //CDialogEx::OnOK();

    UpdateData();

    if (content_send.IsEmpty()) {

    MessageBox(_T("请填入消息!"), _T("提示"), MB_OK);

    return;

    }

    if (isSendFile == FALSE) {

    /*

    char *buff;

    buff = (char *)content_send.GetBuffer(0);

    m_sock.Send(buff, strlen(buff),0);//用多字节来发送

    content_send = _T("");//清空内容

    */

    /*

    MSG_TALKONE takone;

    takone.msgID = TALKONE_MSGID;

    strcpy_s(takone.userName, userName);

    strcpy_s(takone.Content, content_send.GetBuffer(0));

    m_sock.Send(&takone, sizeof(MSG_TALKONE));

    */

    MSG_TALKALL takall;

    takall.msgID = TALKALL_MSGID;

    strcpy_s(takall.Content, content_send.GetBuffer(0));

    m_sock.Send(&takall, sizeof(MSG_TALKALL));

    content_send = _T("");//清空内容

    UpdateData(0);

    }

    else//发送文件

    {

    CreateThread(0, 0,sendFileThread, this,0,0);//方法只能为全局方法,不能为类方法

    }

    }

    发送文件代码如下:

        DWORD WINAPI sendFileThread(LPVOID lpParameter)//开启多线程来传输文件

    {

    CclientDlg *dlg = (CclientDlg *)lpParameter;

    CFileStatus fileStatus;

    ULONGLONG size;

    if (CFile::GetStatus(dlg->content_send.GetBuffer(0), fileStatus)) {

    size = fileStatus.m_size;

    }

    MSG_SENDFILEINFO msgInfo;

    msgInfo.msgID = FILEINFO_MSGID;

    strcpy_s(msgInfo.fileName,dlg->content_send.GetBuffer(0));

    msgInfo.fileSize = size;

    dlg->m_sock.Send(&msgInfo, sizeof(MSG_SENDFILEINFO));

    // 只读方式打开文件 

    CFile file;

    BOOL b = file.Open("C:\\Users\\WJ\\Desktop\\wifi.txt", CFile::modeRead);

    while (b)

    {

    // 读取文件数据 

    char ReadBuf[1016] = { 0 };

    int ret = file.Read(ReadBuf, 100);

    printf("%s\n", ReadBuf);

    MessageBox(0, ReadBuf, 0, 0);

    MSG_SENDFILE msg;

    msgInfo.msgID = FILE_MSGID;

    strcpy_s(msg.fileBuf, ReadBuf);

    dlg->m_sock.Send(&msg, sizeof(MSG_SENDFILE));

    if (ret < 100)// 如果到达文件结尾则中止循环

    break;

    }

    // 关闭文件 

    file.Close();

    dlg->isSendFile = FALSE;

    return TRUE;

    }

    运行结果如下:

    相关文章

      网友评论

          本文标题:2018-03-11

          本文链接:https://www.haomeiwen.com/subject/ecfgfftx.html