简单CS多人聊天工具

最近做培训,由于在同一局域网,本来打算用飞秋的,但由于飞秋是广播包传输数据,再加上学校机房的网络好像出了点问题,所以一些师弟师妹收不到消息。

挺悲催的,然后就写了这样一个小工具。虽然已经能使用算法和数据结构,自以为思路也应该很清晰了,却发现做起来还是有点难。

后来才发现,我才做到一半:程序=算法+数据结构+方法(思想);软件=程序+软件工程。

我还只是能写程序,所以写一个大一点的软件有时就会思路混乱,看来又要去研究下软件工程了。

纪念那个上午和下午~

 

客户端:

/////////////////////////////
//RichEdit.h:客户端的某个头文件,用于向RicEdit控件写入数据
/////////////////////////////
#include <commctrl.h> #include <RichEdit.h> #pragma comment(lib,"comctl32") //清空 void ClearData(HWND hRichEdit) { SendMessage(hRichEdit,WM_SETTEXT,0,(WPARAM)(LPCTSTR)""); } //添加字符串 void AppendStrData(HWND hRichEdit,char *szData) { SendMessage(hRichEdit,EM_SETSEL,-1,-1); SendMessage(hRichEdit,EM_REPLACESEL,0,(WPARAM)(LPCTSTR)szData); SendMessage(hRichEdit,EM_SETSEL,-1,-1); } //添加有色字符串 void AppendSectionData(HWND hRichEdit,char *szData,COLORREF RGB) { DWORD nPosStart,nPosEnd; CHARFORMAT cf; memset(&cf,0x00,sizeof(cf)); cf.cbSize=sizeof(cf); cf.dwMask=CFM_COLOR|CFM_BOLD; cf.crTextColor=RGB; cf.dwEffects=CFE_BOLD; SendMessage(hRichEdit,EM_SETSEL,-1,-1); nPosStart=LOWORD(SendMessage(hRichEdit,EM_GETSEL,0,0)); //获取光标位置 SendMessage(hRichEdit,EM_REPLACESEL,0,(WPARAM)(LPCTSTR)szData); //设置文字 nPosEnd=nPosStart+strlen(szData); SendMessage(hRichEdit,EM_SETSEL,(WPARAM)(INT)nPosStart,(LPARAM)(INT)nPosEnd); //设置选区 SendMessage(hRichEdit,EM_SETCHARFORMAT,SCF_SELECTION,(LPARAM)&cf); //设置选区内容颜色|加粗 SendMessage(hRichEdit,EM_SETSEL,-1,-1); }
/////////////////////////
//client.cpp:客户端主程序文件。
/////////////////////////
#include "resource.h" #include <NewSocket.h> #include <windows.h> #include <stdio.h> #include <SkinH.h> #include <time.h> #include <MMSYSTEM.H> #include "RichEdit.h" #pragma comment(lib,"SkinH.lib") #pragma comment(lib,"Winmm.lib") //消息结构 struct Msg { char NickName[12]; char szData[10240]; }; //全局变量 #define WM_SOCKET 0x401 HINSTANCE hInst; NewSocket client; char IP[16]={0},szTmp[255]={0}; int PORT; HWND hEditView,hEditSend; Msg sMsg,rMsg; ATOM atom; HMENU hSysMenu,hWav; NOTIFYICONDATA NID; RECT rect; bool upWav=false,downWav=false,msgWav=false; //连接窗口过程 int CALLBACK ConnDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam) { switch (uMsg) { case WM_CLOSE: { GetDlgItemText(hDlg,IDC_IP,IP,16); GetDlgItemText(hDlg,IDC_NICKNAME,sMsg.NickName,12); sMsg.NickName[strlen(sMsg.NickName)]=0x20; PORT=GetDlgItemInt(hDlg,IDC_PORT,NULL,false); EndDialog(hDlg,0); } break; } return 0; } //发送数据函数 bool SendData() { GETTEXTEX GT; GT.cb=10240; GT.flags=GT_USECRLF; GT.codepage=CP_ACP; GT.lpDefaultChar=false; GT.lpUsedDefChar=false; SendMessage(hEditSend,EM_GETTEXTEX,(LPARAM)&GT,(LPARAM)sMsg.szData); if (!client.Send((char *)&sMsg)) return false; ClearData(hEditSend); memset(sMsg.szData,0x00,10240); return true; } //主窗口过程 int CALLBACK DlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: { char hostname[255]={0}; hostent *HostEnt; //获取ip和主机名 gethostname(hostname,255); HostEnt=gethostbyname(hostname); sprintf(szTmp,"%s:%s",hostname,inet_ntoa(*(in_addr *)HostEnt->h_addr_list[0])); SetWindowText(hDlg,szTmp); //设置热键 char HotKey[]="HK_SEND"; if (GlobalFindAtom(HotKey)==0){ atom=GlobalAddAtom(HotKey); RegisterHotKey(hDlg,atom,MOD_CONTROL,VK_RETURN); }else{ MessageBox(0,"热键注册失败!","错误!",MB_OK|MB_ICONERROR); } //修改系统菜单 hSysMenu=GetSystemMenu(hDlg,FALSE); ModifyMenu(hSysMenu,0,MF_BYPOSITION,0x100,"连接"); ModifyMenu(hSysMenu,1,MF_BYPOSITION,0x101,"托盘"); AppendMenu(hSysMenu,MF_UNCHECKED,0x102,"置顶"); AppendMenu(hSysMenu,MF_SEPARATOR,0x103,"-"); AppendMenu(hSysMenu,MF_BYCOMMAND,0x104,"清空"); hWav=CreateMenu(); InsertMenu(hSysMenu,5,MF_POPUP,(int)hWav,"声音"); AppendMenu(hWav,MF_UNCHECKED,0x106,"消息"); AppendMenu(hWav,MF_UNCHECKED,0x107,"上线"); AppendMenu(hWav,MF_UNCHECKED,0x108,"下线"); AppendMenu(hSysMenu,MF_BYCOMMAND,SC_MOVE,"移动"); AppendMenu(hSysMenu,MF_BYCOMMAND,SC_CLOSE,"退出"); //加载图标 SendMessage(hDlg,WM_SETICON,(WPARAM)(BOOL)ICON_BIG,(LPARAM)LoadIcon(hInst,MAKEINTRESOURCE(IDC_MAIN))); //加载皮肤 SkinH_Attach(); SkinH_AdjustHSV(150,20,30); //初始化数据 memset(sMsg.szData,0x00,10240); memset(sMsg.NickName,0x20,12); memset(rMsg.szData,0x00,10240); memset(rMsg.NickName,0x20,12); hEditView=GetDlgItem(hDlg,IDC_TEXTVIEW); hEditSend=GetDlgItem(hDlg,IDC_TEXTSEND); } break; case WM_CLOSE: { if (MessageBox(hDlg,"确认退出?","提示",MB_OKCANCEL|MB_ICONQUESTION)==IDOK) { DestroyIcon(NID.hIcon); UnregisterHotKey(hDlg,atom); GlobalDeleteAtom(atom); EndDialog(hDlg,0); } } break; case WM_SYSCOMMAND: { switch(wParam) { case 0x100: { DialogBox(hInst,MAKEINTRESOURCE(IDD_CONNECT),hDlg,ConnDlgProc); if (!client.Connect(IP,PORT)){ MessageBox(hDlg,"连接失败!","Error",MB_OK|MB_ICONERROR); }else{ sprintf(szTmp,"服务器:%s:%d",IP,PORT); SetDlgItemText(hDlg,IDC_CONNINFO,szTmp); EnableMenuItem(hSysMenu,0x100,MF_GRAYED); EnableWindow(GetDlgItem(hDlg,IDB_SEND),true); WSAAsyncSelect(client.S,hDlg,WM_SOCKET,FD_READ); } } break; case 0x101: { memset(&NID,0x00,sizeof(NID)); NID.cbSize=sizeof(NID); NID.uFlags=NIF_ICON|NIF_MESSAGE|NIF_TIP; NID.hIcon=LoadIcon(hInst,MAKEINTRESOURCE(IDC_MAIN)); NID.hWnd=hDlg; strcpy(NID.szTip,"ChatRoom-Client"); NID.uCallbackMessage=0x402; NID.uID=IDC_MAIN; Shell_NotifyIcon(NIM_ADD,&NID); GetWindowRect(hDlg,&rect); SetWindowPos(hDlg,NULL,rect.left,rect.right,rect.right-rect.left,rect.bottom-rect.top,SWP_HIDEWINDOW); } break; case 0x102: { if (GetMenuState(hSysMenu,0x102,MF_BYCOMMAND)==MF_CHECKED) { CheckMenuItem(hSysMenu,0x102,MF_UNCHECKED); GetWindowRect(hDlg,&rect); SetWindowPos(hDlg,HWND_NOTOPMOST,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top,SWP_SHOWWINDOW); } else { CheckMenuItem(hSysMenu,0x102,MF_CHECKED); GetWindowRect(hDlg,&rect); SetWindowPos(hDlg,HWND_TOPMOST,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top,SWP_SHOWWINDOW); } } break; case 0x104: { SendMessage(hEditView,WM_SETTEXT,0,(LPARAM)""); } break; case 0x106: { if (GetMenuState(hWav,0x106,MF_BYCOMMAND)==MF_UNCHECKED){ CheckMenuItem(hSysMenu,0x106,MF_CHECKED); msgWav=true; }else{ CheckMenuItem(hSysMenu,0x106,MF_UNCHECKED); msgWav=false; } } break; case 0x107: { if (GetMenuState(hWav,0x107,MF_BYCOMMAND)==MF_UNCHECKED){ CheckMenuItem(hSysMenu,0x107,MF_CHECKED); upWav=true; }else{ CheckMenuItem(hSysMenu,0x107,MF_UNCHECKED); upWav=false; } } break; case 0x108: { if (GetMenuState(hWav,0x108,MF_BYCOMMAND)==MF_UNCHECKED){ CheckMenuItem(hSysMenu,0x108,MF_CHECKED); downWav=true; }else{ CheckMenuItem(hSysMenu,0x108,MF_UNCHECKED); downWav=false; } } break; } } break; case 0x402: { if (lParam==WM_LBUTTONUP) { Shell_NotifyIcon(NIM_DELETE,&NID); SetWindowPos(hDlg,HWND_NOTOPMOST,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top,SWP_SHOWWINDOW); } } break; case WM_COMMAND: { if (LOWORD(wParam)==IDB_SEND) SendData(); } break; case WM_HOTKEY: { SendData(); } break; case WM_SOCKET: { client.Recv((char *)&rMsg,sizeof(Msg)); //播放音乐 if (strncmp(rMsg.NickName,sMsg.NickName,11)!=0) { if (strncmp(rMsg.NickName,"服务器",6)==0) { if (strstr(rMsg.szData,"上线")!=NULL && upWav) sndPlaySound("Sound\\up.wav",SND_ASYNC); else if (strstr(rMsg.szData,"下线")!=NULL && downWav) sndPlaySound("Sound\\down.wav",SND_ASYNC); } else if (msgWav) sndPlaySound("Sound\\msg.wav",SND_ASYNC); } if (rMsg.NickName[0]) { time_t t; tm *T; time(&t); T=localtime(&t); rMsg.NickName[11]=0x00; sprintf(szTmp,"%s %02d:%02d:%02d\n",rMsg.NickName,T->tm_hour,T->tm_min,T->tm_sec); AppendSectionData(hEditView,szTmp,RGB(0x00,0xBB,0x00)); AppendStrData(hEditView,rMsg.szData); AppendStrData(hEditView,"\r\n\r\n"); memset(&rMsg,0x00,sizeof(Msg)); } } break; } return 0; } int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state ) { LoadLibrary("riched32.dll"); InitCommonControls(); hInst=hInstance; return DialogBox(hInstance,MAKEINTRESOURCE(IDD_CLIENT),NULL,DlgProc); }

 

服务端:

//////////////////////////
//server.cpp:果然还是服务端好写,这个思路就清晰多了
//////////////////////////
#include <NewSocket.h> #include <windows.h> #include <stdio.h> #include "resource.h" //全局变量 #define MAXSIZE 255 #define WM_SOCKET 0x401 NewSocket server,client[MAXSIZE+1]; int ClientNum=0; HANDLE hThread; HWND hMainDlg; char szRecv[10240]={0}; //上线消息 void NewMember() { char szTmp[255]={0}; sprintf(szTmp,"服务器 --------------------------------\r\n\t主机:%s 上线\r\n--------------------------------",client[ClientNum].SockInfo.IP); for (int i=0;i<ClientNum;i++) client[i].Send(szTmp); } //监听线程 DWORD WINAPI StartSvc(LPVOID lpParameter) { while(1) { server.Accept(client[ClientNum]); if (ClientNum>=MAXSIZE){ client[ClientNum].Send("服务器 --------------------------------\r\n\t服务器人数已满!\r\n--------------------------------"); closesocket(client[ClientNum].S); }else{ NewMember(); WSAAsyncSelect(client[ClientNum].S,hMainDlg,WM_SOCKET,FD_READ|FD_CLOSE); ClientNum++; client[ClientNum].S=0x00; } } } //关闭服务器 void CloseSvc() { for (int i=0;i<ClientNum;i++) client[i].Send("\r\n服务器 --------------------------------\r\n\t服务器即将关闭!\r\n--------------------------------"); } //主对话框过程 int CALLBACK DlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam) { switch (uMsg) { case WM_CLOSE: { EndDialog(hDlg,0); } break; case WM_INITDIALOG: { hMainDlg=hDlg; } break; case WM_COMMAND: { if (LOWORD(wParam)==IDB_START) { EnableWindow(GetDlgItem(hDlg,IDB_START),false); EnableWindow(GetDlgItem(hDlg,IDB_STOP),true); server.Bind(NULL,5566); server.Listen(5); hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)StartSvc,NULL,0,NULL); } else { EnableWindow(GetDlgItem(hDlg,IDB_STOP),false); EnableWindow(GetDlgItem(hDlg,IDB_START),true); CloseSvc(); TerminateThread(hThread,0); closesocket(server.S); ClientNum=0; } } break; case WM_SOCKET: { if (WSAGETSELECTERROR(lParam) || WSAGETSELECTEVENT(lParam)==FD_CLOSE) { char szTmp[255]={0}; for (int i=0;i<ClientNum;i++){ if (client[i].S==wParam) break; } sprintf(szTmp,"服务器 --------------------------------\r\n\t主机:%s 下线\r\n--------------------------------",client[i].SockInfo.IP); for (int j=i;j<ClientNum;j++) client[j]=client[j+1]; ClientNum--; for (i=0;i<ClientNum;i++) client[i].Send(szTmp); TerminateThread(hThread,0); hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)StartSvc,NULL,0,NULL); } if (WSAGETSELECTEVENT(lParam)==FD_READ) //接收到数据 { int ret=recv(wParam,szRecv,10240,0); if (ret>0) { for (int k=0;k<ClientNum;k++) client[k].Send(szRecv,ret); memset(szRecv,0,ret); } } } break; } return 0; } int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state ) { return DialogBox(hInstance,MAKEINTRESOURCE(IDD_SERVER),NULL,DlgProc); }

 

希望以后还能看懂这个客户端。。。>_<

功能说明:支持窗口置顶、托盘、消息声音及消息提醒、上下线声音及消息提醒、最大上线人数控制

截图纪念:

posted @ 2012-05-21 10:34  little evil  阅读(560)  评论(0编辑  收藏  举报