#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;
}
}
代码问题和软件问题欢迎交流