基于UDP协议的网上聊天程序.docx
《基于UDP协议的网上聊天程序.docx》由会员分享,可在线阅读,更多相关《基于UDP协议的网上聊天程序.docx(16页珍藏版)》请在冰豆网上搜索。
基于UDP协议的网上聊天程序
湖南工业大学
课程设计
资料袋
计算机与通信学院学院(系、部)2011~2012学年第1学期
课程名称计算机网络原理指导教师李建设职称副教授
学生姓名蒋政专业班级软件092学号09408300209
题目基于UDP协议网上聊天程序
成绩起止日期2011年11月24日~2011年12月4日
目录清单
序号
材料名称
资料数量
备注
1
课程设计任务书
2
课程设计说明书
3
4
5
6
课程设计任务书
2010—2011学年第2学期
学院(系、部)计算机与通信学院专业软件工程班级091-3
课程名称:
计算机网络原理学生姓名:
蒋政
设计题目:
基于UDP协议网上聊天程序指导教师:
李建设
完成期限:
自2011年11月24日至2011年12月4日共2周
内
容
及
任
务
一、设计内容
实现一简单的聊天程序实现网上聊天,包括服务器和客户端。
1.支持多人聊天。
2.客户端具有图形化用户界面。
二、设计任务
课程设计说明书(纸质+电子版),内容包括:
设计内容、系统分析(包括可行性分析、需求分析等)及功能分析;系统设计(要求画出系统整体功能框图、流程图、并给出相应地关键的代码且对所使用的主要数据结构进行说明等。
)、设计总结(评价/遇到的问题/体会/建议等)、使用说明等。
三、设计要求
1.按功能要求开发系统,能正确运行。
程序代码书写规范,有充足的注释。
2.课程设计所使用的编程语言任选,但建议使用C或C++;
3.绿色软件:
程序运行不需安装,避免写系统和注册表;
进
度
安
排
起止日期
工作内容
2011-11-24~2011-11-27
选题
2011-11-28~2011-11-29
系统分析和设计;
2011-11-30~2011-12-3
熟悉软件开发工具、编码系统、系统测试
2011-12-04~2011-12-05
撰写并提交课程设计说明书(含电子文档)、源程序等。
主
要
参
考
资
料
[1](美)LarryPeterson著,薛静锋等译.《计算机网络:
系统方法》(第4版),机械工业出版社,2009
[2](荷)AndrewS.Tanenbaum著,潘爱民译.《计算机网络》(第4版),清华大学出版社,2004
[3]谢希仁著.《计算机网络》((第5版),电子工业出版社,2008.1
[4]吴功宜等著.《计算机网络课程设计》,机械工业出版社,2005
指导教师(签字):
年月日
系(教研室)主任(签字):
年月日
课程设计说明书
课程名称:
计算机网络原理
设计题目:
基于UDP协议的聊天程序
专业:
软件工程班级:
092
学生姓名:
蒋政学号:
09408300209
指导教师:
李建设
年月日
目录
1.课程设计具体实现过程………………………..4
2.具体代码的实现…………………………………8
3.注意事项………………………………………….12
4.参考文献………………………………………….12
一.多线程编程聊天程序的具体实现
1创建MFC-EXE工程,工程名为Chat,Dialogbased。
2删除对话框控件,添加自己的控件,各个控件的ID分别为:
接受文本框的ID为IDC_EDIT_RECV,发送数据的文本框ID为IDC_EDIT_SEND,发送按钮的ID为IDC_BTN_SEND。
其余默认即可。
3加载套接字库,进行版本协商
MFC环境下使用AfxSocketInit(查看MSDN帮助)
AfxSocketInit加载的1.1版本,可以确保Socket库的释放
MSDN中提到在CWinApp:
:
InitInstance中使用
在这里就是CChatAPP类的InitInstance中调用
if(!
AfxSocketInit())
{
AfxMessageBox("加载套接字库失败!
");
returnFALSE;
}
4刚才使用了AfxSocketInit需要加入头文件
#include放入到StdAfx.h的头文件中,StdAfx.h是预编译头文件,包括MFC工程必要的头文件
5在CChatDlg类中定义一个成员函数,作为套接字初始化工作BOOLInitSocket()
同时增加一个变量SOCKETm_socket
6实现InitSocket()
m_socket=socket(AF_INET,SOCK_DGRAM,0);
if(INVALID_SOCKET==m_socket)
{
MessageBox("套接字创建失败!
");
returnFALSE;
}
SOCKADDR_INaddrSock;
addrSock.sin_family=AF_INET;
addrSock.sin_port=htons(1234);
addrSock.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
intretval;
retval=bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR));
intretv=WSAGetLastError();
if(SOCKET_ERROR==retval)
{
closesocket(m_socket);
MessageBox("绑定失败!
");
returnFALSE;
}
returnTRUE;
7在CChatDlg的OnInitDialog()的最后调用InitSocket()
8下面编写接收端程序
(1)当接受数据时如果没有数据到来,recvfrom函数会阻塞,从而导致进程暂停运行,所以采用线程来完成
(2)创建线程后,我们需要传递两个参数,一个是套接字,一个是对话框的句柄(接受编辑框的句柄),这样可以通过套接字接受数据,传给接受编辑框,但CreateThread只能传递一个参数(第四个),但是个指针
9在CChatDlg的头文件中定义一个结构体
structRECVPARAM
{
SOCKETsock;
HWNDhwnd;
};//注意这里的分号
10在CChatDlg的OnInitDialog()最后,即InitSocket()后
RECVPARAM*pRecvParam=newRECVPARAM;
pRecvParam->sock=m_socket;
pRecvParam->hwnd=m_hWnd;
HANDLEhThread=CreateThread(
NULL,0,RecvProc,(LPVOID)pRecvParam,0,NULL);
CloseHandle(hThread);
returnTRUE;
11加入线程函数,从MSDN中COPY
加入一个全局函数,有的单位要求不允许使用全局函数,那这里加给CChatDlg类,作为一个成员函数
DWORDWINAPIRecvProc(LPVOIDlpParameter)
12此时程序报错,因为要执行RecvProc需要CChatDlg的实例,不能在类中调用,所以报错
解决方法:
在CChatDlg头文件中,将RecvProc函数定义为static类型
13实现线程函数
SOCKETsock=((RECVPARAM*)lpParameter)->sock;
HWNDhwnd=((RECVPARAM*)lpParameter)->hwnd;
deletelpParameter;
SOCKADDR_INaddrFrom;
intlen=sizeof(SOCKADDR);
charrecvBuf[200];
chartempBuf[300];
intretval;
while(TRUE)
{
retval=recvfrom(sock,recvBuf,200,0,
(SOCKADDR*)&addrFrom,&len);
if(SOCKET_ERROR==retval)
break;
sprintf(tempBuf,"%s说:
%s",
inet_ntoa(addrFrom.sin_addr),recvBuf);
:
:
PostMessage(hwnd,WM_RECVDATA,0,
(LPARAM)tempBuf);
//发送数据传送一个自定义消息,传给窗口
}
return0;
14加入消息相应机制
(1)在CChatDlg的头文件中定义消息值,加入
#defineWM_RECVDATAWM_USER+1
(2)做一个消息相应函数原型的声明
在CChatDlg的头文件的下方的protected声明中加入
afx_msgvoidOnRecvData(WPARAMwParam,LPARAMlParam);//注意加入了两个参数,因为PostMessage发送消息的过程中传递了参数
(3)加入消息映射:
注意因为CAboutDlg类和CChatDlg类在一个CPP文件中,所以消息映射一定要加正确了
找到BEGIN_MESSAGE_MAP(CChatDlg,CDialog)
在最后面写入:
ON_MESSAGE(WM_RECVDATA,OnRecvData)
第一个参数是消息本身:
WM_RECVDATA
第二个参数是消息相应函数:
OnRecvData
注意后面不要加分号
(4)最后是消息相应函数的实现
在CPP最后加入
voidCChatDlg:
:
OnRecvData(WPARAMwParam,LPARAMlParam)
{
}
15实现刚才加入的OnRecvData消息相应函数
CStringstr=(char*)lParam;
CStringstrTemp;
//获取旧文本
GetDlgItemText(IDC_EDIT_RECV,strTemp);
str+="\r\n";//增加一个换行
str+=strTemp;
SetDlgItemText(IDC_EDIT_RECV,str);//放到编辑框
16双击发送按钮,系统自动生成按钮相应事件
(1)首先要获取IP,查看MSDN帮助CIPAddressCtrl
CIPAddressCtrl对应的就是IP控件,通过一个DWORD类型的变量来接受IP地址
首先得到控件的指针:
GetDlgItem(IDC_IPADDRESS1)
强制转换为(CIPAddressCtrl*)类型
最后调用CIPAddressCtrl下的一个函数GetAddress
完整的程序是
DWORDdwIP;
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);
(2)得到IP地址后,可以发送数据,继续在后面加入代码
SOCKADDR_INaddrTo;
addrTo.sin_family=AF_INET;
addrTo.sin_port=htons(1234);
addrTo.sin_addr.S_un.S_addr=htonl(dwIP);
CStringstrSend;
GetDlgItemText(IDC_EDIT_SEND,strSend);
sendto(m_socket,strSend,strSend.GetLength()+1,0,
(SOCKADDR*)&addrTo,sizeof(SOCKADDR));
SetDlgItemText(IDC_EDIT_SEND,"");
17此时发现文本框并没有换行显示选择接收数据的文本框控件,右键选择属性,在Styles中选择Multiline就可以了;
18为了让我们回车就发送数据,可以设置发送按钮为缺省按钮,可以右键选择按钮属性,在在Styles中选择Defaultbutton就可以了。
二.具体代码的实现
CPaintDCdc(this);//devicecontextforpainting
SendMessage(WM_ICONERASEBKGND,(WPARAM)dc.GetSafeHdc(),0);
//Centericoninclientrectangle
intcxIcon=GetSystemMetrics(SM_CXICON);
intcyIcon=GetSystemMetrics(SM_CYICON);
CRectrect;
GetClientRect(&rect);
intx=(rect.Width()-cxIcon+1)/2;
inty=(rect.Height()-cyIcon+1)/2;
//Drawtheicon
dc.DrawIcon(x,y,m_hIcon);
}
else
{
CDialog:
:
OnPaint();
}
}
//Thesystemcallsthistoobtainthecursortodisplaywhiletheuserdrags
//theminimizedwindow.
HCURSORCChartDlg:
:
OnQueryDragIcon()
{
return(HCURSOR)m_hIcon;
}
BOOLCChartDlg:
:
InitSocket()
{
m_socket=socket(AF_INET,SOCK_DGRAM,0);
if(INVALID_SOCKET==m_socket)
{
MessageBox("套接字创建失败!
");
returnFALSE;
}
SOCKADDR_INaddrSock;
addrSock.sin_family=AF_INET;
addrSock.sin_port=htons(1234);
addrSock.sin_addr.S_un.S_addr=inet_addr(GethostIP());
intretval;
retval=bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR));
intretv=WSAGetLastError();
if(SOCKET_ERROR==retval)
{
closesocket(m_socket);
MessageBox("绑定失败!
");
returnFALSE;
}
returnTRUE;
}
DWORDWINAPICChartDlg:
:
RecvProc(LPVOIDlpParameter)
{
SOCKETsock=((RECVPARAM*)lpParameter)->sock;
HWNDhwnd=((RECVPARAM*)lpParameter)->hwnd;
deletelpParameter;
SOCKADDR_INaddrFrom;
intlen=sizeof(SOCKADDR);
charrecvBuf[200];
chartempBuf[300];
intretval;
while(TRUE)
{
retval=recvfrom(sock,recvBuf,200,0,(SOCKADDR*)&addrFrom,&len);
if(SOCKET_ERROR==retval)
break;
sprintf(tempBuf,"%s说:
%s",inet_ntoa(addrFrom.sin_addr),recvBuf);
:
:
PostMessage(hwnd,WM_RECVDATA,0,(LPARAM)tempBuf);
//发送数据传送一个自定义消息,传给窗口
}
return0;
}
voidCChartDlg:
:
OnRecvData(WPARAMwParam,LPARAMlParam)
{
CStringstr=(char*)lParam;
CStringstrTemp;
//获取旧文本
GetDlgItemText(IDC_EDIT_RECV,strTemp);
if(strTemp!
="")strTemp+="\r\n";//增加一个换行
strTemp+=str;
SetDlgItemText(IDC_EDIT_RECV,strTemp);
((CEdit*)GetDlgItem(IDC_EDIT_RECV))->LineScroll(((CEdit*)GetDlgItem(IDC_EDIT_RECV))->GetLineCount());
}
voidCChartDlg:
:
OnButtonSend()
{
//TODO:
Addyourcontrolnotificationhandlercodehere
DWORDdwIP;
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);
SOCKADDR_INaddrTo;
addrTo.sin_family=AF_INET;
addrTo.sin_port=htons(1234);
addrTo.sin_addr.S_un.S_addr=htonl(dwIP);
CStringstrSend;
GetDlgItemText(IDC_EDIT_SEND,strSend);
sendto(m_socket,strSend,strSend.GetLength()+1,0,(SOCKADDR*)&addrTo,sizeof(SOCKADDR));
SetDlgItemText(IDC_EDIT_SEND,"");
CStringstrTemp;
CStringstr="我说:
"+strSend;
//获取旧文本
GetDlgItemText(IDC_EDIT_RECV,strTemp);
if(strTemp!
="")strTemp+="\r\n";//增加一个换行
strTemp+=str;
SetDlgItemText(IDC_EDIT_RECV,strTemp);
((CEdit*)GetDlgItem(IDC_EDIT_RECV))->LineScroll(((CEdit*)GetDlgItem(IDC_EDIT_RECV))->GetLineCount());
}
LPCSTRCChartDlg:
:
GethostIP()
{
charname[255];
PHOSTENThostinfo;
LPCSTRip;
if(gethostname(name,sizeof(name))==0)
{//如果能获取计算机主机信息的话,则获取本机IP地址
if((hostinfo=gethostbyname(name))!
=NULL)
{
ip=inet_ntoa(*(structin_addr*)*hostinfo->h_addr_list);
}
}
returnip;
}
三.注意事项:
1一定要保证bind的端口和recvfrom的端口号一致;
2在绑定的IP地址设置中,如果只与本地进行通信,采用("127.0.0.1")是可以的,但如果和其他网络的机器进行通信,必须写完整的机器IP,比如:
("172.18.49.126");
3注意控件的ID要一致,比如这里的示例中:
接受文本框的ID为IDC_EDIT_RECV,发送数据的文本框ID为IDC_EDIT_SEND,发送按钮的ID为IDC_BT。
四.参考文献
[1]袁庆龙,候文义.Ni-P合金镀层组织形貌及显微硬度研究[J].太原理工大学学报,2001,32
(1):
51-53.(连续出版物:
[序号]主要责任者.文献题名[J].刊名,出版年份,卷号(期号):
起止页码)
[2] 刘国钧,王连成.图书馆史研究[M].北京:
高等教育出版社,1979:
15-18,31.(专著:
[序号]主要责任者.文献题名[M].出版地:
出版者,出版年:
起止页码.)
[3]孙品一.高校学报编辑工作现代化特征[C].中国高等学校自然科学学报研究会.科技编辑学论文集
(2).北京:
北京师范大学出版社,1998:
10-22.(论文集:
[序号]主要责任者.文献题名[C]∥主编.论文集名.出版地:
出版者,出版年:
起止页码.)