副标题#e#
措施实现成果:
局域网谈天软件,启动即可找到在线设备,并可以或许举办简朴的文字谈天。
其实下面这个框图已经说明白措施的绝大部门成果道理。
焦点类的措施框图
我以为,这个措施中利用的最好的技能,应该就是IOCP了。后头我会针对IOCP好好地写一篇博文,这个技能固然刚学的时候有点乱,可是确实很好用。
上面的框图中中间的UDPServer线程期待的事件完成是MainServer线程在Listen函数挪用竣事后配置的事件。这里忘了标了。
说明
前几天在尝试室看《Windows网络与通信措施设计》这本书,看完了前5章吧,就以为今朝手头的技能去做一个局域网谈天软件应该差不多了。固然尚有许多细节性的对象还不长短常懂,可是做一个小局限的软件,我自认为是问题不大的。就这样,在约莫4天之后,也就是这个月的18号,这款LANChat措施降生~
首先我声明:因为我第一次写网络相关的措施,所以必定存在疏忽,在相关方面必定是存在不少bug的,别的我在测试的时候也常常碰着措施启动失败的环境,并且原因尚未查明。所以我并不担保这款措施的不变性和安详性。(作为这个措施的设计人员真是感想汗颜~今后会好的)
别的代码中大部门析构函数形同虚设,究竟最初实现的时候尚不清楚可以或许实现与其成果,所以基础就没忌惮资源释放这一块。好比,谈天窗口成立这一块我就没利用delete。
多线程部门因为涉及到对数据的会见问题,所以我利用了要害段:CriticalSection布局以及相关函数。
因为这个文档是在寝室写的,所以没有在线设备,也无法打开谈天窗口,在尝试室三台计较机之间利用没问题。
别的winsock2初始化是在工程中选择的,在如console类的措施中利用socket措施前必然要做好相关的初始化以及库,文件包括事情。
socket利用措施初次实验。
#p#副标题#e#
1 措施实现根基思想
首先用UDP广播通知在线设备,在当地IP对应的位置有设备上线。局域网内的在线设备收到UDP广播动静后,首先会做的工作是,获取方针的IP地点,然后初始化一个sockaddr布局,向其发送TCP毗连请求。
刚上线的设备此时会已经做好接管筹备(这里利用的事件线程同步),在TCP毗连请求成立后,会将其插手到IOCP中,而且为其投递一个数据接管的动静。之后任何到来的动静城市由IOCP的线程函数专门举办处理惩罚,十分的利便。而IOCP线程函数是通过挪用GetQueueCompletionStatues函数将线程添加到IOCP的线程池中的。
这样任何一台刚上限的设备都可以或许找到局域网内的其他在线设备并与其简历TCP链接,为局域网谈天软件的实现奠基了理论基本。
2 界面实现
因为这个措施其实并不设计什么算法性的代码,连数据查找我用的貌似也是STL的find要领,所以对付措施的网络代码我并不想将太多,意义不大,等会先容下焦点类的接口就算完事了,先先容界面的设计。
看着win低画质的界面,即刻以为,Win7下高画质的界面许多几何啦~
首先,主界面中有2个Edit编辑控件,一个List控件,两个按钮。在list控件中包括了两个列,今朝仅仅对第一个IPAddr举办了利用。在线设备在list控件中会显示出来其IP地点。
今朝仅做了双击显示出来的在线项弹出谈天对话框的成果,其余尚未添加。并且按下空格和Enter键会直接退出措施。这个问题属于还未处理惩罚好的键盘动静响应。
(1) 当有数据报到来而且完成吸收后如何通知子窗口而且显示出来?
CLANChat结构函数的参数为一个CDialog的指针。而在主对话框措施中,作为显示的Dialog对话框派生类包括一个CLANChat的工具。在对话框工具启动的时候,在其结构函数的参数列表中为CLANChat工具传入this指针,用于其初始化。这样对付到来的动静,在未弹出窗口的环境下,就可以或许按照对话框窗口的句柄利用postmessage函数将动静派发出去。
同时CLANChat类中的在线列表是一个vector模板,作为其范例参数的是专门设计的OnLineNode类,个中包括了指向字谈天对话框窗口的指针。
class OnLineNode { public: //当年一个小小布局体,如今都需要做结构函数了…… OnLineNode(); //布局体和类在添加数据上确实很有优势~ SOCKET sClient;//I like this name sockaddr_in sa_in;//生存和客户端的IP信息 vector<CString> vec_str_ChatHistory;//在这谈天记录也做了,全职保姆…… CDialog * pDlg;//指向与本身相绑定的对话框工具。 //用于支持STL要领 int operator == (const OnLineNode & OLN); inline void AddtoChatHistory(CString & str);//这个函数太简朴了 void PostMsg(CString & str); };
#p#分页标题#e#
这个类维护了许多的信息,包罗:指向的socket句柄, 地点信息, 谈天记录, 谈天对话框指针。其结构函数会对其举办相应的初始化,好比pDlg在刚启动的时候,其值为NULL.
并且在其双击响应的动静函数中,我也专门对其举办了维护,这样才气对pDlg举办初始化操纵。总体上说,这个类的任务照旧不少的。
上面讲到的动静派发还只是传给主窗口的,那么什么时候传给谈天用的窗口呢?。这里在上面的框图中可以看到在IOCPServer中有动静派发一栏,在这里,会按照pDlg的值,也就是是否窗口已存在来抉择这个动静发送给谁。
代码如下:
//这里直接给本身的对话框窗口动员静这还挺巨大的。 //当窗口已经成立的时候,应该直接给本身的窗口动员静。 //当窗口未成立的时候,应该将动静发送给主措施,然后主措施使相应的item闪烁。 if(it_temp->pDlg != NULL)//窗口未成立 { //在这只要把和本身相关联的在线节点地点值穿已往就OK了~ PostMessage(it_temp->pDlg->m_hWnd, WM_RECVMESSAGE, (DWORD)&(*it_temp), 0); } else//将动静发送主窗口 { PostMessage(pDlg->m_hWnd, WM_RECVMESSAGE, tmpOLN.sClient, pComKey->sa_in.sin_addr.S_un.S_addr); }
个中的it_temp为姑且的迭代器指针,指向OnLineList的成员。所以在这里,当数据到来且谈天窗口已经建设的环境下,谈天窗口就会收到动静而且对数据举办更行。所以指针是个好对象。
别的关于上面的CListCtrl的初始化以及气势气魄配置,我在前面的博文中专门举办了讲授。尽量只是一部门,可是做到上面的结果照旧很容易的。
3 CLANChat类的接口
class CLANChat { private: //local veriable CDialog * m_pDlgFrame;//指向主体窗口 DlgIOCP ** m_pDlgIOCPArray; //socket about static vector<OnLineNode> g_OnLineList;//需要维护的在线列表 static volatile HANDLE hIOCP;//IO完成端口句柄 static ULONG g_IPaddr;//当地IP地点 static SOCKET sListen;//当地SOCKET static sockaddr_in m_local_sa_in;//当地sockaddr private: //Method Lock CLANChat(CLANChat &); CLANChat & operator = (CLANChat &); public: CLANChat(CDialog * pDlg); ~CLANChat(void); void CreateUDPBroadcastThread(LPVOID lpParam); DWORD GetCPUNumber(); int SendInfo(char * pbuf, int length, in_addr nListID); SOCKET GetSocketWithIP(in_addr addr); vector<OnLineNode> * GetOnLineListPtr(); CEvent m_eMainUDPServer; //类线程函数必需是静态函数,因为静态函数无this指针 static DWORD WINAPI MainServer(LPVOID lpParam); static DWORD WINAPI UDPServer(LPVOID lpParam); static DWORD WINAPI IOCPTCPSever(LPVOID lpParam); };
上面的这个类就是我代码中最焦点的一个类吧,个中的重要数据并不算多,并且跟着代码变动,有些变量和函数已经变得没用可能不符合这种生命方法了。其主要原因照旧在于,我初次编写多线程的措施,对个中的部门内容利用的不纯熟,改来改去,这代码我此刻本身看着都有点以为糟心。唉,假如再让我写一遍这个措施,我做的第一件工作必定是先设计,把框图什么的都先设计好,然后再开始动手写代码。此刻我以为我写这个文档都有点不知道应该怎么写了~
这个类最主要的任务就是启动3中线程:MainServer UDPServer IOCPServer。就这些事情,然后还要认真动静的派送,仅此罢了。
在这措施中我最满足的就是,我较量好的利用了IOCP,这一点我很兴奋,因为之前在做串口调试助手的时候,曾经在《windows焦点编程》这本书中看到过,可是其时没领略。如今到了Socket下,我用的还不错,并且算是领略其道理了。
4:CLANChat类的实现:(代码)
#include "stdafx.h" #include "LANChat.h" #include "afxdialogex.h" #include "LANChat20130815.h" #include <algorithm> DWORD CLANChat::g_IPaddr = 0; SOCKET CLANChat::sListen = 0; HANDLE volatile CLANChat::hIOCP = 0; sockaddr_in CLANChat::m_local_sa_in; vector<OnLineNode> CLANChat::g_OnLineList; using namespace std; CLANChat::CLANChat(CDialog * pDlg):m_pDlgFrame(pDlg) { //线程同步时间初始化 m_eMainUDPServer.ResetEvent(); //sockaddr m_local_sa_in.sin_family = AF_INET; m_local_sa_in.sin_port = htons(4567); m_local_sa_in.sin_addr.S_un.S_addr = INADDR_ANY; //这里有须要获取一下当地的IP地点 char szHostName[256]; gethostname(szHostName, 256); hostent * pht = gethostbyname(szHostName); char * pIP = pht->h_addr_list[0];//多网卡设备还临时无法处理惩罚 memcpy(&g_IPaddr, pIP, pht->h_length); //IOCP建设以及sever线程启动 //按照焦点数来建设线程数 int cpunum = GetCPUNumber(); hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, cpunum); m_pDlgIOCPArray = new DlgIOCP * [cpunum]; for(int i = 0;i < cpunum;++ i) { m_pDlgIOCPArray[i] = new DlgIOCP; m_pDlgIOCPArray[i]->hIOCP = hIOCP; m_pDlgIOCPArray[i]->pDlg = pDlg; } //在这里需要特别建设一个主处事线程,因为在console措施中,可以或许利用措施的线程,而这里无法利用。 CreateThread(0, 0, MainServer, this, 0, 0); //接着建设UDP线程。开始广播以及筹备接管UDP广播 CreateUDPBroadcastThread(this); //建设IOCP线程,并添加到线程池中 for(int i = 0;i < cpunum;++ i) { CreateThread(NULL, 0, IOCPTCPSever, m_pDlgIOCPArray[i], 0, 0); } } CLANChat::~CLANChat(void) { //清理事情 delete [] m_pDlgIOCPArray; } void CLANChat::CreateUDPBroadcastThread(LPVOID lpParam) { CreateThread(0, 0, UDPServer, lpParam, 0, 0); } DWORD WINAPI CLANChat::MainServer(LPVOID lpParam) { CLANChat * pCLC = (CLANChat *)lpParam; //成立当地TCP毗连 SOCKET sListen = socket(AF_INET, SOCK_STREAM, 0); //绑定到一个当地地点 CRITICAL_SECTION cs; InitializeCriticalSection(&cs); if(bind(sListen, (sockaddr *)&m_local_sa_in, sizeof(sockaddr)) == -1) { AfxMessageBox(_T("Bind Fail!!!")); return 0; } listen(sListen, 10);//随便设个数 //char pBufferRecv[1024]; //这个只要利用一次,就是最初的那次。 pCLC->m_eMainUDPServer.SetEvent(); while(TRUE) { sockaddr_in sa_temp; int nsa_size = sizeof(sockaddr_in); SOCKET snewsocket = accept(sListen, (sockaddr *)&sa_temp, &nsa_size); if(snewsocket == INVALID_SOCKET) { continue; } //在有效的新毗连成立后,立即将其插手到在线列表中 OnLineNode tmpnode; tmpnode.sClient = snewsocket; tmpnode.sa_in = sa_temp; EnterCriticalSection(&cs); g_OnLineList.push_back(tmpnode); LeaveCriticalSection(&cs); //在这里利用IO完成端口来做毗连监听 //先建设一个key布局 PIOCP_KEY pTempKey = (PIOCP_KEY)GlobalAlloc(GPTR, sizeof(IOCP_KEY)); if(pTempKey == NULL) continue; pTempKey->sClient = snewsocket; pTempKey->sa_in = sa_temp; pTempKey->sa_in.sin_port = htons(4567); //将IO完成端口和新建设的套接字毗连绑定在一起 CreateIoCompletionPort((HANDLE)snewsocket, pCLC->hIOCP, (ULONG_PTR)pTempKey/*这个参数要用来通报socket所属以及方针地点,z临时NULL*/, 0); //投递一个针对方针IO端口的数据接管请求 PMOV pMyOv = (PMOV)GlobalAlloc(GPTR, sizeof(MOV)); memset(pMyOv, 0, sizeof(MOV)); if(pMyOv == NULL) return 0; pMyOv->wbuf.buf = pMyOv->buf; pMyOv->wbuf.len = 1024; pMyOv->OperateType = 0;//read DWORD flag = 0; //send(snewsocket, "HELLO", 7, 0); WSARecv(snewsocket, &pMyOv->wbuf, 1, &pMyOv->nRecvLength, &flag, (LPWSAOVERLAPPED)pMyOv, NULL); //后头这些都是用来将数据添加到在线列表后,向dialogbox发送动静 PostMessage(pCLC->m_pDlgFrame->m_hWnd, WM_NEWSOCKET, snewsocket, sa_temp.sin_addr.S_un.S_addr); //pure test send(snewsocket, "0816", 4, 0); } return 0; } DWORD WINAPI CLANChat::UDPServer(LPVOID lpParam) { CLANChat * pCLC = (CLANChat *)lpParam; sockaddr_in sa_broadcast, sa_local; //broadcast sa_broadcast.sin_addr.S_un.S_addr = inet_addr("255.255.255.255"); sa_broadcast.sin_family = AF_INET; sa_broadcast.sin_port = htons(4567); //local sa_local.sin_addr.S_un.S_addr = INADDR_ANY; sa_local.sin_family = AF_INET; sa_local.sin_port = htons(4567); //socket SOCKET sUDPBroadcast = socket(AF_INET, SOCK_DGRAM, 0); SOCKET sUDPListen = socket(AF_INET, SOCK_DGRAM, 0); if(bind(sUDPListen, (sockaddr *)&sa_local, sizeof(sockaddr)) == -1) { AfxMessageBox(_T("UDP Bind Fail!")); return 0; } //配置广播套接字模式为广播通信 BOOL bBroadcast = TRUE; setsockopt(sUDPBroadcast, SOL_SOCKET, SO_BROADCAST, (char *)&bBroadcast, sizeof(BOOL)); //期待MainServer根基完成初始化 WaitForSingleObject(pCLC->m_eMainUDPServer.m_hObject, INFINITE); //AfxMessageBox(_T("Get The Event!")); //广播通信 sendto(sUDPBroadcast, "C", 1, 0, (sockaddr *)&sa_broadcast, sizeof(sockaddr)); //发送完广播数据,这个socket就没什么用了 closesocket(sUDPBroadcast); //数据接管相关变量 char BUF[3];//不消很大 int nfromlength = sizeof(sockaddr); CRITICAL_SECTION cs; InitializeCriticalSection(&cs); while(TRUE) { //正在期待吸收数据 //这里今后可以改成WSARecvFrom函数,再用一个专门的线程来精心数据接管处理惩罚,效率会更高。 int recvlength = recvfrom(sUDPListen, BUF, 3, 0, (sockaddr *)&sa_broadcast, &nfromlength); //检测收到的是否为当地IP if(g_IPaddr == sa_broadcast.sin_addr.S_un.S_addr) continue; if(recvlength > 0) { //这一个有效的UDP数据 BUF[recvlength] = 0; #ifdef _DEBUG ; #endif //将该sockaddr布局添加到在线vector容器中,而且通知主节制线程将其添加到显示列表 //与其成立TCP毗连,这样就可以或许很好的,很实时的实此刻线列表的更新 //成立一个新的当地TCP套接字 SOCKET sNewConnectSock = socket(AF_INET, SOCK_STREAM, 0); sa_broadcast.sin_port = htons(4567); #ifdef _DEBUG ;//cout << "Connecting IP " << inet_ntoa(sa_broadcast.sin_addr) << endl; #endif if(connect(sNewConnectSock, (sockaddr *)&sa_broadcast, sizeof(sockaddr)) == -1) { #ifdef _DEBUG ;//cout << "UDP Thread Connect Fail!" << endl; #endif //别忘了在毗连失败后释放资源 closesocket(sNewConnectSock); continue;//期待下一个广播动静 } //添加到全局列表中 OnLineNode tmpOLN; tmpOLN.sClient = sNewConnectSock; tmpOLN.sa_in = sa_broadcast; EnterCriticalSection(&cs); g_OnLineList.push_back(tmpOLN); EnterCriticalSection(&cs); #ifdef _DEBUG ;//cout << "UDP Thread's TCP Connect Success!" << endl; #endif //在这里既需要将其插手IO完成端口,还要投递一个WSARecv动静 PIOCP_KEY ptmpkey = (PIOCP_KEY)GlobalAlloc(GPTR, sizeof(IOCP_KEY)); ptmpkey->sClient = sNewConnectSock; ptmpkey->sa_in = sa_broadcast; ptmpkey->sa_in.sin_port = htons(4567); CreateIoCompletionPort((HANDLE)ptmpkey->sClient, pCLC->hIOCP, (ULONG_PTR)ptmpkey, 0); //投递WSARecv请求 PMOV pMyOv = (PMOV)GlobalAlloc(GPTR, sizeof(MOV)); memset(pMyOv, 0, sizeof(MOV)); pMyOv->wbuf.buf = pMyOv->buf; pMyOv->wbuf.len = 1024; pMyOv->OperateType = 0;//read DWORD flag = 0; WSARecv(ptmpkey->sClient, &pMyOv->wbuf, 1, &pMyOv->nRecvLength, &flag, (LPWSAOVERLAPPED)pMyOv, NULL); //添加完布局后,向主窗口发送一个更新动静,至于发什么数据,临时还没想好。 PostMessage(pCLC->m_pDlgFrame->m_hWnd, WM_NEWSOCKET, tmpOLN.sClient, tmpOLN.sa_in.sin_addr.S_un.S_addr); //pure test send(ptmpkey->sClient, "0816", 4, 0); } else continue; } return 0; } DWORD WINAPI CLANChat::IOCPTCPSever(LPVOID lpParam)//这个参数需要传入的是IOCP,挪用GetQueue……函数时需要利用 { DlgIOCP * DI = (DlgIOCP *)lpParam; HANDLE hCompletion = DI->hIOCP; CDialog * pDlg = DI->pDlg; delete DI;//这个要立即销毁,之后没用的。 PMOV pMyOv; PIOCP_KEY pComKey; DWORD dwRecvNum; DWORD dwflag; BOOL bOK; CRITICAL_SECTION cs; InitializeCriticalSection(&cs); while(TRUE) { dwRecvNum = 0; dwflag = 0; OnLineNode tmpOLN; bOK = GetQueuedCompletionStatus(hCompletion, &dwRecvNum, (PULONG_PTR)&pComKey, (LPOVERLAPPED *)&pMyOv, INFINITE); //不管是什么动静,都要先获取其对应于表中的地点 EnterCriticalSection(&cs); tmpOLN.sClient = pComKey->sClient; ASSERT(!g_OnLineList.empty()); vector<OnLineNode>::iterator it_temp = find(g_OnLineList.begin(), g_OnLineList.end(), tmpOLN); if(bOK == 0) { #ifdef _DEBUG ;//cout << "GetQueuedCompletionStatus Error!!!" << endl; #endif closesocket(pComKey->sClient); //CString tmpstr; //tmpstr = inet_ntoa(pComKey->sa_in.sin_addr); //AfxMessageBox(tmpstr); //删除列表中的数据 if(it_temp != g_OnLineList.end()) g_OnLineList.erase(it_temp); PostMessage(pDlg->m_hWnd, WM_CLOSESOCKET, tmpOLN.sClient, pComKey->sa_in.sin_addr.S_un.S_addr); GlobalFree(pComKey); GlobalFree(pMyOv); LeaveCriticalSection(&cs); continue; } //下面这句的判定条件是,当收到读或写数据后,数据长度却为0,则表白对方封锁了套接字 if(0 == dwRecvNum && (pMyOv->OperateType == 0 || pMyOv->OperateType == 1)) { #ifdef _DEBUG /*cout << "Socket From IP " << inet_ntoa(pComKey->sa_in.sin_addr) << " Has Closed" << endl;*/ #endif //tmpOLN.sClient = pComKey->sClient; closesocket(pComKey->sClient); //删除列表中的数据 //CString tmpstr; //tmpstr = inet_ntoa(pComKey->sa_in.sin_addr); //AfxMessageBox(tmpstr); //vector<OnLineNode>::iterator it_temp = find(g_OnLineList.begin(), g_OnLineList.end(), tmpOLN); if(it_temp != g_OnLineList.end()) g_OnLineList.erase(it_temp); PostMessage(pDlg->m_hWnd, WM_CLOSESOCKET, tmpOLN.sClient, pComKey->sa_in.sin_addr.S_un.S_addr); GlobalFree(pComKey); GlobalFree(pMyOv); LeaveCriticalSection(&cs); continue; } ;//cout << "IOCP Server Thread Get A Message!" << endl; CString tmp_str; switch(pMyOv->OperateType) { case 0://读 WSARecv(pComKey->sClient, &pMyOv->wbuf, 1, &dwRecvNum, &dwflag, (LPOVERLAPPED)pMyOv, NULL); //这里的数据必需做成Unicode ((TCHAR *)pMyOv->wbuf.buf)[dwRecvNum] = 0; /*这里的环境是收到了发送到本措施的动静,可是,在在线列表中并不存在 这种环境是不公道的。只有在在线列表中的数据才气收到动静。也就是说,肯能是在push_back 函数的挪用序次上呈现了问题。 */ if(it_temp == g_OnLineList.end()) { LeaveCriticalSection(&cs); continue; } ;//cout << "Message From" << inet_ntoa(pComKey->sa_in.sin_addr) << endl ;// << "Info :" << pMyOv->wbuf.buf << endl; tmp_str = (TCHAR *)pMyOv->wbuf.buf; tmp_str += "\r\n"; it_temp->vec_str_ChatHistory.push_back(tmp_str); //这里直接给本身的对话框窗口动员静这还挺巨大的。 //当窗口已经成立的时候,应该直接给本身的窗口动员静。 //当窗口未成立的时候,应该将动静发送给主措施,然后主措施使相应的item闪烁。 if(it_temp->pDlg != NULL)//窗口未成立 { //在这只要把和本身相关联的在线节点地点值穿已往就OK了~ PostMessage(it_temp->pDlg->m_hWnd, WM_RECVMESSAGE, (DWORD)&(*it_temp), 0); } else//将动静发送主窗口 { PostMessage(pDlg->m_hWnd, WM_RECVMESSAGE, tmpOLN.sClient, pComKey->sa_in.sin_addr.S_un.S_addr); } break; case 1: break; case 2: break; } LeaveCriticalSection(&cs); } return 0; } DWORD CLANChat::GetCPUNumber() { SYSTEM_INFO si; GetSystemInfo(&si); return si.dwNumberOfProcessors; } /* 2013 08 15 17:15 这个函数用来发送数据,参数1为要传送的字符串,参数2为字符串长度,3为在线列表中的ID号,这个号最好是用IP地点号。 返回值是-1或正常发送的字节数 */ int CLANChat::SendInfo(char * pbuf, int length, in_addr nListID) { SOCKET sSend = GetSocketWithIP(nListID); if(sSend == 0)//表白ID是错的 { AfxMessageBox(_T("SendInfo Wrong ID")); return -1; } return send(sSend, pbuf, length, 0); } int OnLineNode::operator == (const OnLineNode & OLN) { if(OLN.sClient == sClient) return 1; else return 0; } void OnLineNode::AddtoChatHistory(CString & str) { vec_str_ChatHistory.push_back(str); } void OnLineNode::PostMsg(CString & str) { if(pDlg != NULL) { PostMessage(pDlg->m_hWnd, WM_RECVMESSAGE, (DWORD)&str, 0); AddtoChatHistory(str); } } SOCKET CLANChat::GetSocketWithIP(in_addr addr) { int VecSize = g_OnLineList.size(); for(int i = 0;i < VecSize;++ i) { if(g_OnLineList[i].sa_in.sin_addr.S_un.S_addr == addr.S_un.S_addr) return g_OnLineList[i].sClient; } return 0; } vector<OnLineNode> * CLANChat::GetOnLineListPtr() { return &g_OnLineList; } OnLineNode::OnLineNode() { sClient = 0; pDlg = NULL; } // CChatDlg dialog IMPLEMENT_DYNAMIC(CChatDlg, CDialogEx) CChatDlg::CChatDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CChatDlg::IDD, pParent) { } CChatDlg::~CChatDlg() { } void CChatDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_RECVEDIT, m_RecvEditCtrl); DDX_Control(pDX, IDC_SENDEDIT, m_EditSend); } BEGIN_MESSAGE_MAP(CChatDlg, CDialogEx) ON_MESSAGE(WM_RECVMESSAGE, &CChatDlg::OnRecvmessage) ON_BN_CLICKED(IDC_BUTTONSEND, &CChatDlg::OnBnClickedButtonsend) END_MESSAGE_MAP() // CChatDlg message handlers //参数1是一个指向和本对话框绑定的节点迭代器 afx_msg LRESULT CChatDlg::OnRecvmessage(WPARAM wParam, LPARAM lParam) { //这里主要的操纵就是对输出窗口的操纵了。 //获取本身所对应的节点 OnLineNode * pNode = (OnLineNode *)wParam; if(!pNode->vec_str_ChatHistory.empty()) { //这里每次只添加最后一个即可,第一次的初始化不是在这里做的。 int nLength = m_RecvEditCtrl.SendMessage(WM_GETTEXTLENGTH); m_RecvEditCtrl.SetSel(nLength, nLength); m_RecvEditCtrl.ReplaceSel(pNode->vec_str_ChatHistory[pNode->vec_str_ChatHistory.size() - 1]); } return 0; } void CChatDlg::OnBnClickedButtonsend() { // TODO: Add your control notification handler code here int nLineNumber = m_EditSend.GetLineCount(); if(nLineNumber == 0) return; //CString cstmp; TCHAR buf[256]; int nTxtLength = 0; memset(buf, 0, sizeof(TCHAR) * 256); for(int i = 0;i < nLineNumber;++ i) { nTxtLength += m_EditSend.GetLine(i, buf + nTxtLength, 256); } buf[nTxtLength] = 0; //AfxMessageBox(cstmp); //第N次感应万恶的Unicode send(pOLN->sClient, (char *)buf, nTxtLength * sizeof(TCHAR), 0); //数据发送完之后,还要对当地的数据举办更新,也就是谈天窗口上要举办处理惩罚。 //主动向谈天记录列表中添加一个记录 而且发送动静到当地的接管窗口对显示数据举办更新。 wcscat(buf, _T("\r\n")); pOLN->vec_str_ChatHistory.push_back(buf); int nLength = m_RecvEditCtrl.SendMessage(WM_GETTEXTLENGTH); m_RecvEditCtrl.SetSel(nLength, nLength); m_RecvEditCtrl.ReplaceSel(pOLN->vec_str_ChatHistory[pOLN->vec_str_ChatHistory.size() - 1]); }
作者:cnblogs Matrix_R