2018-03-11

网络聊天室(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;

}

运行结果如下:


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容

  • 大纲 一.Socket简介 二.BSD Socket编程准备 1.地址 2.端口 3.网络字节序 4.半相关与全相...
    VD2012阅读 2,335评论 0 5
  • WinSock2网络通信(win32编程) 本代码有服务器端server.cpp和客户端client.cpp,客户...
    黄文俊_阅读 325评论 0 0
  • 下面为Daytime这个服务的源代码例子,同时兼容IPV6和IPV4的地址,最后部分有更多说明。 单播模式下的Se...
    天楚锐齿阅读 5,668评论 0 2
  • 参考链接:http://blog.csdn.net/sweetsnow24/article/details/863...
    Tony1213阅读 4,440评论 0 0
  • 问:“五音不全”怎么说? 答:【音痴】 这里的「音痴」是日语,念作おんち。 其实要得出这个答案并不简单。我们只是因...
    bravo1988阅读 540评论 0 1