本系统基于客户端/服务器基本原理,程序即是服务端,也是客户端,通过IP就可以相互添加好友,并且实现点到点通信,有聊天记录(未实现根据用户发送的消息而改变字体颜色,只能在客户端改),字体颜色设置(一改全改~),包含主界面MaindBord和聊天Talk两大功能模块。
(1) MaindBord模块
A、登录模块
登陆模块的实现过程有下面几个步骤:
(1)初始化列表信息,获取用户名,服务器的IP地址,使按键失效 (2)确认登陆后创建套接字,绑定,监听 (3)初始化成功后,激活添加好友和隐藏功能 B、好友模块
添加好友的实现过程有下面几个步骤: (1)弹出对话框 (2)获取对话框中的IP
(3)检查对方是否在线,建立连接 (4)发送请求验证 C、好友列表模块
(1)客户接收请求,将服务端加入列表,发送客户端信息 (2)服务端得到客户端信息,加入列表 (3)双击好友列表,弹出发送信息对话框 D、隐藏模块 (1)隐藏与显示
(2)响应鼠标,弹出控制菜单
(2)聊天Talk模块
A、发送信息模块
聊天模块中,发送信息模块的实现过程有下面几个步骤: (1)通过列表双击一个好友为聊天对象,弹出聊天窗口; (2)用户按下发送按钮后,检测对方是否在线, 检测对方是否在线 (3)将聊天内容发送出去
(4)在本地即时聊天内容显示的编辑框中添加发送的内容并写入聊天记录。 B、 接收信息模块
聊天模块中,接收信息模块的实现过程有下面几个步骤:
(1)由主界面判断接收到信息的类型,是否为好友发来信息,如果是,则打开聊天窗口 (2)读取消息,并写入聊天记录, (3)载入字体颜色信息。 (4) 显示聊天记录 C、聊天记录管理模块
聊天记录管理模块的实现过程有下面几个步骤: (1)将发送内容和接收内容写入文本文件; (2)显示聊天记录; (3)删除聊天记录。 D、 字体颜色模块
实现字体颜色模块的过程有下面几个步骤: (1)调用系统字体,获取字体,写入存档 (2) 调用系统字体颜色,获取颜色,写入存档 (3)读取、更新字体颜色。
让我们先看下运行结果:
Windows7
Windows2003
Windows7 Windows2003
Windows7 Windows2003
Windows7
Windows 2003
心动了么?让我们开始对第一个模块,也就是MainBord的设计吧。 一、MainBord模块
新建一个MainBord工程
选择Dialog based,然后Finsh
二、界面设计如下:
(一)、MainBord界面设计如下:
将各ID值设置如下图,通过类向导添加listbox成员变量
(二)、添加好友对话框界面设计如下:
双击,添加类
将各ID值设置如下图,通过类向导添加成员变量
(三)、弹出菜单界面设计如下:
(四)添加图标
三、代码部分:
(一)准备部分 首先,在StdAfx.h中添加如下代码:afxsock.h 头文件;在MainBordDlg.cpp添加Message.h 、AddFreind.h;
其次,MainBordDlg.h添加如下代码: #define WM_CLIENT_ACCEPT WM_USER+101 #define WM_CLIENT_READCLOSE WM_USER+102 ///////////////////////////////////////////////////////////////////////////// // CMainBordDlg dialog // Construction public:
int TrayAddIcon();
char lpszTipBefore[100]; //修改前的图标提示信息 LRESULT OnAccept(WPARAM wParam,LPARAM lParam); LRESULT OnRead(WPARAM wParam,LPARAM lParam);
CMainBordDlg(CWnd* pParent = NULL); // standard constructor SOCKET Client; //客户的连接请求 SOCKET ServerSocket; //SOCKET
SOCKET tempSocket;
SOCKADDR_IN tempSocketAddr;
SOCKADDR_IN m_sockServerAddr; //SOCKET 结构 BOOL IsTrue; CString ip;
CString SystemMessage; void SendMsg(); CString IPName; CString HostName; void GetIP_Name();
void SelectWay(); //好友列表
{
struct FriendList char name[100]; char IP[18];
struct FriendList *next;
public: CString ip;
} *start,*last;
第三,添加Message.h文件 代码如下: struct Message {
int sign; char msg[1000]; }msg;
第四,添加消息响应
LRESULT CMainBordDlg::OnRead(WPARAM wParam, LPARAM lParam) { //
if(!IsTrue) { //m_List.InsertString(0,\有数据到达,但是没有工作站连接。\}
CString str;
switch (WSAGETSELECTEVENT(lParam)) {
case FD_READ:
if(recv(Client,(char *)&msg,sizeof(msg),0) == SOCKET_ERROR) { }
SelectWay(); //m_History_List.InsertString(0,str);
m_List.InsertString(0,\接收数据发生错误。\return 0;
}
//MessageBox(str);
break; case FD_CLOSE: str = _T(\工作站退出。\ }
//m_List.InsertString(0,str); closesocket(Client); //IsTrue = FALSE; break;
return 0L;
LRESULT CMainBordDlg::OnAccept(WPARAM wParam, LPARAM lParam) { }
(一)、登录模块
登陆模块的实现过程有下面几个步骤:
if (WSAGETSELECTERROR(lParam))
{ }
//m_List.InsertString(0,\return 0L;
if(WSAGETSELECTEVENT(lParam) == FD_ACCEPT)//如果
{
Client = accept(ServerSocket,(LPSOCKADDR)&m_sockServerAddr,0);
if (Client == INVALID_SOCKET)
{
//m_List.InsertString(0,\
return 0L; }
//Clinet
WSAAsyncSelect(Client,m_hWnd,WM_CLIENT_READCLOSE,FD_READ|FD_CLOSE);
IsTrue = TRUE;
}
//m_List.InsertString(0,\有客户机连接上了服务器。\
return 0L;
(1)初始化列表,获取用户名,服务器的IP地址,使按键失效。 在MainBordDlg.cpp的初始化函数OnInitDialog添加如下代码:
m_FriendList.SetItemHeight( 0, 18 ); start=NULL; last=NULL;
// TODO: Add extra initialization here GetIP_Name(); strcpy(lpszTipBefore,\
(CEdit *)GetDlgItem(IDC_AddBTN)->EnableWindow(FALSE); (CEdit *)GetDlgItem(IDC_HIDEBTN)->EnableWindow(FALSE);
(2)在MainBordDlg.cpp添加获取主机获得IP和主机名的函数 void CMainBordDlg::GetIP_Name()//获得IP和主机名 { }
(3) 确定登陆后创建套接字,绑定,监听, 初始化成功后,激活添加好友和隐藏功能
WORD wVersionRequested; WSADATA wsaData; char hostname[128]; CString strip; PHOSTENT hostinfo;
wVersionRequested=MAKEWORD(2,0);
if(WSAStartup(wVersionRequested,&wsaData)==0) {
if(gethostname(hostname,sizeof(hostname))==0) { }
if((hostinfo=gethostbyname(hostname))!=NULL) { strip=inet_ntoa(*(struct in_addr *)*hostinfo->h_addr_list); }
WSACleanup();
}
IPName=strip;
HostName.Format(\
在MainBordDlg.cpp添加登陆按钮的方法
void CMainBordDlg::OnStartBTN() {
// TODO: Add your control notification handler code here
WSADATA wsaData; int iErrorCode;
if (WSAStartup(MAKEWORD(2,1),&wsaData)) //调用Windows Sockets DLL
{
WSACleanup(); return; } //m_List.InsertString(0,\服务器开始创建SOCKET。\ ServerSocket=socket(PF_INET,SOCK_STREAM,0); //创建服务器端Socket,类型为SOCK_STREAM,面向连接的通信
if(ServerSocket == INVALID_SOCKET)
{
//m_List.InsertString(0,\无法创建服务器socket!\ return;
}
m_sockServerAddr.sin_family = AF_INET;
m_sockServerAddr.sin_addr.s_addr = INADDR_ANY; //向所有的IP地址发送消息 m_sockServerAddr.sin_port = htons(8001);
if (bind(ServerSocket,(LPSOCKADDR)&m_sockServerAddr,sizeof(m_sockServerAddr)) == SOCKET_ERROR) //与选定的端口绑定
{ //m_List.InsertString(0,\无法绑定服务器。\ return;
}
iErrorCode=WSAAsyncSelect(ServerSocket,m_hWnd,WM_CLIENT_ACCEPT,FD_ACCEPT); // 产生相应传递给窗口的消息为WM_SERVER_ACCEPT ,这是自定义消息 if (iErrorCode == SOCKET_ERROR) {
return;
}
if (listen(ServerSocket,1) == SOCKET_ERROR) //开始监听客户连接请求
{
//m_List.InsertString(0,\服务器监听失败!\
return;
}
IsTrue = TRUE;
//m_List.AddString(\服务器绑定监听成功。\
(CEdit *)GetDlgItem(IDC_AddBTN)->EnableWindow(TRUE); (CEdit *)GetDlgItem(IDC_HIDEBTN)->EnableWindow(TRUE);
return; }
(二)好友模块
添加好友的实现过程有下面几个步骤:(1)弹出对话框 (2)获取对话框中的IP(3)检查对方是否在线,建立连接(4)发送请求验证
1在AddFreind.h添加一个成员 // Construction public: CString ipstr;
2好友请求对话框添加代码,在AddFreind.cpp增加发送请求(确定)按钮代码: void AddFreind::OnOK() { }
// TODO: Add extra validation here UpdateData(TRUE);
m_IPAddress.GetWindowText(ipstr); CDialog::OnOK();
3在MainBordDlg.cpp添加好友按钮的方法
void CMainBordDlg::OnAddBTN() { // TODO: Add your control notification handler code here
AddFreind mydlg;
if(mydlg.DoModal() == IDOK) { }
if(mydlg.ipstr.GetLength()==0) { MessageBox(\您没有输入IP地址!!!\错误信息\ }
return;
ip=mydlg.ipstr;
else return;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,1),&wsaData)) //调用Windows Sockets DLL {
WSACleanup();
return; } tempSocket=socket(PF_INET,SOCK_STREAM,0); //创建服务器端Socket,类型为SOCK_STREAM,面向连接的通信 if(tempSocket == INVALID_SOCKET) {
return;
}
DWORD dwip;
if((dwip=inet_addr(ip))==INADDR_NONE) {
MessageBox(\赋值错误\错误信息\
return; }
tempSocketAddr.sin_family=AF_INET; //使用TCP/IP协议 tempSocketAddr.sin_port = htons(8001);
tempSocketAddr.sin_addr.S_un.S_addr = dwip;
if(connect(tempSocket,(LPSOCKADDR)&tempSocketAddr,sizeof(tempSocketAddr)) ==
SOCKET_ERROR) { MessageBox(\对方不在线\添加失败\ return; }
} else
{ msg.sign=2; SystemMessage=IPName+\ }
if(send(tempSocket,(char *)&msg,sizeof(msg),0)==SOCKET_ERROR) { MessageBox(\发送消息失败\错误信息\ }
return;
strcpy(msg.msg,SystemMessage);
3好友列表模块与消息响应
(1)客户接收请求,将服务端加入列表,发送客户端信息 ;(2)服务端得到客户端信息,加入列表;
void CMainBordDlg::SelectWay() { // //
FriendList *newlist=new struct FriendList; CString strIP,strName,temp;//添加新成员 CString message; int i,j=0; i=msg.sign;
message.Format(\MessageBox(\MessageBox(message); CFile myfile; switch(i)
//
{
case 1: //别人跟你聊天
myfile.Open(\ myfile.Write(message, message.GetLength()); myfile.Close();
//(HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName)
WinExec(\case 2:message.Format(\
MessageBox(message); strIP=\ strName=\
j=0;
while(msg.msg[j]!='#') {
strIP+=msg.msg[j];
j++; } j++;
while(msg.msg[j]!='#') {
strName+=msg.msg[j]; j++; }
if((MessageBox(strName+\请求你通过身份验证\请求信息\
{ //MessageBox(\
return; } for(j=0;j { } m_FriendList.GetText(j,temp); if(strcmp(strName,temp)==0) {MessageBox(\你已经添加了这个成员\错误信息\return;} strcpy(newlist->IP,strIP); strcpy(newlist->name,strName); newlist->next=NULL; if(start==NULL){start=newlist;last=newlist;} else{ } last->next=newlist; last=newlist; } m_FriendList.AddString(strName); ip=strIP;//同意加为好友 SystemMessage=IPName+\msg.sign=3; SendMsg(); break; message.Format(\//MessageBox(message); strIP=\strName=\j=0; while(msg.msg[j]!='#') { strIP+=msg.msg[j]; j++; case 3://MessageBox(message); } j++; while(msg.msg[j]!='#') { strName+=msg.msg[j]; j++; } for(j=0;j m_FriendList.GetText(j,temp); if(strcmp(strName,temp)==0) {MessageBox(\你已经添加了这个成员\错误信息\return;} strcpy(newlist->IP,strIP); strcpy(newlist->name,strName); newlist->next=NULL; if(start==NULL){start=newlist;last=newlist;} else{ last->next=newlist; last=newlist; } m_FriendList.AddString(strName); MessageBox(strName+\通过你的身份验证!!!\验证信息\UpdateData(TRUE);break; default:break; } (2) 消息响应: void CMainBordDlg::SendMsg()//接受或发送系统请求信息 { WSADATA wsaData; if (WSAStartup(MAKEWORD(2,1),&wsaData)) //调用Windows Sockets DLL { WSACleanup(); return; } tempSocket=socket(PF_INET,SOCK_STREAM,0); //创建服务器端Socket,类型为SOCK_STREAM,面向连接的通信 if(tempSocket == INVALID_SOCKET) { return; } DWORD dwip; if((dwip=inet_addr(ip))==INADDR_NONE) { MessageBox(\赋值错误\错误信息\ return; } tempSocketAddr.sin_family=AF_INET; //使用TCP/IP协议 tempSocketAddr.sin_port = htons(8001); tempSocketAddr.sin_addr.S_un.S_addr = dwip; if(connect(tempSocket,(LPSOCKADDR)&tempSocketAddr,sizeof(tempSocketAddr)) SOCKET_ERROR) { MessageBox(\对方不在线\失败信息\ return; } } (3)双击好友列表,弹出发送信息对话框。 void CMainBordDlg::OnDblclkFriendLIST() { // TODO: Add your control notification handler code here { } strcpy(msg.msg,SystemMessage); if(send(tempSocket,(char *)&msg,sizeof(msg),0)==SOCKET_ERROR) MessageBox(\发送消息失败\错误信息\return; == if(IsTrue!=TRUE)return;//未登录 int index=m_FriendList.GetCurSel(); if(index!=LB_ERR) { } CString strname; m_FriendList.GetText(index,strname); FriendList *s; s=start; while(s!=NULL&&strcmp(s->name,strname)!=0) { } if(s==NULL){MessageBox(\没找到!\CString ipstr; ipstr.Format(\CString a; a=\CFile myfile; myfile.Open(\myfile.Write( a, a.GetLength()); myfile.Close(); //MessageBox(a); WinExec(\ s=s->next; this->UpdateData(false); m_ServerIPADDRESS.SetWindowText(IPstr); GetIP_Name(); 添加GetIP_Name()函数 void CTalkDlg::GetIP_Name() { } 3. 聊天记录管理模块 聊天记录管理模块的实现过程有下面几个步骤: (1)将发送内容和接收内容写入文本文件,发送消息时已实现 (2)显示聊天记录,读取消息时已实现 (3)删除聊天记录。 void CTalkDlg::OnClean() { } 4. 字体颜色模块 DeleteFile(\m_Show=\ this->UpdateData(false); WORD wVersionRequested; WSADATA wsaData; char hostname[128]; CString strip; PHOSTENT hostinfo; wVersionRequested=MAKEWORD(2,0); if(WSAStartup(wVersionRequested,&wsaData)==0) { if(gethostname(hostname,sizeof(hostname))==0) { } if((hostinfo=gethostbyname(hostname))!=NULL) { strip=inet_ntoa(*(struct in_addr *)*hostinfo->h_addr_list); } WSACleanup(); } IPName=strip; HostName.Format(\ 字体颜色模块的实现过程有下面几个步骤: (1)调用系统字体,获取字体,写入存档 void CTalkDlg::OnFont() { Font=new CFont; // TODO: Add your control notification handler code here CFontDialog fontDlg; if (fontDlg.DoModal()==IDOK) { fontDlg.GetCurrentFont(&m_logft); CFile Fontfile; Fontfile.Open(\ Fontfile.Write(&m_logft,sizeof(m_logft)); Fontfile.Close(); } //Font->GetLogFont(&lf); //lf.lfUnderline=TRUE; Font->DeleteObject(); Font->CreateFontIndirect(&m_logft); ((CEdit*)GetDlgItem(IDC_TALK))->SetFont(Font,true); } (2) 调用系统字体颜色,获取颜色,写入存档 void CTalkDlg::OnColor() { // TODO: Add your control notification handler code here CColorDialog colorDlg; if (colorDlg.DoModal()==IDOK) { m_color=colorDlg.GetColor(); CFile colorfile; colorfile.Open(\ colorfile.Write(&m_color,sizeof(m_color)); } } colorfile.Close(); (2)读取更新字体颜色,部分代码在读取消息时已实现 (1)为对话框类添加WM_CTLCOLOR消息函数OnCtlColor(); (2)在OnCtlColor()函数中添加代码后如下: HBRUSH CTalkDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { } HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: Change any attributes of the DC here // if(nCtlColor==CTLCOLOR_EDIT) 可以实现发送框颜色变化~ // { //pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(m_color); //文字颜色 // pDC->SetBkColor(RGB(233,233,220)); //背景颜色 // HBRUSH b=CreateSolidBrush(RGB(233,233,220));//背景颜色 // } // TODO: Return a different brush if the default is not desired return hbr;