聊天程序实验报告.docx
《聊天程序实验报告.docx》由会员分享,可在线阅读,更多相关《聊天程序实验报告.docx(24页珍藏版)》请在冰点文库上搜索。
聊天程序实验报告
桂林电子科技大学
计算机网络C实验报告
实验名称聊天程序的设计
电子工程与自动化系测控技术与仪器专业
辅导员
成绩签名
14008104班第实验小组
作者张业标学号1400810427
同作者
实验日期2016年12月日
辅导员意见
【实验目的】
1、熟悉VisualC++的基本操作;
2、基本了解基于对话框的windows应用程序的编写过程;
3、对于WindowsSocket编程建立初步概念,并实现聊天程序的编写。
【实验要求】
1、能编写基于对话框的windows应用程序;
2、能用VisualC++中的MFCCSocket类,实现网络传输数据;
3、制作实用的局域网一对一聊天程序;
【实验原理】
一、WindowsSocket和套接口的基本概念
网际协议(InternetProtocol,IP)是一种用于互联网的网络协议,已广为人知。
它可以
广泛用于大多数计算机操作系统上,也可用于大多数局域网LAN(比如小型办公网络,小型
宿舍网络)和广域网WAN(例如互联网)。
从它的设计来看,IP是一个无连接的协议,并不能保证数据投递万无一失。
两个上层协议(TCP和UDP)依赖IP协议进行数据通信。
如果希望在MicrosoftWindows下通过TCP和UDP协议建立网络应用程序,则需要使用
Winsock套接口编程技术。
套接口,就是指向传输提供者的句柄。
Win32中,套接口不同于文件描述符,所以它
是一个独立的类型SOCKETWindowsSocket描述并定义了一个MicrosoftWindows的网
络编程界面,它是从UnixSocket的基础上发展而来的,为WindowsTCP/IP提供了一个BSD
型的套接字规范,除与4.3BSDUnixSocket完全兼容外,还包括一个扩充文件,通过一组附加的API实现Windows式(即事件驱动)的编程风格;而Winsock则是在MicrosoftWindows中进行网络应用程序设计的接口。
Windows在Internet支配域中的TCP/IP协议定义了Winsock
网络编程规范,融入了许多新特点。
使用Socket的目的是使用户在网络协议上工作而不必
Socket
对该网络协议有非常深入的了解。
此外,编写的程序还可被迅速地移植到任何支持
的网络系统中去。
Winsock提供了一种可为指定传输协议打开、计算和关闭会话的能力。
在Windows下,TCP/IP上层模型在很大程度上与用户的Winsock应用有关;换言之,用户的Winsock应用控制了会话的方方面面,必要时,还会根据程序的需要格式化数据。
套接口有三种类型:
流式套接口、数据报套接口及原始套接口。
流式套接口定义了一种可靠的面向连接的服务(利用TCP协议),实现了无差错无重复
的顺序数据传输。
数据报套接口定义了一种无连接的服务(UDP),数据通过相互独立的报
文进行传输,是无序的,并且不保证可靠和无差错。
原始套接口允许对低层协议如IP和ICMP
直接访问,主要用于新的网络协议实现的测试等。
无连接服务器一般都是面向事务处理,一个请求、一个应答就完成了客户程序与服务
程序之间的相互作用。
套接口工作过程如下:
服务器启动,通过调用socket()建立一个套接口,然后调用bind()将该套接口和本地网络地址联系在一起,再调用accept()来接收连接。
客户在建立套接口后
调用connect()和服务器建立连接。
连接一旦建立,客户机和服务器之间就可以通过调用
read()和write()来发送和接受数据。
最后,待数据传送结束后,双方调用close()关闭套接口。
在网络编程中,掌握端口的概念十分重要。
端口:
基于TCP/IP协议的网络中,计算机
都分配有一个IP地址,用一个32位二进制数来表示,正式的称呼是“Ipv4地址”。
客户机
需要通过TCP或UDP和服务器通信时,必须指定服务器的IP地址和服务端口号。
另外,服
从本质上说,端口可分为3类:
“已知”端口、已注册端口、动态和(或)私用端口。
(1)“已知”端口0〜1023,由IANA控制,是在UNIX中为固定服务保留的。
(2)已注册的端口1024〜49151,由IANA列出来的,供普通用户的普通用户进程或程序使用。
(3)动态和(或)私用端口49152〜65535.
普通用户应选择1024〜49151之间的已注册端口,从而避免端口号已被另一个应用或系统服务所用。
此外,49152〜65535间的端口可自由使用,因为IANA在这些端口上没有注
册服务。
、MFC对Socket编程的封装类简介
MicrosoftWindowsClassLibrary(MFC)中提供了较高级圭寸装的类来实现网络通信。
图
4-1给出了CSocket类的继承关系。
CObject
CAsyncSocket
CSocket
图4-1
CAsyncSocket类圭寸装了WindowsSocketsAPI函数,提供了较低层的与WindowsSockets对话接口,一般适用于有相当水平的网络编程者使用,可方便地进行低层的网络事件通知及
信息回叫控制等操作。
CSocket类派生于CAsyncSocket,它继承了父类中一些常用易懂的WindowsSocketsAPI
函数,并对CAsyncSocket中低层的较难控制的一些API函数或成员函数进行了处理,使得网
络传输简捷易用,同时它支持模块化的后台信息处理,解决了CAsyncScoket中较难克服的多
线程处理。
下面介绍用VisualC++在windows中实现Socket类型成员函数(这些成员函数实际上是从CAsyncSocket类继承来的)。
成员函数和参数说明:
(1)BOOLCreate(UNITnsocketPort=0,intnSocketType=SOCK_STREAM,onglEvent=
FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE,LPCTSTR
lpszSocketAddress=NULL)
该函数用来建立Socket,如果函数成功,则返回非零值;否则返回值为0。
其中:
nSocketPort:
为所选择的socket端口,一般要大于1023,如果该参数为0,则由系统选定一端口,默认值0。
nSocketType:
为套接字类型——SOCK_STREAM^SOCK_DGRAMSOCK_STREAM表示为流套接字(本实验使用基于TCP连接的流套接字编程),SOCK_DGRAM表示为数据报套接字,默认值为SOCK_STREAM
lEvent:
标识该Socket要完成哪种工作,默认值为FD_READ|FD_WRITE|FD_OOB|
FD_ACCEPT|FD_CONNECT|FD_CLOSE
lpszSocketAddress:
一个指向字符串的指针,该字符串包含了被连接套接口的网络地址。
一个带点的数据,如“128.56.22.8”默认值为NULL。
注意:
CSocket中,WinsockAPI的初始化(socket)和绑定(bind)两部分工作都完成了。
(2)BOOLListen(intnConnectionBacklog=5)
该函数的作用是等待Socket请求,如果调用成功,则返回非零值;否则返回值为0。
Listen仅对那些支持连接的套接字起作用,也就是SOCK_STREA啖型的套接字。
在进程应答连接
并把它放到等待队列时,套接字被置成被动模式(passivemode)。
本函数一般由那些一次可
以用多个连接的服务器使用(或任何需要接受连接的应用)。
nConnectionBacklog:
表示等待队列的长度,默认值为最大值5,有效值为1〜5。
(3)BOOLConnect(LPCTSTRlpszHostAddress,UINTnHostPort)
该函数的作用是提出连接请求。
其中:
lpszHostAddress:
对象连接的套接字的网络地址、机器名,女口ftp.sjtu.edu.dn,或以句点
分隔的数字,如“211.80.43.100”
nHostPort:
为接受请求进程的网络地址和Socket端口号。
注意:
Connect函数还有另一个版本:
BOOLConnect(constSOCKADDR*lpSockAddr,int
nSockAddrLen);具体用法可以参阅MSDNLibrary中关于CAnyscSocket类的阐述。
(4)virtualvoidClose()
该函数的作用是关闭该Socketo
三、利用CSocket进行传输的辅助类简介
1、CSocketFile类
CSocketFile继承自CFile类,用在基于WindowsSocket的网络上传输数据。
首先,将一
个建立连接的CSocket对象实例作为参数进行初始化,然后,将已经初始化的CSocket对象
连接到CArchive对象上,接着将数据串行化,以使用MFC系列来简化发送和接受数据,最
终实现利用网络的Socket传输和本机上的流传输一样简单。
成员函数:
CSocketFile用到的成员函数只有构造函数。
CSocketFile:
:
CSocketFile(CSocket*pSocket,BOOLbArchiveCompatible=true);其中:
pSocket:
连接到CSocketFile对象的套接口。
bArchiveCompatible:
指示该文件对象是否与一个CArchive对象一起使用。
只有当希望
在单机方式下来使用这个CSocketFile对象时,才传递FALSE因为仅有CSocketFile类实例本
身没有什么意义,所以通常将其置为TRUE
说明:
此成员函数用来构造一个CSocketFile对象。
当此对象超出范围或被删除时,它
的析构函数将使它自己从插槽对象上分离。
注意:
一个CSocketFile对象也可以在没有CAchive对象的情况下作为一个(受限制的)文件来使用。
缺省情况下,CSocketFile构造函数的bArchiceCompatible参数是TRUE它表明此文件对象与一个档案一起使用。
要在没有档案的情况下使用该文件对象,则给bArchiceCompatible参数传递FALSE在“档案兼容”模式下,一个CSocketFile对象可以提供更好的表现,并减少出现“死锁”的几率。
2、CArchive类
CAchive类没有基类。
CArchive允许以永久二进制(通常为磁盘存储)的形式保存一个
对象的复杂网络,它可以从永久存储中装载对象,并在内存中重新构造它们。
使数据永久保
留的过程就叫做“串行化”。
一般可以把一个CArchive对象看作一个二进制流,可以将它和输入流iostream类的用法进行比较。
CArchive对象一般和一个文件类关联(CFile类或
CSocketFile类)。
输入输出流是加工处理ASCII字符,而CArchive类的用处是高效、无冗余地
处理二进制数据。
在CArchive类中,重载了提取(>>)和插入(<<)运算符,它是方便的归档编程接口,主要支持CObject派生类。
四、MFCCSocket类的通信流程
使用CSocket类进行网络二进制数据通信的连接流程,如下表所示:
服务器端
注释
客户端
1
CSocketmserver
构造一个socket对象
CSocketmclient;
2
m_server.creat(nport)
创建socket
m_client.creat(nport);
3
m_server.listen();
听等连接
与服务器
建立连接
m_client.connect(straddr,nport);
此时阻塞,等待服务器端监听。
4
CSocketm_receive;
m_server.accept(m_receive);
此时阻塞,等待客户机连接
构造新的socket对象用
以接受客户端的连接
5
CSocketFilefile(&m_server)
构造一文件对象
CSocketFilefile(&m_server);
6
CArchivearin(&file,CAchive:
:
load);
CArchivearout(&file,CAchive:
:
store);
构造流对象
CArchivearin(&file,CArchive:
:
load);
CArchivearout(&file,CArchive:
:
store);
7
arin>>value;arout<用流进行数据的传输概
念和cin,cout相似
arin>>value;arout<注意事项:
利用CArchive类进行网络数据传输的操作固然方便直观,但是如果编写的
程序是和别人的程序进行通信的话,就要注意对方的程序是否也使用了CArchive类,否则
会造成数据相互不能识别。
五、使用CSocket类的同步问题和解决方法
有了上面的理论基础,就可以自己设计实现网络通信了。
可以做到基于阻塞发送和接受二进制数据。
例如:
可以Client端发送数据,Server端接收数据:
Server端:
m_receive(void*lpBuf,intnBufLen,intnFlags=0);
Clietn端:
m_client(constvoid*lpBuf,intnBufLen,intnFlags=0);
接着我们再分析一下各个类中提到的常用方法的同步特性:
Listen(•••.):
执行后无论有无连接,立即返回。
Connect(…):
如果服务器端有端口正在监听,则立即成功返回,如果没有,则过几秒钟后将显示无法连接。
Accept(-•):
Listen函数返回后可以执行此函数,但是此函数是基于阻塞的,只要客户机connect连接并且端口正确,则立即成功返回并建立连接;若迟迟监听不到连接,则不
断阻塞,直到连接成功或者强行关闭。
Send(…):
调用后就将数据保存在socket缓冲区中,立即返回。
Receive(…):
和Accept一样的阻塞,直到能从socket缓冲区成功读取到nBufLen长度的数据。
按照上述分析,读者可能会考虑这样的两个问题:
(1)监听的时候,如果客户端迟迟没有连接,则监听方执行到Accept则阻塞不能相应。
(2)在用Receive接受数据时,若迟迟得不到发送的数据,也阻塞不能响应。
显然,带有这两个问题的软件是不能被接受的。
CSocket类里的OnReceive和OnAccept
消息处理函数(继承自CAsyncSocket类)可以解决上述两个问题,其原理是:
(1)OnAccept():
Listen过后,若监听到客户机有连接,则产生消息调用OnAccept(),
一般可以在此函数里面调用Accept便可避免监听时的阻塞。
virturalvoidOnAccept(intnErrorCode):
nErrorCode为套接字上最近的错误代码,此成员
函数可用的错误代码有:
0:
函数成功地执行并返回。
WSAENETDOWNWindowsSocket检测到网络系统故障。
说明由框架调用,通知监听套接字现在可以调用Accept成员函数来接收挂起的连接请求(有connect请求进入)。
(2)OnReceive():
建立连接后,若检测到Socket缓冲区里有数据到达,便自动调用
OnReceive(),在此函数里面使用Receive接收就可避免接受数据的阻塞。
virturalvoidOnReceive(intnErrorCode):
nErrorCode为套接字上最近的错误代码,此成员函数可用的错误代码有:
0:
函数成功地执行并返回。
WSAENETDOWNWindowsSocket检测到网络系统故障。
说明由框架调用,通知套接字缓冲区中有数据,可以调用成员函数Receive取出。
【实验内容与步骤】
在上述实验原理的基础上,以点对点通信的聊天程序为例实现网络通信,客户机/服务
器模式是socket点对点网络程序的典型模式。
使用的面向连接的TCP连接套接字是MFC的
典型方式。
其实现步骤为:
1、创建两个对话框程序,分别为服务器端和客户端;
2、启动服务器端程序;
3、创建套接字后等待客户的连接;
4、客户启动,创建套接字,然后和服务器连接;
5、连接建立后,客户机和服务器可以通过建立的套接字连接进行信息通信。
聊天工具设计
目的、需求
r甲精
r■户曲
沽'rrf
Erik
複逢IS1
it方洋®
SMC
IT壬记吉
.•:
i-.
*仝记杲
■E岀较杵
Edta
1、建立MFC工程
2、插入对话框
右侧对话框,左侧工具栏
一、设计对话框界面
1•设置对话框属性2、静态文本控件3•编辑控件3.组框4•单选框5•复选框6•列表框7•组
合框
PS:
更改控件的ID号和标题
:
■、为设计好的对话框添加类
(为对话框添加相应的类才能使用对话框)
CChatDIg,类名的第一个C字母代表一个"类(class)
flnivrXEvents
□古帰空Into
AddCJd^s,,
2
DeFelcrunctiuu
Metistiutfs;
tditCode
clhoc
dklj
Memberfunctions:
DoDatatxchinge
Description:
CnncRl
V.FC匚lassWizard
AotnmMinn
fldiurXFv^nfs
□購Into
AddClass.
3
BindAll
Description:
Cnncel
dkrlklj
CfiicWindowRectCreateDeiWindowPracDBEtrcyWindowDoDataExchsnqe
DoModalGHStrolllBarClrl
iAddVariable..'IIrhe匕巴3
DeleteVdiidUl
inctum
IDCZEOIT2IDCEDITHIDCZCOITb闾ia
Classname中出现CChartDIg,表明类已经添加成功
AddFunction
3、为对话框中的控件添加变量
接步骤
(2),开始:
点击MemberVariables选项卡,表中列出ID控件
MAmh^rViri4hlfi>AuinmMir>n
fwq
IDAPPABOUT
JptiaieCfflumne
Ernjenrprogramctial
r—=—=
MFCClassVt/izard
Class
|CChatUIg-
CIjiss耐皿尺
|CChatUIg-
CChatni
(CChirDiia
y||WIcHai&
Wpmgrrtmcfwit-Mircc-isor?
V
HlFileE.ditViewtn&etlPi
4、为所有控件添加变量如图所示:
MFCCh«Wlj^rd
MapsMemberVariablesMtomMionActiveXEventsCla€GInfo
Pro|ect
Classnanne:
AddClisi
|pio(|r3(nch3l
二]CChalOlg
F:
l,.4prvgramcbdlWhatDIgJi・F:
t.Aprogramdiai^ChatDIyxpp
AildV^rifl
ConkvlIDs;
Type
Mrrnhirr
DeleteV«
kslj
CBuUan
mkslj
*
l)zt
CSiring
IDIj理
UpdateCq
It"
CStfng
Ik
CniiHon
m_lx
Bindi
财
CBvUan
m_qkjl
rj^m
^Button
mrj^rn
£7
CRunon
mS7
tcrj
CBuHon
m_tcrj
—
Mail
CButlon
inxsjl
xxnr
CStllnq
mKKnr
CSlrliiu
in_&
Dcauiptiori:
CStringwithIcngitivalidEdon
M^^imurriChsracter^;
澤program监k"-5CAfeu^Di^m-«■■:
CCh^CJlg
■cchiimigicwv锵DqDnlaLMJcIi-Bi
*mdhocdkdMIFnnfits
0nn_ks4i严rti」|wl
•mlx
审m_nr
*心啊
令呵—愉|wmM|lr/nnvxnr
叶■:
CChlldiFianic
14HCLliihniFrHimfr
I-H:
CPrcigrsnMchsUipnB1CPfugfdmthalDB対■T聘Pm#*"砸ir;h冲fVt亡i±_Globals
口亠誉口咱…月Filr
_J
5、控件变量的意义:
6、CChatDIg.cpp文件中对成员函数变量进行初始化
CChatDlg:
:
CChatDlg(CWnd*pParent/*=NULL*/):
CDialog(CChatDlg:
:
IDD,pParent)
{
〃{{AFX_DATA_INIT(CChatDlg)m_computer=_T("");
m_connect=_T("");
m_example=_T("");
m_ip=_T(””);
m」pport=_T("");m_message=_T("");m_otherview=_T("");
m_port=_T("");m_recordvew=_T("");
m_serve=-1;//}}AFX_DATA_INIT
}
三、为组合框和列表框添加初始化内容
OnlnitDialog()函数,初始化内容在OnlnitDialog()函数中添加。
1.因为OnlnitDialog()函数是VM」NITDIALOG消息的响应函数,所以我们使用添加消息响应函数的方法添加On