通信协议与网络程序设计实验指导书.docx
- 文档编号:9209487
- 上传时间:2023-02-03
- 格式:DOCX
- 页数:61
- 大小:174.57KB
通信协议与网络程序设计实验指导书.docx
《通信协议与网络程序设计实验指导书.docx》由会员分享,可在线阅读,更多相关《通信协议与网络程序设计实验指导书.docx(61页珍藏版)》请在冰豆网上搜索。
通信协议与网络程序设计实验指导书
网络程序设计实验指导书
电子与信息学院
2008年10月
《网络程序设计》实验指导书
一、说明
本实验指导书根据《网络程序设计》教学大纲和实验大纲编写。
前2个实验为练习性实验,旨在让学生在熟悉开发工具的环境和使用,熟悉套接字编程的基本原理。
后面的各个实验主要让学生理解各协议的工作原理,掌握相应应用程序的实现过程。
二、实验目的
通过实验,使学生熟悉并掌握计算机网络编程的基本知识,加深对课堂教学内容的理解,掌握基本的编程技巧,使学生能够进行一些简单的网络程序设计。
三、实验安排
《网络程序设计》实验部分共有9实验,每个实验占用2学时。
具体安排如下:
实验一、熟悉开发工具(2课时)
实验二、套接字编程练习(2课时)
实验三、TCP套接字编程(2课时)
实验四、UDP组播程序编程(2课时)
实验五、windowssocketsAPI练习(2课时)
实验六、聊天室程序的设计及实现(2课时)
实验七、PING程序的设计及实现(2课时)
实验八、电子邮件程序的设计与实现(SMTP客户端、POP3客户端程序)(2课时)
实验九、FTP客户端实现(2课时)
实验一熟悉开发工具
一、实验目的
通过编写简单的Windows窗口、基于MFC的计算器以及车站售票程序(多线程实现),深入了解VC++的开发环境,掌握常用的控件使用方法和程序编写过程。
二、实验内容
1.开发简单的Windows窗口
2.开发MFC下的程序界面,实现计算器,能进行运算。
3.开发一个多线程的例子:
车站售票程序。
三、实验环境
Windows操作系统、VC++6.0
四、实验指导
1.利用WindowsAPI函数编写Windows应用程序必须首先了解以下内容:
(1)窗口的概念
(2)事件驱动的概念
(3)句柄
(4)消息
2.Windows应用程序常用消息
(1)WM_LBUTTONDOWN:
产生单击鼠标左键的消息
(2)WM_KEYDOWN:
按下一个非系统键时产生的消息
(3)WM_CHAR:
按下一个非系统键时产生的消息
(4)WM_CREATE:
由CreateWindow函数发出的消息
(5)WM_CLOSE:
关闭窗口时产生的消息
(6)WM_DESTROY:
由DestroyWiodow函数发出的消息
(7)WM_QUIT:
由PostQuitMessage函数发出的消息
(8)WM_PAINT
3.Windows应用程序组成及编程步骤
(1)应用程序的组成
●C语言源程序文件
●头文件
●模块定义文件
●资源描述文件
●项目文件
(2)源程序组成结构
入口函数WinMain
窗口函数WndProc
①入口函数WinMain
●WinMain函数的说明如下:
intWINAPIWinMain
(HINSTANCEhThisInst,∥应用程序当前实例句柄
HINSTANCEhPrevInst,∥应用程序其他实例句柄
LPSTRlpszCmdLine,∥指向程序命令行参数的指针
IntnCmdShow∥应用程序开始执行时窗口显示方式的整数值标识
)
●初始化
●消息循环
消息循环的常见格式如下:
MSGMsg;
…
while(GetMessage(&Msg,NULL,0,0))
{TranslateMessage(&Msg);
DispatchMessage(&Msg);}
②窗口函数的一般形式如下:
LRESULTCALLBACKWndProc(HWNDhwnd,UINTmessgae,
WPARAMwParam,LPARAMlParam)
{…
switch(message)∥message为标识的消息
{case…
…
break;
…
caseWM_DESTROY:
PostQuitMessage(0);
default:
returnDefWindowProc(hwnd,message,wParam,lParam);
}
return(0);
2.多线程
(1)HANDLECreateThread(
LPSECURITY_ATTRIBUTESlpThreadAttributes,
SIZE_TdwStackSize,
LPTHREAD_START_ROUTINElpStartAddress,
LPVOIDlpParameter,
DWORDdwCreationFlags,
LPDWORDlpThreadId
);
A:
第三个参数是个函数指针,指向某种特定的函数,调用约定是WINAPI//#defineWINAPI__stdcall, 参数LPVOID保障了函数的合法性.
B:
次函数两个得到两个值:
第一个值是HANDLE,大部分和线程有关的API函数都要使用它;第二个是参数ThreadID带回来的值,它是独一无二的表示一个进程中的某个线程.
说明:
我们不可以从一个线程的ID从而得到其HANDLE.
(2)BOOLCloseHandle(
HANDLEhObject
);
用来释放核心对象。
(3)创建一个新的线程
要创建一个线程,必须得有一个主进程,然后由这个主进程来创建一个线程,在一般的VC程序中,主函数所在的进程就是程序的主进程。
让我们从主函数来开始编写我们这个简单的小程序。
我们知道CreateThread函数可以用来创建一个线程,在MSDN中查找这个函数得到如下信息:
"TheCreateThreadfunctioncreatesathreadtoexecutewithintheaddressspaceofthecallingprocess."和"Ifthefunctionsucceeds,thereturnvalueisahandletothenewthread."所以我们得定义一个句柄用来存放它的返回值。
还定义一个指向线程ID的DWORD值dwThreadId。
然后我们就可以用CreateThread函数来创建我们的线程了,CreateThread函数有六个参数分别是
LPSECURITY_ATTRIBUTESlpThreadAttributes, //pointertosecurityattributes
DWORDdwStackSize, //initialthreadstacksize
LPTHREAD_START_ROUTINElpStartAddress, //pointertothreadfunction
LPVOIDlpParameter, //argumentfornewthread
DWORDdwCreationFlags, //creationflags
LPDWORDlpThreadId //pointertoreceivethreadID
其中第一个参数我们设置为NULL,使这个句柄不能被继承;第二个参数设置为0,使用默认的堆栈大小;第三个参数为线程函数的起始地址,也就是线程函数的函数名;第四个参数为NULL,没有值要传递给线程函数;第五个参数为0,创建好之后马上让线程运行;第六个参数设置为指向线程ID的地址。
创建好线程之后,线程函数进行初始化之类的操作,主函数继续执行,此时可以输出被创建线程的ID。
我们在主函数中用WaitForSingleObject函数来等待线程函数变成受信(signaled)状态,它的两个参数分别是
HANDLEhHandle, //handletoobjecttowaitfor
DWORDdwMilliseconds //time-outintervalinmilliseconds
第一参数为线程函数的句柄,第二个参数设置为INFINITE,等待线程一直执行完。
在程序的最后还要记得用CloseHandle函数关闭线程,这样主函数就写完了。
在线程函数里面我们可以简单地做一些工作,比如设置一个循环,让它输出一定的信息等。
源程序如下:
#include
#include
DWORDWINAPIThreadFunc(HANDLEThread)
{
inti;
for(i=0;i<10;i++)
{
cout<<"Anewthreadhascreated!
"< } return0; } intmain(intargc,char*argv[]) { HANDLEThread; DWORDdwThreadId; Thread=: : CreateThread (NULL,0,ThreadFunc,NULL,0,&dwThreadId); cout<<"ThenewthreadIDis: "< : : WaitForSingleObject(Thread,INFINITE); : : CloseHandle(Thread); return0; } 五、实验报告要求 1.实验名称 2.实验目的 3.实验内容 4.测试数据和预期结果(必要时应准备多组数据) 5.算法分析和流程图 6.源程序(应加适当的注释,可读性好) 7.小结(出错及解决方法,上机调试的结果和体会) 实验二套接字编程练习 一、实验目的 通过在SDK模式下完成数据通信的过程,掌握WindowsSocket的常用函数的形式和使用方法,理解数据通信的过程。 二、实验内容 1.Winsock的启动与终止。 2.Winsock的创建及绑定和关闭。 3.建立通信连接listen及accept和connect。 4.数据的传输。 5.简单的客户机/服务器之间的通信。 三、实验环境 Windows操作系统、VC++6.0 四、实验指导 在WINDOWS95/98,WINDOWSNT进行WINSOCK开发使用的编程语言有很多,VC++,JAVA,DELPHI,VB等。 其中VC时使用最普遍,和WINSOCK结合最紧密的。 并且VC++对原来的WindowsSockets库函数进行了一系列封装,继而产生了CAsynSocket、CSocket、CSocketFile等类,它们封装着有关Socket的各种功能,是编程变得更加简单。 但如果你是一个WINSOCK编程的初学者,那么建议你在一开始还是学习WINSOCK最基本的API函数进行编程,这样可以大大加深对WINSOCK的了解,对将来很有好 处。 在VC中进行WINSOCK的API编程开发,需要使用到下面三个文件: 1.WINSOCK.H: 这是WINSOCKAPI的头文件。 2.WSOCK32.LIB: WINSOCKAPI连接库文件。 在使用中,一点要把它作为项目的非缺省的连接库包含到项目文件中去。 3.WINSOCK.DLL: WINSOCK的动态连接库,位于WINDOWS的安装目录下。 WINSOCK.DLL位于TCP/IP协议栈和应用程序之间。 也就是说,WINSOCK管理与TCP/IP协议的接口。 在一开始WINSOCK的应有开发时,你不必对TCP/IP协议有很深刻的了解。 但是,如果想成为一个为网络编程的高手,就一定要对下层了解得十分清楚。 总的来说,使用SOCKET接口(面向连接或无连接)进行网络通信时,必须按下面简单的四步进行处理: 1.程序必须建立一个SOCKET。 2.程序必须按要求配置此SOCKET。 也就是说,程序要么将此SOCKET连接到远方的主机上,要么给此SOCKET指定一个本地协议端口。 3.程序必须按要求通过此SOCKET发送和接收数据。 4.程序必须关闭此SOCKET。 (一)WinSock原理 1.客户机/服务器模式 在TCP/IP网络中两个进程间的相互作用的主机模式是客户机/服务器模式(Client/Servermodel)。 该模式的建立基于以下两点: (1)非对等作用; (2)通信完全是异步的。 客户机/服务器模式在操作过程中采取的是主动请示方式: 首先服务器方要先启动,并根据请示提供相应服务: (过程如下) (1)打开一通信通道并告知本地主机,它愿意在某一个公认地址上接收客户请求。 (2)等待客户请求到达该端口。 (3)接收到重复服务请求,处理该请求并发送应答信号。 (4)返回第二步,等待另一客户请求 (5)关闭服务器。 客户方: (1)打开一通信通道,并连接到服务器所在主机的特定端口。 (2)向服务器发送服务请求报文,等待并接收应答;继续提出请求…… (3)请求结束后关闭通信通道并终止。 2.基本套接字 为了更好说明套接字编程原理,给出几个基本的套接字,在以后的篇幅中会给出更详细的使用说明。 创建套接字──socket() 应用程序在使用套接字前,首先必须拥有一个套接字,系统调用socket() 向应用程序提供创建套接字的手段,其调用格式如下: SOCKETsocket(intaf,inttype,intprotocol); 该调用要接收三个参数: af、type、protocol。 参数af指定通信发生的区域,UNIX系统支持的地址族有: AF_UNIX、AF_INET、AF_NS等,而DOS、WINDOWS中仅支持AF_INET,它是网际网区域。 因此,地址族与协议族相同。 参数type描述要建立的套接字的类型。 参数protocol说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,则置为0,使用默认的连接模式。 根据这三个参数建立一个套接字,并将相应的资源分配给它,同时返回一个整型套接字号。 因此,socket()系统调用实际上指定了相关五元组中的“协议”这一元。 指定本地地址──bind() 当一个套接字用socket()创建后,存在一个名字空间(地址族),但它没有被命名。 bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号联系起来,即将名字赋予套接字,以指定本地半相关。 其调用格式如下: intbind(SOCKETs,conststructsockaddrFAR*name,intnamelen); 参数s是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。 参数name是赋给套接字s的本地地址(名字),其长度可变,结构随通信域的不同而不同。 namelen表明了name的长度。 如果没有错误发生,bind()返回0。 否则返回值SOCKET_ERROR。 地址在建立套接字通信过程中起着重要作用,作为一个网络应用程序设计者对套接字地址结构必须有明确认识。 3)建立套接字连接──connect()与accept() 这两个系统调用用于完成一个完整相关的建立,其中connect()用于建立连接。 无连接的套接字进程也可以调用connect(),但这时在进程之间没有实际的报文交换,调用将从本地操作系统直接返回。 这样做的优点是程序员不必为每一数据指定目的地址,而且如果收到的一个数据报,其目的端口未与任何套接字建立“连接”,便能判断该端口不可操作。 而accept()用于使服务器等待来自某客户进程的实际连接。 connect()的调用格式如下: intconnect(SOCKETs,conststructsockaddrFAR*name,intnamelen); 参数s是欲建立连接的本地套接字描述符。 参数name指出说明对方套接字地址结构的指针。 对方套接字地址长度由namelen说明。 如果没有错误发生,connect()返回0。 否则返回值SOCKET_ERROR。 在面向连接的协议中,该调用导致本地系统和外部系统之间连接实际建立。 由于地址族总被包含在套接字地址结构的前两个字节中,并通过socket()调用与某个协议族相关。 因此bind()和connect()无须协议作为参数。 accept()的调用格式如下: SOCKETaccept(SOCKETs,structsockaddrFAR*addr,intFAR*addrlen); 参数s为本地套接字描述符,在用做accept()调用的参数前应该先调用过listen()。 addr指向客户方套接字地址结构的指针,用来接收连接实体的地址。 addr的确切格式由套接字创建时建立的地址族决定。 addrlen为客户方套接字地址的长度(字节数)。 如果没有错误发生,accept()返回一个SOCKET类型的值,表示接收到的套接字的描述符。 否则返回值INVALID_SOCKET。 accept()用于面向连接服务器。 参数addr和addrlen存放客户方的地址信息。 调用前,参数addr指向一个初始值为空的地址结构,而addrlen的初始值为0;调用accept()后,服务器等待从编号为s的套接字上接受客户连接请求,而连接请求是由客户方的connect()调用发出的。 当有连接请求到达时,accept()调用将请求连接队列上的第一个客户方套接字地址及长度放入addr和addrlen,并创建一个与s有相同特性的新套接字号。 新的套接字可用于处理服务器并发请求。 四个套接字系统调用,socket()、bind()、connect()、accept(),可以完成一个完全五元相关的建立。 socket()指定五元组中的协议元,它的用法与是否为客户或服务器、是否面向连接无关。 bind()指定五元组中的本地二元,即本地主机地址和端口号,其用法与是否面向连接有关: 在服务器方,无论是否面向连接,均要调用bind();在客户方,若采用面向连接,则可以不调用bind(),而通过connect()自动完成。 若采用无连接,客户方必须使用bind()以获得一个唯一的地址。 以上讨论仅对客户/服务器模式而言,实际上套接字的使用是非常灵活的,唯一需遵循的原则是进程通信之前,必须建立完整的相关。 监听连接──listen() 此调用用于面向连接服务器,表明它愿意接收连接。 listen()需在accept()之前调用,其调用格式如下: intlisten(SOCKETs,intbacklog); 参数s标识一个本地已建立、尚未连接的套接字号,服务器愿意从它上面接收请求。 backlog表示请求连接队列的最大长度,用于限制排队请求的个数,目前允许的最大值为5。 如果没有错误发生,listen()返回0。 否则它返回SOCKET_ERROR。 listen()在执行调用过程中可为没有调用过bind()的套接字s完成所必须的连接,并建立长度为backlog的请求连接队列。 调用listen()是服务器接收一个连接请求的四个步骤中的第三步。 它在调用socket()分配一个流套接字,且调用bind()给s赋于一个名字之后调用,而且一定要在accept()之前调用。 数据传输──send()与recv() 当一个连接建立以后,就可以传输数据了。 常用的系统调用有send()和recv()。 send()调用用于在参数s指定的已连接的数据报或流套接字上发送输出数据,格式如下: intsend(SOCKETs,constcharFAR*buf,intlen,intflags); 参数s为已连接的本地套接字描述符。 buf指向存有发送数据的缓冲区的指针,其长度由len指定。 flags指定传输控制方式,如是否发送带外数据等。 如果没有错误发生,send()返回总共发送的字节数。 否则它返回SOCKET_ERROR。 recv()调用用于在参数s指定的已连接的数据报或流套接字上接收输入数据,格式如下: intrecv(SOCKETs,charFAR*buf,intlen,intflags); 参数s为已连接的套接字描述符。 buf指向接收输入数据缓冲区的指针,其长度由len指定。 flags指定传输控制方式,如是否接收带外数据等。 如果没有错误发生,recv()返回总共接收的字节数。 如果连接被关闭,返回0。 否则它返回SOCKET_ERROR。 输入/输出多路复用──select() select()调用用来检测一个或多个套接字的状态。 对每一个套接字来说,这个调用可以请求读、写或错误状态方面的信息。 请求给定状态的套接字集合由一个fd_set结构指示。 在返回时,此结构被更新,以反映那些满足特定条件的套接字的子集,同时,select()调用返回满足条件的套接字的数目,其调用格式如下: intselect(intnfds,fd_setFAR*readfds,fd_setFAR*writefds,fd_setFAR*exceptfds,conststructtimevalFAR*timeout); 参数nfds指明被检查的套接字描述符的值域,此变量一般被忽略。 参数readfds指向要做读检测的套接字描述符集合的指针,调用者希望从中读取数据。 参数writefds指向要做写检测的套接字描述符集合的指针。 exceptfds指向要检测是否出错的套接字描述符集合的指针。 timeout指向select()函数等待的最大时间,如果设为NULL则为阻塞操作。 select()返回包含在fd_set结构中已准备好的套接字描述符的总数目,或者是发生错误则返回SOCKET_ERROR。 关闭套接字──closesocket() closesocket()关闭套接字s,并释放分配给该套接字的资源;如果s涉及一个打开的TCP连接,则该连接被释放。 closesocket()的调用格式如下: BOOLclosesocket(SOCKETs); 参数s待关闭的套接字描述符。 如果没有错误发生,closesocket()返回0。 否则返回值SOCKET_ERROR。 (7)多路复用——select() 功能: 用来检测一个或多个套接字状态。 格式: intPASCALFARselect(intnfds,fd_setFAR*readfds,fd_s
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 通信协议 网络程序设计 实验 指导书