#include "stdafx.h" #include <windows.h> #include <windowsx.h> #include <winsock2.h> #include "resource.h" #include "MainDlg.h" #include "chart.h" #include "Commctrl.h" #include "mmsystem.h" #include "shellapi.h" #pragma comment(lib, "wsock32.lib") #define HIGHTIME 21968699 // 21968708 // Jan 1, 1900 FILETIME.highTime #define LOWTIME 4259332096 // 1604626432 // Jan 1, 1900 FILETIME.lowtime #define WM_MYMESSAGE WM_USER+100 //自定义消息 最小化 typedef struct IpInfo { SOCKET sock; HWND hwnd; }IpInfo; typedef struct ClSocket //用户列表 { SOCKET Sock; HANDLE handle; ClSocket* next; }ClSocket; typedef struct SendAllInfo { ClSocket *SockList; HWND hwnd; TCHAR Msg[255]; }SendAllInfo; //含结构体不好在.h文件中声明的函数区 void AddUserSock(ClSocket* UserList,SOCKET addsock,HANDLE addhandle); void DelUserSock(ClSocket* UserList,SOCKET delsock); SOCKET c_sock; //本地聊天用套接字 SOCKADDR_IN AdIp; SOCKADDR_IN sa; WSADATA wsaData; SOCKET sock; TCHAR HostName[50]; TCHAR PORT[8]; struct hostent* Mhost = NULL; static HANDLE handle; //接受消息的线程 static HANDLE Rec_handle; //接受用户发送消息 static HANDLE Send_handle; //发送消息给全部用户 static IpInfo m_ipinfo; //接收信息时创建线程时传输信息 static IpInfo rec_ipinfo; //接受客户端信息 static SendAllInfo send_Msg_all; //把消息送到所以用户 ClSocket *UserList; //登录的用户的sock和线程信息 bool IsStartSer = false; //判断服务器是否开启 bool IsChartExt = false; //判断聊天窗口是否弹出 HWND Charthwnd; //聊天窗口的句柄 HWND Listhwnd; //用户登录信息列表 SYSTEMTIME Time; //本地系统时间 BOOL WINAPI Main_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { HANDLE_MSG(hWnd, WM_INITDIALOG, Main_OnInitDialog); HANDLE_MSG(hWnd, WM_COMMAND, Main_OnCommand); HANDLE_MSG(hWnd,WM_CLOSE, Main_OnClose); case WM_MYMESSAGE: { if(lParam == WM_LBUTTONDOWN) {//在托盘图标上左击 ShowWindow(hWnd,SW_SHOW); } else if(lParam == WM_RBUTTONDOWN) {//在托盘图标上右击 MessageBox(NULL,TEXT("托盘来消息了,右键"),TEXT("消息"),MB_OK); } } break; } return FALSE; } BOOL Main_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { //设置图标的 HICON hIcon = LoadIcon((HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE) ,MAKEINTRESOURCE(IDI_ICON)); SendMessage(hwnd, WM_SETICON, TRUE, (LPARAM)hIcon); SendMessage(hwnd, WM_SETICON, FALSE, (LPARAM)hIcon); SetWindowText(hwnd,TEXT("昆河 聊天服务器1.0")); //初始化Socket库 WSAStartup(MAKEWORD(2,0),&wsaData); gethostname(HostName,sizeof(HostName)/sizeof(TCHAR)); SetDlgItemText(hwnd,IDC_SERVERNAME,HostName); Mhost = gethostbyname(HostName); SetDlgItemText(hwnd,IDC_IPADDRESSIP,inet_ntoa(*(struct in_addr*)(Mhost->h_addr))); SetDlgItemText(hwnd,IDC_EDITPORT,TEXT("5566")); InitLVColumn(hwnd); UserList = (ClSocket*)malloc(sizeof(ClSocket)); UserList->next =NULL; send_Msg_all.SockList = UserList; send_Msg_all.hwnd = hwnd; Charthwnd = GetDlgItem(hwnd,IDC_RICHEDITSCREEN); Listhwnd = GetDlgItem(hwnd,IDC_LISTUSER); Minimized(hwnd,0); SetDlgItemText(hwnd,IDC_RICHEDITSCREEN,TEXT("/t**************服务器初始化成功**************")); //EnableMenuItem(GetMenu(hwnd),IDC_BUTTONCHART,MF_DISABLED);//使停止菜单变灰 return TRUE; } void Main_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch(id) { case IDC_OK: { if(!IsStartSer) { if(InitSocket(hwnd)) { m_ipinfo.sock = sock; m_ipinfo.hwnd = hwnd; handle = CreateThread(NULL,0,AgreeNewUser,&m_ipinfo,0,NULL); CloseHandle(handle);//关闭线程。 但实际不是停止进程 CreatSocket(hwnd); UserList->handle = handle; UserList->Sock = sock; IsStartSer = true; break; } } else { MessageBox(NULL,TEXT("服务器已经开启"),TEXT("ERROR"),MB_OK|MB_ICONHAND); } } break; case IDC_BUTTONCHARTALL: { if(IsStartSer) { if(IsChartExt) { ShowWindow(Charthwnd,SW_HIDE); ShowWindow(Listhwnd,SW_SHOW); SetDlgItemText(hwnd,IDC_BUTTONCHARTALL,TEXT("广播消息")); IsChartExt = false; } else { ShowWindow(Charthwnd,SW_SHOW); ShowWindow(Listhwnd,SW_HIDE); SetDlgItemText(hwnd,IDC_BUTTONCHARTALL,TEXT("登录信息")); IsChartExt = true; } } } break; case IDC_POWEROFF: { int IsClose = MessageBox(hwnd,TEXT("真的关闭服务器?"),TEXT("提醒"),MB_YESNO|MB_ICONWARNING|MB_DEFBUTTON2); if(IDYES==IsClose) { Minimized(hwnd,1); EndDialog(hwnd, 0); } } break; case IDC_SEND: { if(!IsStartSer) { MessageBox(hwnd,TEXT("服务器没有开启!!!"),TEXT("ERROR"),MB_OK|MB_ICONHAND); break; } TCHAR Msg[255]; ZeroMemory(Msg,sizeof(Msg)/sizeof(TCHAR)); GetDlgItemText(hwnd,IDC_RICHEDITIN,Msg,sizeof(Msg)/sizeof(TCHAR)); int length=lstrlen(Msg); if(0==length) { MessageBox(NULL,TEXT("不能发送空信息"),TEXT("ERROR"),MB_OK|MB_ICONHAND); break; } else if(length > 200) { MessageBox(hwnd,TEXT("消息太长/t请删除一部分再发送"),TEXT("提醒"),MB_OK|MB_ICONWARNING); break; } TCHAR SendMsg[255]; ZeroMemory(SendMsg,sizeof(SendMsg)/sizeof(TCHAR)); ZeroMemory(send_Msg_all.Msg,sizeof(send_Msg_all.Msg)/sizeof(TCHAR)); //不能放在前面哦!! lstrcpy(SendMsg,TEXT("服务器管理员:/n ")); lstrcat(SendMsg,Msg); lstrcpy(send_Msg_all.Msg,SendMsg); Send_handle = CreateThread(NULL,0,PubMessagetoAll,&send_Msg_all,0,NULL); CloseHandle(Send_handle);//关闭线程。 但实际不是停止进程 //FreshScream(hwnd,SendMsg); SetDlgItemText(hwnd,IDC_RICHEDITIN,TEXT("")); } break; case IDC_CLEARN: { SetDlgItemText(hwnd,IDC_RICHEDITIN,TEXT("")); } break; default: break; } } void Main_OnClose(HWND hwnd) { int IsMin = MessageBox(hwnd,TEXT("最小化到托盘?"),TEXT("提醒"),MB_YESNO|MB_ICONQUESTION); if(IDYES==IsMin) { ShowWindow(hwnd,SW_HIDE);//隐藏窗口 return; } Minimized(hwnd,1); EndDialog(hwnd, 0); } 调试用报错 void ShowError() { TCHAR* lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER| //自动分配消息缓冲区 FORMAT_MESSAGE_FROM_SYSTEM, //从系统获取信息 NULL,GetLastError(), //获取错误信息标识 MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),//使用系统缺省语言 (LPTSTR)&lpMsgBuf, //消息缓冲区 0, NULL); MessageBox(NULL,lpMsgBuf,"",MB_ICONERROR); } ///初始化导线 bool InitSocket(HWND hwnd) { int count = 0; //标记port冲突的次数,假设超过100次,默认为非端口原因造成无法绑定!!! //创建一根电线 sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(INVALID_SOCKET == sock) { MessageBox(hwnd,TEXT("创建套接字失败!"),TEXT("失败"),MB_OK); WSACleanup(); return false; } sa.sin_family=AF_INET; sa.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //sa.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //设置电线连接服务器端的端口 //sa.sin_port = htons(IPPORT_TELNET); GetDlgItemText(hwnd,IDC_EDITPORT,PORT,sizeof(PORT)/sizeof(TCHAR)); sa.sin_port = atoi(PORT); while(SOCKET_ERROR == bind(sock,(SOCKADDR*)&sa,sizeof(sa))) { sa.sin_port++; if(++count>100) { break; } } if(count>100) { MessageBox(hwnd,TEXT("绑定套接字失败,无法启动网络/n检查网络在后再登陆!"), TEXT("网络失败"),MB_OK | MB_ICONSTOP); closesocket(sock); return false; } TCHAR buff_port[6]; itoa(sa.sin_port,buff_port,10); SetDlgItemText(hwnd,IDC_EDITPORT,buff_port); return true; //为什么不用设置客户端的端口,难道不需要客户端的端口吗? } ///回应用户登录到服务器 DWORD WINAPI AgreeNewUser(LPVOID lp) { SOCKET server = ((IpInfo*)lp)->sock; HWND hwnd = ((IpInfo*)lp)->hwnd; rec_ipinfo.hwnd = hwnd; //接受信息句柄 if(SOCKET_ERROR == listen(sock,5)) { ShowError(); MessageBox(hwnd,TEXT("监听失败!"),TEXT("ERROR"),MB_OK|MB_ICONHAND); return 0; } SOCKET t; SOCKADDR_IN ta; int ta_len; ta_len=sizeof(ta); TCHAR RecMsg[255]; while(1) { t=accept(server,(SOCKADDR*)&ta,&ta_len); if(t==INVALID_SOCKET) { MessageBox(hwnd,TEXT("不能确立连接"),TEXT("ERROR"),MB_OK|MB_ICONHAND); continue; } ZeroMemory(RecMsg,sizeof(RecMsg)/sizeof(TCHAR)); recv(t,RecMsg,sizeof(RecMsg)/sizeof(TCHAR),0); TCHAR* Load_IP; Load_IP = inet_ntoa(ta.sin_addr); InsertRecord(hwnd,ta.sin_port,Load_IP,RecMsg); rec_ipinfo.sock = t; Rec_handle = CreateThread(NULL,0,RecvMessage,&rec_ipinfo,0,NULL); CloseHandle(Rec_handle);//关闭线程。 但实际不是停止进程 AddUserSock(UserList,t,Rec_handle); } return 1; } //接受用户发送到服务器的信息并发送给所以用户 DWORD WINAPI RecvMessage(LPVOID lp) { SOCKET client = ((IpInfo*)lp)->sock; HWND hwnd = ((IpInfo*)lp)->hwnd; //HANDLE Rec_handle; TCHAR Msg[50] = "/t/t*********欢迎登录服务器!***********"; if(SOCKET_ERROR == send(client,Msg,sizeof(Msg)/sizeof(TCHAR),0)) { MessageBox(hwnd,TEXT("不能发送消息"),TEXT("ERROR"),MB_OK|MB_ICONHAND); } TCHAR RecMsg[256]; while(1) { ZeroMemory(RecMsg,sizeof(RecMsg)/sizeof(TCHAR)); if(SOCKET_ERROR==recv(client,RecMsg,sizeof(RecMsg)/sizeof(TCHAR),0)) { //ListView_FindItem(hwnd,-1,2); DelUserSock(UserList,client); return 0; } /把消息发送到每一个用户 ZeroMemory(send_Msg_all.Msg,sizeof(send_Msg_all.Msg)/sizeof(TCHAR)); //不能放在前面哦!! lstrcpy(send_Msg_all.Msg,RecMsg); Send_handle = CreateThread(NULL,0,PubMessagetoAll,&send_Msg_all,0,NULL); CloseHandle(Send_handle);//关闭线程。 但实际不是停止进程 } } /把信息像所有sock用户表的人发送 DWORD WINAPI PubMessagetoAll(LPVOID lp) { ClSocket* ClientList = ((SendAllInfo*)lp)->SockList; HWND hwnd = ((SendAllInfo*)lp)->hwnd; ((SendAllInfo*)lp)->Msg[200] = '/0'; GetLocalTime(&Time); TCHAR MsgAll[255]; ZeroMemory(MsgAll,sizeof(MsgAll)/sizeof(TCHAR)); wsprintf(MsgAll," <%d:%d:%d> %s",Time.wHour,Time.wMinute,Time.wSecond,((SendAllInfo*)lp)->Msg); while(ClientList->next!=NULL) { ClientList = ClientList->next; send(ClientList->Sock,MsgAll,255,0); } FreshScream(hwnd,MsgAll); return 1; } ///初始化LISTVIEW控件 void InitLVColumn(HWND hwnd) { LVCOLUMN lvColumn; //LVITEM lvItem; MyLVColumn MyColumn[3] = {{TEXT("姓名"),0x80,LVCFMT_CENTER}, {TEXT("端口"),0x60,LVCFMT_CENTER}, {TEXT("IP地址"),0x98,LVCFMT_CENTER}}; lvColumn.mask = LVCF_TEXT|LVCF_FMT|LVCF_WIDTH|LVCF_SUBITEM; for(int i = 0;i < 3;i++) { lvColumn.pszText = MyColumn[i].szColumnName; lvColumn.fmt = MyColumn[i].fmt; lvColumn.cx = MyColumn[i].cx; SendDlgItemMessage(hwnd,IDC_LISTUSER,LVM_INSERTCOLUMN,i,(LPARAM)&lvColumn); } } ///把用户的登录信息插入到listview void InsertRecord(HWND hwnd,int chPort,TCHAR *chIp,TCHAR *chName ) {//插入记录 LVITEM lvItem; ZeroMemory(&lvItem,sizeof(lvItem)); //初始化0 int ItemCount = ListView_GetItemCount(GetDlgItem(hwnd,IDC_LISTUSER)); //得到数据总条数 lvItem.mask = LVIF_TEXT; lvItem.iItem = ItemCount; lvItem.iSubItem = 0; lvItem.pszText = chName; //输出 姓名 SendDlgItemMessage(hwnd,IDC_LISTUSER,LVM_INSERTITEM,0,(LPARAM)&lvItem); lvItem.iSubItem = 1; lvItem.mask = LVIF_TEXT; TCHAR buff[8]; ZeroMemory(buff,sizeof(buff)/sizeof(TCHAR)); //初始化0 itoa(chPort,buff,10); lvItem.pszText = buff; //输出 端口 SendDlgItemMessage(hwnd,IDC_LISTUSER,LVM_SETITEM,0,(LPARAM)&lvItem); lvItem.iSubItem = 2; lvItem.pszText = chIp; lvItem.mask = LVIF_TEXT; //输出 IP SendDlgItemMessage(hwnd,IDC_LISTUSER,LVM_SETITEM,0,(LPARAM)&lvItem); PostMessage(GetDlgItem(hwnd,IDC_LISTUSER),WM_VSCROLL, SB_BOTTOM,0); //屏幕使起始终在最后一行 } //创建一个socket 接受用户通过UDP发送到本端口的信息 bool CreatSocket(HWND hwnd) { //创建一根电线 c_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(INVALID_SOCKET == c_sock) { MessageBox(hwnd,TEXT("创建套接字失败!"),TEXT("失败"),MB_OK); WSACleanup(); return false; } AdIp.sin_family=AF_INET; AdIp.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //AdIp.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //设置电线连接服务器端的端口 AdIp.sin_port = sa.sin_port; if(SOCKET_ERROR == bind(c_sock,(SOCKADDR*)&AdIp,sizeof(AdIp))) { MessageBox(hwnd,TEXT("绑定套接字失败,无法启动网络/n检查网络在后再登陆!"), TEXT("网络失败"),MB_OK | MB_ICONSTOP); closesocket(c_sock); WSACleanup(); return false; } return true; } /向用户列表中加用户数据 void AddUserSock(ClSocket* UserList,SOCKET addsock,HANDLE addhandle) { ClSocket* addpoint; addpoint = (ClSocket*)malloc(sizeof(ClSocket)); addpoint->handle = addhandle; addpoint->Sock = addsock; addpoint->next = NULL; ClSocket* ptr; ptr = UserList; while(ptr->next != NULL) { ptr = ptr->next; } ptr->next = addpoint; } //从sock表删除离线用户 void DelUserSock(ClSocket* UserList,SOCKET delsock) { ClSocket* ptr,*pfro; pfro = ptr = UserList; while(ptr->next != NULL) { ptr = ptr->next; if(ptr->Sock==delsock) { pfro->next=ptr->next; free(ptr); return; } pfro = pfro->next; } if(pfro->next==NULL) { //MessageBox(NULL,"123","123",MB_OK); } } /刷屏 void FreshScream(HWND hwnd,TCHAR *Msg) { TCHAR buff[0x1002]; ZeroMemory(buff,sizeof(buff)/sizeof(TCHAR)); GetDlgItemText(hwnd,IDC_RICHEDITSCREEN,buff,sizeof(buff)/sizeof(TCHAR)); if(lstrlen(buff)>0x1002-256) { TCHAR swapbuff[0x1002]; lstrcpy(swapbuff,buff+1000); ZeroMemory(buff,sizeof(buff)/sizeof(TCHAR)); lstrcpy(buff,swapbuff); } lstrcat((char*)buff,"/n"); lstrcat((char*)buff,(char*)Msg); SetDlgItemText(hwnd,IDC_RICHEDITSCREEN,buff); PostMessage(GetDlgItem(hwnd,IDC_RICHEDITSCREEN),WM_VSCROLL, SB_BOTTOM,0); //屏幕使起始终在最后一行 } ///最小化到托盘 void Minimized(HWND hwnd,int flag) { NOTIFYICONDATA nid; nid.cbSize = (DWORD)sizeof(NOTIFYICONDATA); nid.hWnd = hwnd; nid.uID = IDI_ICON; nid.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; nid.uCallbackMessage = WM_MYMESSAGE;//自定义消息 HINSTANCE hin = (HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE);//获得程序句柄 nid.hIcon = LoadIcon(hin,MAKEINTRESOURCE(IDI_ICON)); lstrcpy(nid.szTip,TEXT("HK 聊天服务器")); switch(flag) { case 0: {//添加托盘图标 Shell_NotifyIcon(NIM_ADD,&nid); //ShowWindow(hwnd,SW_HIDE);//隐藏窗口 } break; case 1: {//删除托盘图标 Shell_NotifyIcon(NIM_DELETE,&nid); } break; default: break; } } 代码问题和软件问题欢迎交流