实验讲稿(2010) - 东南大学自动化学院

Download Report

Transcript 实验讲稿(2010) - 东南大学自动化学院

《信息通信网络概论》课程实验
东南大学自动化学院
网络编程实验
Windows Socket 编程基础知识
实验一
实验二、三
实验四
实验五、六
Windows Socket 网络编程
Windows Socket 简介
套接字编程基础
Windows Socket 编程原理
Socket 编程步骤
一、Windows Socket的由来
加利福尼亚大学伯克利分校为UNIX系统开发出了伯克
利套接字(BSD socket),在此基础上扩展形成了
windows套接字。
Windows Socket 规范是一套开放的、支持多协议的
Windows 下的网络编程接口,它规范了Internet协议族
(IPS,一般为TCP/IP)的API使用.
针对多样的网络协议,Windows Socket 统一了操作,简化
了编程,使两个进程之间实现连接、通信。
二、windows socket 的版本
Windows Socket 规范主要有两种版本即1.1和2.0
版。 主要区别:1.1版本只支持TCP/IP协议,2.0版本
可以支持多协议
三、编程时的加载事项
•需要包含头文件Winsock2.h,需要使用库ws2_32.lib,
包含办法可以用语句来告诉编译时调用该库
#pragma comment(lib,”ws2_32.lib”);
如果使用Visual C++ 6.0,可以通过“工程” > “设
置”>“工程设置”>“链接”>“对象/库模块”中加入
“ws2_32.lib”
• WinSock是TCP/IP编程最低级的Windows API,其代码的一
部分位于Winsock32.dll中,另一部分位于Windows核心,
使用Windows API可以编写Internet 服务器和客户端程序。
应用程序调用Windows Socket 的API实现相互之间的通信
(应用程序与Windows Socket关系图)
应用程序1
应用程序2
网络编辑界面,如Windows Sockets
操作系统为保证其安全性
可靠性不允许用户直接使
用
网络通信协议服务,如TCP/IP
操作系统,如Windows
物理通信介质
应用程序与Windows Socket 关系图
返回
套接字有三种类型
数据报套接字(SOCK_DGRAM)——一种无连接的服
务,数据通过相互独立的报文进行传输,是无序的,
并且不保证可靠、无差错 (时序图)
流式套接字(SOCK_STREAM)——一种可靠的面向连接
的服务,实现了无差错无重复的顺序数据传输 (时
序图)
原始套接字(SOCK_RAW)——允许对底层协议如IP或
ICMP(因特网控制消息协议)直接访问,主要用于
新的网络协议实现的测试等
返回
socket
创建套接字
bind
绑定本机接口
connect
建立连接
listen
监听端口
accept
接受连接
recv,recvfrom
数据接收
send,sendto
数据发送
close,shutdown
关闭套接字
IP地址:Internet中的主机要与别的机器通信必须具有
一个IP地址,IP地址是Internet中主机的标识。
表示形式:常用点分形式,如202.38.64.10,最后都会
转换为一个32位的整数。
IP地址转换函数
inet_addr() 点分十进制数表示的IP地址转换
为网络字节序的IP地址
inet_ntoa() 网络字节序的IP地址转换为点分
十进制数表示的IP地址
端口号
为了区分一台主机接收到的数据包应该递交
给哪个进程来进行处理,使用端口号
TCP端口号与UDP端口号独立
端口号一般由IANA (Internet Assigned Numbers
Authority) 管理
众所周知端口:1~1023,1~255之间为大部分众所
周知端口,256~1023端口通常由UNIX占用
注册端口:1024~49151
动态或私有端口:49151~65535
如果把IP数据包的投递过程看成是给远方的一位朋
友寄一封信,那么
IP地址就是这位朋友的所在位置,如安徽合肥中国
科大计算系(依靠此信息进行路由)
端口号就是这位朋友的名字(依靠这个信息最终
把这封信交付给这位收信者)
字节序
大尾端(Big-Endian):字节的高位在内存中放在存储单元的
起始位置
00001010
10010111
00001111
10001000
Memory
小尾端(Little-Endian):与大尾端相反
00001010
A
10010111
A+1
00001111
A+2
10001000
A+3
网络字节序(NBO,Network Byte Order)
使用统一的字节顺序,避免兼容性问题
主机字节序(HBO,Host Byte Order)
不同的机器HBO是不一样的,这与CPU的设计有关
Motorola 68K系列,HBO与NBO是一致的
Intel X86系列,HBO与NBO不一致
字节排序函数
htonl 4字节主机字节序转换为网络字节序
ntohl 4字节网络字节序转换为主机字节序
htons 2字节主机字节序转换为网络字节序
ntohs 2字节网络字节序转换为主机字节序
阻塞通信与非阻塞通信
阻塞方式:套接字进行I/O操作时,函数要等待到相
关的操作完成以后才能返回,对提高处理机的利用
率不利,但编程简单。
非阻塞方式:套接字进行I/O操作时,无论操作成功
与否,调用都会立即返回。
阻塞方式编程简单,一个套接口的默认操作模式为
阻塞,可以调用函数ioctlsocket()进行设置。
面向连接的C/S网络通信程序工作流程图(TCP)
WSAStartup()
socket()
bind()
WSAStartup()
listen()
socket()
等待客户连接请求的到来
三次握手过程建立TCP连接
connect()
accept()
recv()
send()
closesocket()
交换数据
交换数据
关闭TCP连接
send()
recv()
closesocket()
WSACleanup()
WSACleanup()
服务器端
客户端
返回
无连接的C/S网络通信程序工作流程图(UDP)
WSAStartup()
WSAStartup()
socket()
socket()
bind()
bind()
recvfrom()
sendto()
交换数据
交换数据
这个bind可以是
隐式的。
sendto()
recvfrom()
closesocket()
closesocket()
WSACleanup()
WSACleanup()
服务器
客户端
返回
注意事项:
无连接的数据报传输过程中,作为服务器的
一方必须先启动
通信的一方可以不用bind()绑定地址和端口
,由系统分配
不绑定IP地址和端口号的一方必须首先向绑
定地址的一方发送数据
无连接客户端一般不调用connect(),在数
据发送前客户与服务器各自通过socket()和
bind()建立了半相关,发送数据时除指定本
地套接口的地址外,还需要指定接收方套接
口地址,从而在数据收发过程中动态建立全
连接
返回
为了支持Windows的消息驱动机制,WinSock和BSD套接口
相比有以下一些扩充:
异步选择机制
异步选择函数WSAAsyncSelect()允许应用程序提名一个或多个感兴趣
异步相比与同步提高了性能更具优势,不需要
的网络事件,如FD_READ、FD_WRITE、FD_CONNECT、FD_ACCEPT等代表
在接收完一个网络事件完后等待其处理完成,
的网络事件
而可以继续接收其他网络事件。CAsyncSocket
异步请求函数
异步请求函数允许应用程序用异步方式获得请求的信息,如
WSAAsyncGetXByY()类函数,这些函数是对BSD标准函数的扩充。函数
WSACancelAsyncRequest()允许用户终止一个正在执行的异步请求
阻塞处理方法
WinSock提供了“钩子函数”负责处理Windows消息,使Windows的消
息循环能够继续。WinSock提供了两个函数(WSASetBlockingHool()和
WSAUnhookBlockingHook())让应用程序设置或取消自己的“钩子函数”。函
数WSAIsBlocking()可以检测是否阻塞,函数WSACancelBlockingCall()可以取消
一个阻塞的调用
错误处理
WinSock提供了两个函数WSAGetLastError()和WSASetLastError()来获取
和设置最近的错误号
启动和终止
Winsock 的启动和终止
应用程序在使用Windows Sockets Dll之
前必须先调用启动函数WSAStartup()。该
函数的功能有两个:一是由应用程序指
定所要求的Windows Sockets Dll 版本;二
是获得系统Windows Sockets Dll的一些细节。
调用终止函数WSACleanup()来终止
Windows Sockets DLL。
返回
Socket 编程过程
(主要讲解使用Socket的过程)
使用WSAStartup()函数检查系统协议栈安装情况
使用Socket()函数创建套接口
socket_handle=socket(protocol_family.Socket_type,protocol);
配置Socket
配置一个socket需要五种信息:



本地和远地机的IP地址
本地和远地进程的协议端口
连接所使用的协议
使用Socket发送或接收数据
使用send或sendto方法发送数据,recv或recvfrom方法接收数据。
使用WSACleanup()函数关闭与Windows Sockets DLL 的
连接
返回
实验一
实验内容:使用Socket 函数编写一个小程序实
现查询主机地址即输入IP地址能够给出该IP地
记录主机信息,包括主机
名、别名、地址类型、
址对应的域名;给出域名可以给出IP地址。
地址列表等。
实验要求:理解掌握WSAStartup()、WSACleanup()
函数的使用;知道WSADATA、HOSTENT结构成
员表示意义和inet_ntoa()、inet_addr()函数的使用。
IP地址的点分十进制字符串(如"4.3.2.16")
主要步骤:
与IN_ADDR 结构体之间的相互转化
1、网络程序初始化,调用其它WinSock函数之前先使用
WSAStartup()函数初始化
2、通过WinSock发送和接收数据
3、程序结束必须关闭Socket,使用WSACleanup()释放所
分配的内部
缓冲区和其他资源。
int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);
函数说明:
① wVersionRequested是应用程序对Windows Sockets DLL版本要求。高
字节代表次版本号,低字节代表主版本号。如定义
wVersionRequested=0x0002,则表示应用程序对DLL的最低要求为
2.0版本
②lpWSAData用于返回Windows Sockets DLL的一些技术细节,是指
向WSAData结构的一个指针。其中WSAData的成员变量
wHighVersion指明DLL支持的最高版本,成员变量wVersion代表
wHighVersion与wVersionRequested的最小值,也就是DLL希望用
户使用的版本号
③ 函数调用成功,返回值0;若失败,则返回错误码,可用
WSAGetLastError()查看
下面举例说明函数的使用
Windows Sockets DLL的初始化过程
WORD wVersionRequested;
WSADATA wsaData;
Int err;
wVersionRequested=0x0002;
Err=WSAStartup(wVersionRequested,&wsaData);
If(err!=0)
{
/*通知用户找不到可用的Winsock DLL*/
Return;
}
If(wsaData.wVersion!=0x0002)
{
/*通知用户找不到可用的Winsock DLL,因为当前Winsock版本低于2.0*/
WSACleanup();
Return;
}
/*协商成功,启动了DLL*/
实验一界面样例
返回
实验二、三
实验内容:编写一个连接FTP程序,实现检索
FTP文件服务器、上传文件、下载文件。
理解FTP协议是非对称的协议。
实验要求:理解掌握CInternetSession()、
GetFtpConnection()、 CFtpFileFind()、GetFile()、
PutFile()、 FindNextFile()函数的使用
类、函数表单:
CInternetSession
用于建立一个Internet会话对象
GetFtpConnection()
用于建立连接(使用该函数时要进行
异常处理)
CFtpFileFind(包括两虚成员 用于建立文件查找类
函数FindFile()和FindNextFile())
GetFile()
用于下载文件
PutFile()
用于上传文件
FindNextFile()
用于查找下一个文件,与
CFtpFileFind::FindFile()配合使用;
主要步骤:
1、创建一个INTERNET会话( CInternetSession())
2、建立与FTP服务器的连接——CFtpConnection对象
如果连接成功,获得当前登陆的缺省目录下所有的文
件和目录名称,即如何检索某一个目录下的文件,并
显示文件信息
3、下载文件或上传文件
4、关闭连接
举例说明一下一些函数的使用
CInternetSession sess(_T(“MyProgram/1.0”)); //建立会话
CFtpConnection* pConnect = NULL;
try //需要进行异常处理
{
//建立ftp连接
pConnect = sess.GetFtpConnection(_T("ftp.microsoft.com"));
//创建CFtpFileFind对象
//利用函数FindFile()以及FindNextFile()查找并显示所有文件
CFtpFileFind finder(pConnect);
BOOL bWorking = finder.FindFile(_T("*"));
while (bWorking)
{
bWorking = finder.FindNextFile();
printf("%s\n", (LPCTSTR) finder.GetFileURL());
}
}
catch (CInternetException* pEx) //注意异常类型
{
TCHAR sz[1024];
pEx->GetErrorMessage(sz, 1024);
printf("ERROR! %s\n", sz);
pEx->Delete();
}
实验二界面样例
返回
实验四:聊天工具
实验目的
实验内容及要求
实验步骤
注意事项
实验目的
在前面两个实验的基础上进一步了解网
络编程的过程。
掌握Windows环境下基于WinSock的编程方
法和通讯实现。
编写一个聊天程序,即以客户端和服务
器的模式进行互发消息。
back
实验内容及要求
利用MFC中的CAsyncsocket类已经集成了socket的基本函
数,我们可以直接使用其成员函数来建立,初始化并
应用socket,利用客户端和服务器模式,编写一个点对
点的,可以相互发送和接收消息的程序,也就是常说
的聊天工具。
该工具具有既可以作为服务器也可以做为客户端使用。
当作为服务器时要具有能输入自己的IP值和端口值,
并能进行在线监听是否有客户端要求连接,如果有,
还要能进行适当的响应,也就是接受连接请求建立连
接,并实现消息互发。
当作为客户端是,要求能输入远地服务器的IP值和
端口数,并进行连接请求,等待远地服务器的响应,
当连接上后要能与服务器进行互发消息,聊天结束后
还要实现与服务器的断开的功能。
back
实验步骤(1)
建立自己的socket类
使用MFC 中的CAsyncsocket类作为基类重载虚拟函数建立自己
的socket类。 Eg.:class CMySocket : public CAsyncSocket
然后重载函数
void CMySocket::OnAccept()
void CMySocket::OnClose()
void CMySocket::OnConnect()
void CMySocket::OnReceive()
这些函数系统会在有网络消息的时候自动调用它们,在下面的实
验步骤(2)中提到要使用的函数就是在这些重载函数中调用的。
(例子)我们还可以在这些函数中加入自己的代码,实现自己想
要得功能。
最后还要在这个类中加入一个指向对话框类的指针作为成员变
量,我们就是利用这个对话框类的成员函数来重载上面的虚拟函
数的。
实验步骤(2)
使用的函数
服务器端:
•建立Socket:调用 成员函数Socket()函数
•绑定端口:调用成员函数bind()函数
(注:对于MFC编程我们只要调用Create()函数就可以包括上面的两
个函数)
• 监听:调用成员函数listen()函数
• 服务器端接受客户端的连接请求:调用成员函数accept() 函数
•结束 socket 连接 :调用成员函数closesocket()
客户端:
•建立客户端的 Socket :调用 socket() 函数
(注:我们也是调用Create()函数来完成Socket的建立的)
• 提出连接申请 :调用成员函数connect()函数
以上这些函数我们是在重载前面的虚拟函数时使用的,例如
OnAccept()函数中调用accept() 函数等等。
实验步骤(3)
制作界面对话框类
使用的控件:
List box,用于显示已发出和已接收的消息。
Combo box(or Radio button),用于模式选择,客户机
或服务器。
Edit box,用于输入IP和端口。
Button,控制命令按钮。
注意:要在对话框类中添加两个我们自己的socket类作
为成员变量。因为当作为服务器时要有两个套接字。
一个用于侦听连接请求,一个用于被连接到另一个应
用程序。当然作为客户机时只要一个套接字就可以了。
界面例子
back
注意事项(1)
两个MFC函数
BOOL AfxSocketInit( WSADATA* lpwsaData =
NULL );这个函数中调用::WSAStartup在你的程序
退出的时候自动调用::WSACleanup
创建工程时别忘了
CWinApp::InitInstance ()
函数中调用这个函数来初
始化
int WSAAsyncSelect( SOCKET s, HWND hWnd,
unsigned int wMsg, long lEvent ); 当有网络事件发生时
系统会通知窗口并调用对应的函数。我们要做的就是
重载这些回调函数,加入我们要的指令实现我们要的
功能。
注意事项(2)
消息发送函数int Send( const void* lpBuf, int nBufLen,
int nFlags = 0 );
在使用这个函数之前要先判断消息发送编辑框是否有消息,如果是
空则不发送,用CString.IsEmpty()函数进行判断,当不为空的时候返
回0,否则返回非0。当发送函数调用成功时返回发送数据的长度,否则
返回SOCKET_ERROR.
消息接受函数Receive( void* lpBuf, int nBufLen, int
nFlags = 0 );
当该函数调用成功时返回接收到的数据的长度,否则返回
SOCKET_ERROR。我们可以事先分配一个大小为1025的数组用来缓
存接收数据,当接收成功后要在这个数组最后加上NULL,即字符串的
结束符,并将之转化为字符串类型,然后就可以使之在列表框中显示了。
注意事项(3)
实现控件的禁用和启用函数EnableWindow(TRUE/FALSE),
这个函数在我们这个实验中经常用到,使用这个函数
我们就可以实现当程序在某种状态下禁止一些不相关
的控件的使用,这样就可以避免用户的一些不必要的
误操作。
back
网络消息
意义
调用相应的函数
FD_READ
欲接收读准备好
的通知
CAsyncSocket::OnRe
ceive
FD_WRITE
欲接收写准备好
的通知
CAsyncSocket::OnSe
nd
FD_ACCEPT
欲接收将要连接
的通知
CAsyncSocket::OnAc
cept
FD_CONNECT
欲接收已连接好
的通知
CAsyncSocket::OnCo
nnect
FD_CLOSE
欲接收套接口关
闭的通知
CAsyncSocket::OnCl
ose
back
back
back
back
back
实验五、六:设计协议
实验内容:在Windows网络环境下,以其中的2台计算
机为对象,构成主从计算机应用系统,设计简单的应
用层协议,开发基于TCP/IP或UDP/IP的网络通信程序,
实现数据传送和文件传输。
实验要求:正确理解应用层协议的概念;更深入了解
客户/服务器模式的网络编程设计。
实验步骤及所需函数:参考实验二、三、四。
基于TCP的SOCKET编程
服务器端
1:创建套接字(socket)
2:将套接字绑定到一个本地地址和端口上(bind)
3:将套接字设为监听模式,准备接受客户请求(listen
)
4:等待客户请求到来;请求到来后,接受请求,返回
一个新的对应于此次连接的套接字(accept)
5:用返回的套接字和客户端进行通信(send/recv)
6:返回,等待另一客户请求
7:关闭套接字
客户端
1:创建套接字(socket)
2:向服务器发出连接请求(connect)
3:和服务器进行通信(send/recv)
4:关闭套接字
基于UDP的socket编程
服务器端
1:创建套接字(socket)
2:将套接字绑定到一个本地地址和端口
上(bind)
3:等待接收数据(recvfrom)
4:关闭套接字
客户端
1.创建套接字(socket)
2.向服务器发送数据(sendto)
3.关闭套接字