Winsock网络编程1.docx
- 文档编号:30670491
- 上传时间:2023-08-19
- 格式:DOCX
- 页数:33
- 大小:494.01KB
Winsock网络编程1.docx
《Winsock网络编程1.docx》由会员分享,可在线阅读,更多相关《Winsock网络编程1.docx(33页珍藏版)》请在冰豆网上搜索。
Winsock网络编程1
Winsock网络编程
一、相关概念
●网络协议
TCP/IP(传输控制协议/网际协议)是业界标准的协议组。
它是基于四层参考模型,属于TCP/IP协议组的所有协议都位于该模型的上面三层。
TCP/IP模型的每一层对应于国际标准化组织(ISO)提议的七层“开放系统互联(OSI)”参考模型的一层或者多层。
●TCP/IP协议描述
层
描述
协议
应用层
定义了TCP/IP应用协议以及主机程序与要使用网络的传输层服务之间的接口
HTTP,Telnet,FTP,TFTP,
SNMP,DNS,SMTP等
传输层
提供主机之间的通讯会话管理,定义传输数据时的服务级别以及连接状态
TCP,UDP,RTP
网络层
将数据装入IP数据包,包括用于在主机间以及经过网络转发数据包时所用的源和目标地址信息。
实现IP数据包的路由
IP,ICMP,ARP,RARP
网络接口层
指定如何通过网络物理地发送数据,包括直接与网络媒体直接接触的硬件设备如何将比特流转换为电信号
以太网,帧中继,
令牌环,ATM
●TCP/IP核心协议
1、地址解析协议(ARP):
实现IP地址到物理地址的转换;
2、网际协议(IP):
是无连接的、不可靠的数据报协议,主要负责主机之间的寻址和选择数据包的路由;
3、网际消息协议(ICMP):
通过ICMP,使用IP通信的主机和路由器可以报告错误并交换受限控制和状态信息;
4、用户数据报协议(UDP):
在主机之间提供轻便、快捷、不可靠地传输数据;
5、传输控制协议(TCP):
提供可靠的、面向连接的数据报传递服务。
●UDP/TCP比较
UDP
TCP
无连接的服务:
在主机间不建立会话
面向连接的服务:
在主机间建立会话
UDP不能确保或承认数据传递或序列化数据
TCP通过确认和按照顺序传递数据来确保数据的传递
使用UDP的程序负责提供数据传递的可靠性
TCP确保数据的可靠传输
UDP非常快,具有低开销要求,支持点对点或者一点对多点的通信
TCP比较慢,具有更高的开销要求,只支持点对点通信。
●Windows网络编程的三种模式:
1、阻塞套接字模式
2、异步套接字模式(基于消息的非阻塞套接字模式)
3、MFC异步套接字模式
二、使用阻塞套接字模式进行Windows网络编程
1、WindowsSockets通信程序开发的基本步骤:
见《TCP_UDP网络编程步骤.xls》
2、相关函数说明
●WSAStartup:
intWSAStartup(
WORDwVersionRequested,
LPWSADATAlpWSAData
);
wVersionRequested:
用于指定准备加载的Winsock库的版本。
高位字节指定所需要的Winsock库的副版本,而低位字节则是主版本。
可用MAKEWORD(x,y)(其中,x是高位字节,y是低位字节)方便地获得wVersionRequested的正确值。
lpWSAData:
是指向WSADATA结构的指针,WSAStartup用其加载的库版本有关的信息填在这个结构中。
WSADATA结构定义如下:
typedefstructWSAData{
WORDwVersion;
WORDwHighVersion;
charszDescription[WSADESCRIPTION_LEN+1];
charszSystemStatus[WSASYS_STATUS_LEN+1];
unsignedshortiMaxSockets;
unsignedshortiMaxUdpDg;
charFAR*lpVendorInfo;
}WSADATA,*LPWSADATA;
WSAStartup把第一个字段wVersion设成打算使用的Winsock版本。
wHighVersion参数容纳的是现有的Winsock库的最高版本。
记住,这两个字段中,高位字节代表的是Winsock副版本,而低位字节代表的则是Winsock主版本。
szDescription和szSystemStatus这两个字段由特定的Winsock实施方案设定,事实上没有用。
不要使用下面这两个字段:
iMaxSockets和iMaxUdpDg,它们是假定同时最多可打开多少套接字和数据报的最大长度。
然而,要知道数据报的最大长度应该通过WSAEnumProtocols来查询协议信息。
同时最多可打开套接字的数目不是固定的,很大程度上和可用物理内存的多少有关。
最后,lpVendorInfo字段是为Winsock实施方案有关的指定厂商信息预留的。
任何一个Win32平台上都没有使用这个字段。
如果WinSock.dll或底层网络子系统没有被正确初始化或没有被找到,WSAStartup将返回WSASYSNOTREADY。
此外这个函数允许你的应用程序协商使用某种版本的WinSock规范,如果请求的版本等于或高于DLL所支持的最低版本,WSAData的wVersion成员中将包含你的应用程序应该使用的版本,它是DLL所支持的最高版本与请求版本中较小的那个。
反之,如果请求的版本低于DLL所支持的最低版本,WSAStartup将返回WSAVERNOTSUPPORTED。
关于WSAStartup更详细的信息,请查阅MSDN中的相关部分。
对于每一个WSAStartup的成功调用(成功加载WinSockDLL后),在最后都对应一个WSACleanUp调用,以便释放为该应用程序分配的资源。
●socket
SOCKETsocket(
intaf,
inttype,
intprotocol
);
af:
指定地址族,对于TCP/IP协议的套接字,它只能是AF_INET(也可写成PF_INET)。
type:
指定Socket类型,对于1.1版本的Socket,它只支持两种类型的套接字,SOCK_STREAM指定产生流式套接字,SOCK_DGRAM产生数据报套接字。
protocol:
是与特定的地址家族相关的协议,如果指定为0,那么它就会根据地址格式和套接字类别,自动为你选择一个合适的协议。
这是推荐使用的一种选择协议的方法。
如果函数调用成功,将返回一个新的SOCKET数据类型的套接字描述符。
如果调用失败,这个函数就会返回一个INVALID_SOCKET,错误信息可以通过WSAGetLastError函数返回。
●bind
intbind(
SOCKETs,
conststructsockaddrFAR*name,
intnamelen
);
s:
指定要绑定的套接字
name:
指定了该套接字的本地地址信息,是指向sockaddr结构的指针变量,由于该地址结构是为所有的地址家族准备的,这个结构可能(通常会)随所使用的网络协议不同而不同,所以,要用第三个参数namelen指定该地址结构的长度
namelen:
指定该地址结构的长度。
sockaddr结构定义如下:
structsockaddr{
u_shortsa_family;
charsa_data[14];
};
sockaddr的第一个字段sa_family指定该地址家族,在这里必须设为AF_INET。
sa_data仅仅是表示要求一块内存分配区,起到占位的作用,该区域中指定与协议相关的具体地址信息。
由于实际要求的只是内存区,所以对于不同的协议家族,用不同的结构来替换sockaddr。
除了sa_family外,sockaddr是按网络字节顺序表示的。
在TCP/IP中,我们可以用sockaddr_in结构替换sockaddr,以方便我们填写地址信息。
sockaddr_in的定义如下:
structsockaddr_in{
shortsin_family;//AF_INET
unsignedshortsin_port;//htons(6000)
structin_addrsin_addr;//sin_addr.S_un.S_addr=htonl(INADDR_ANY)
charsin_zero[8];
};
其中,sin_family表示地址族,对于IP地址,sin_family成员将一直是AF_INET。
成员sin_port指定的是将要分配给套接字的端口。
使用htos(6000)转换成网络字节顺序
成员sin_addr给出的是套接字的主机IP地址。
使用addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);这种形式将其转换成网络字节顺序。
成员sin_zero只是一个填充数,以使sockaddr_in结构和sockaddr结构的长度一样。
网络字节顺序:
不同的计算机存放多字节值的顺序不同,有的机器在起始地址存放低位字节(低位先存),有的机器在起始地址存放高位字节(高位先存)。
基于Intel的CPU,即我们常用的PC机采用的是低位先存。
为保证数据的正确性,在网络协议中需要指定网络字节顺序。
TCP/IP协议使用16位整数和32位整数的高位先存格式。
上面的htos()和htonl()函数就是相关的转换函数。
如果bind函数调用成功,它将返回0。
如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。
将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。
多数情况下,每个机器只有一个IP地址,但有的机器可能会有多个网卡,每个网卡都可以有自己的IP地址,用INADDR_ANY可以简化应用程序的编写。
将地址指定为INADDR_ANY,允许一个独立应用接受发自多个接口的回应。
如果我们只想让套接字使用多个IP中的一个地址,就必须指定实际地址,要做到这一点,可以用inet_addr()函数,这个函数需要一个字符串作为其参数,该字符串指定了以点分十进制格式表示的IP地址(如192.168.0.16)。
而且inet_addr()函数会返回一个适合分配给S_addr的u_long类型的数值。
inet_ntoa()函数会完成相反的转换,它接受一个in_addr结构体类型的参数并返回一个以点分十进制格式表示的IP地址字符串。
3、实例1TCP实现了一个最原始的有连接的单次网络通讯
4、实例2UDP实现了一个最原始的无连接的单次网络通讯
5、实例3UDP_Chat改善实例2UDP而来
6、实例4Chat_Thread是使用多线程技术改善实例2UDP而来
三、使用异步套接字模式进行Windows网络编程
所谓异步套接字,是指基于消息的、非阻塞模式的套接字。
Windows套接字可以在两种模式下执行I/O操作,即阻塞模式和非阻塞模式。
在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回程序(即不会将控制权立即交还给程序)。
而在非阻塞模式下,Winsock函数无论如何都会立即返回。
WindowsSockets为了支持Windows消息驱动机制,使应用程序开发者能够方便地处理网络通信,它对网络事件采用了基于消息的异步存取策略。
该异步策略主要是通过WindowsSockets的异步选择函数WSAAsyncSelect()来实现的,该函数提供了消息机制的网络事件选择,当使用它登记的网络事件发生时,Windows应用程序相应的窗口函数将收到一个消息,消息中指示了发生的网络事件,以及与事件相关的一些信息。
相关函数说明
intWSAEnumProtocols(
LPINTlpiProtocols,
LPWSAPROTOCOL_INFOlpProtocolBuffer,
ILPDWORDlpdwBufferLength
);
Win32平台支持多种不同的网络协议,采用Winsock2,就可以编写可直接使用任何一种协议的网络应用程序了。
通过WSAEnumProtocols函数可以获得系统中安装的网络协议的相关信息。
lpiProtocols,一个以NULL结尾的协议标识号数组。
这个参数是可选的,如果lpiProtocols为NULL,则返回所有可用协议的信息,否则,只返回数组中列出的协议信息。
lpProtocolBuffer,[out],一个用WSAPROTOCOL_INFO结构体填充的缓冲区。
WSAPROTOCOL_INFO结构体用来存放或得到一个指定协议的完整信息。
lpdwBufferLength,[in,out],在输入时,指定传递给WSAEnumProtocols()函数的lpProtocolBuffer缓冲区的长度;在输出时,存有获取所有请求信息需传递给WSAEnumProtocols()函数的最小缓冲区长度。
这个函数不能重复调用,传入的缓冲区必须足够大以便能存放所有的元素。
这个规定降低了该函数的复杂度,并且由于一个机器上装载的协议数目往往是很少的,所以并不会产生问题。
SOCKETWSASocket(
intaf,
inttype,
intprotocol,
LPWSAPROTOCOL_INFOlpProtocolInfo,
GROUPg,
DWORDdwFlags
);
前三个参数和socket()函数的前三个参数含义一样。
lpProtocolInfo,一个指向WSAPROTOCOL_INFO结构体的指针,该结构定义了所创建的套接字的特性。
如果lpProtocolInfo为NULL,则WinSock2DLL使用前三个参数来决定使用哪一个服务提供者,它选择能够支持规定的地址族、套接字类型和协议值的第一个传输提供者。
如果lpProtocolInfo不为NULL,则套接字绑定到与指定的结构WSAPROTOCOL_INFO相关的提供者。
g,保留的。
dwFlags,套接字属性的描述。
intWSARecvFrom(
SOCKETs,
LPWSABUFlpBuffers,
DWORDdwBufferCount,
LPDWORDlpNumberOfBytesRecvd,
LPDWORDlpFlags,
structsockaddrFAR*lpFrom,
LPINTlpFromlen,
LPWSAOVERLAPPEDlpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINElpCompletionRoutine
);
s,标识套接字的描述符。
lpBuffers,[in,out],一个指向WSABUF结构体的指针。
每一个WSABUF结构体包含一个缓冲区的指针和缓冲区的长度。
dwBufferCount,lpBuffers数组中WSABUF结构体的数目。
lpNumberOfBytesRecvd,[out],如果接收操作立即完成,则为一个指向本次调用所接收的字节数的指针。
lpFlags,[in,out],一个指向标志位的指针。
lpFrom,[out],可选指针,指向重叠操作完成后存放源地址的缓冲区。
lpFromlen,[in,out],指向from缓冲区大小的指针,仅当指定了lpFrom才需要。
lpOverlapped,一个指向WSAOVERLAPPED结构体的指针(对于非重叠套接字则忽略)。
lpCompletionRoutine,一个指向接收操作完成时调用的完成例程的指针(对于非重叠套接字则忽略)。
intWSASendTo(
SOCKETs,
LPWSABUFlpBuffers,
DWORDdwBufferCount,
LPDWORDlpNumberOfBytesSent,
DWORDdwFlags,
conststructsockaddrFAR*lpTo,
intiToLen,
LPWSAOVERLAPPEDpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINECompletionRoutine
);
s,标识一个套接字(可能已连接)的描述符。
lpBuffers,一个指向WSABUF结构体的指针。
每一个WSABUF结构体包含一个缓冲区的指针和缓冲区的长度。
dwBufferCount,lpBuffers数组中WSABUF结构体的数目。
lpNumberOfBytesSent,[out],如果发送操作立即完成,则为一个指向本次调用所发送的字节数的指针。
dwFlags,指示影响操作行为的标志位。
lpTo,可选指针,指向目标套接字的地址。
iToLen,lpTo中地址的长度。
lpOverlapped,一个指向WSAOVERLAPPED结构的指针(对于非重叠套接字则忽略)。
lpCompletionRoutine,一个指向接收操作完成时调用的完成例程的指针(对于非重叠套接字则忽略)。
实例5Chat_WSA
四、使用MFC异步套接字模式进行Windows网络编程
MFC对Sockect的封装:
CSockect的继承关系:
CObject
CSyncSockect
CSockect
MFC对CSockect的封装采用如下步骤:
服务器
客户端
//构造一个CSockect对象sockect
CSockectsockSrvr;
//构造一个CSockect对象sockect
CSockectsockClient;
//生成sockect
sockSrvr.Create(nPort);
//生成sockect
sockClient.Create();
//进行监听
sockSrvr.Listen();
//实现链接
sockClient.Connect(strAddr,nPort)
//构造新的sockect
sockSrvr.Accept(sockRecv);
//构造文件对象
CSockFilefile(&sockRecv);
//构造文件对象
CSockFilefile(&sockClient);
//构造CArchive对象
CArchive.arIn(&file,CArchive:
:
load)
//构造CArchive对象
CArchive.arIn(&file,CArchive:
:
load)
//使用CArchive传送数据
arIn>>dwValue;
//使用CArchive传送数据
arIn>>dwValue;
具体代码实现实例:
非面向连接的SocketsMFC例子
1建立基于对话框的MFC工程Socketa,记住选择Sockect支持;
2设计一个对话框,用来设置IP地址
3添加类CsocketSer,选择基类为CAsyncSocket
相关函数代码如下:
SockectaDlg.cpp
//SockectaDlg.cpp:
implementationfile
#include"stdafx.h"
#include"Sockecta.h"
#include"IPSetDlg.h"
#include"SockectaDlg.h"
#ifdef_DEBUG
#definenewDEBUG_NEW
#undefTHIS_FILE
staticcharTHIS_FILE[]=__FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
//CAboutDlgdialogusedforAppAbout
classCAboutDlg:
publicCDialog
{
public:
CAboutDlg();
//DialogData
//{{AFX_DATA(CAboutDlg)
enum{IDD=IDD_ABOUTBOX};
//}}AFX_DATA
//ClassWizardgeneratedvirtualfunctionoverrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtualvoidDoDataExchange(CDataExchange*pDX);//DDX/DDVsupport
//}}AFX_VIRTUAL
//Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg:
:
CAboutDlg():
CDialog(CAboutDlg:
:
IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
voidCAboutDlg:
:
DoDataExchange(CDataExchange*pDX)
{
CDialog:
:
DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg,CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
//Nomessagehandlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
//CSockectaDlgdialog
CSockectaDlg:
:
CSockectaDlg(CWnd*pParent/*=NULL*/)
:
CDialog(CSockectaDlg:
:
IDD,pParent)
{
//{{AFX_DATA_INIT(CSockectaDlg)
//NOTE:
theClassWizardwilladdmemberinitializationhere
//}}AFX_DATA_INIT
//NotethatLoadIcondoesnotrequireasubsequentDestroyIconinWin32
m_hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
voidCSockectaDlg:
:
DoDataExchange(CDataExchange*pDX)
{
CDialog:
:
DoDataExchange(pDX);
//{{AFX_DATA_MAP(CSock
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Winsock 网络 编程