Win32平台上的Winsock编程,Winsock是一个与协议无关的接口。以下协议是我们需要了解的:
网络协议的特征包括:
1、 面向消息
2、 面向连接和无线接
3、 可靠性和次序性
4、 从容关闭(这是指协议中断,连接不会立即中断)
5、 广播数据
6、 多播数据
7、 服务质量(QOS)
8、 部分消息(大数据进行分段发送,分段接受)
9、 路由选择(考虑协议是否可路由)
10、 字节序
11、 最大传输单元
Windows支持的协议如下图
Winsocket协议相关结构介绍
//获得系统中安装的网络协议的相关信息 int WSAEnumProtocols( _In_ LPINT lpiProtocols,// _Out_ LPWSAPROTOCOL_INFO lpProtocolBuffer,// _Inout_ LPDWORD lpdwBufferLength// );
其中的相关信息都保存在参数二当中,这个一个数据结构
1 //本机协议信息 2 typedef struct _WSAPROTOCOL_INFO { 3 DWORD dwServiceFlags1;//协议标志 4 DWORD dwServiceFlags2;// 5 DWORD dwServiceFlags3;// 6 DWORD dwServiceFlags4;// 7 DWORD dwProviderFlags;// 8 GUID ProviderId;// 9 DWORD dwCatalogEntryId;// 10 WSAPROTOCOLCHAIN ProtocolChain;// 11 int iVersion;// 12 int iAddressFamily;// 13 int iMaxSockAddr;// 14 int iMinSockAddr;// 15 int iSocketType;// 16 int iProtocol;// 17 int iProtocolMaxOffset;// 18 int iNetworkByteOrder;// 19 int iSecurityScheme;// 20 DWORD dwMessageSize;// 21 DWORD dwProviderReserved;// 22 TCHAR szProtocol[WSAPROTOCOL_LEN+1];// 23 } WSAPROTOCOL_INFO, *LPWSAPROTOCOL_INFO;//
而其中比较重要的参数是dwServiceFlags1,如下图
如何打开Socket
在可以调用一个Winsock函数之前,必须先加载一个版本正确的Winsock库。Winsock启动例程是WSAStartip();
第一个参数是准备加载的Winsock库的版本号。就目前的 Win32平台而言,Winsock2库的最新版本是 2.2。唯一的例外是 Windows CE,它只支持 Winsock1.1版。如果需要Winsock2.2版,指定这个值(0x0202)或使用宏MAKEWORD(2,2)即可。高位字节指定副版本,而低位字节则指定主版本。
第二个参数是 WSADATA结构,它是调用完成之后立即返回的。
接着使用Winsocket函数
最后清除,记住,每次调用WSACleanup,都需要调用相应的 WSACleanup,因为每次启动调用都会增加对加载 Winsock DLL的引用次数,它要求调用同样多次的 WSACleanup,以此抵消引用次数。
1 // WSAData数据结构 2 typedef struct WSAData { 3 WORD wVersion;//Winsock版本号 4 WORD wHighVersion;//最高版本号 5 char szDescription[WSADESCRIPTION_LEN+1];//Winsock说明 6 char szSystemStatus[WSASYS_STATUS_LEN+1];//系统状态信息 7 unsigned short iMaxSockets;//套接字的最大编号 8 unsigned short iMaxUdpDg;//UDP数据报的最大容量 9 char FAR *lpVendorInfo;//厂商专有信息 10 } WSADATA, *LPWSADATA;
//1.启用socket库,初始化 WSAData wsa; if (0 != WSAStartup(MAKEWORD(2,0),&wsa)) { cout<<"Socket2.0初始化失败,Exit!"<<endl; return ; } //2.创建套接字 ...... //3.绑定套接字 ..... //4.监听 ...... //5.连接 ..... //6.通信 .... //7.断开 .... //8.清除 if (!WSACleanup()) { WSAGetLastError(); return; }
SOCKET 的使用有两种定义方式
//方法1 SOCKET WSASocket( _In_ int af, _In_ int type, _In_ int protocol, _In_ LPWSAPROTOCOL_INFO lpProtocolInfo,//0表示默认的协议条目 _In_ GROUP g,//组参数始终为0 _In_ DWORD dwFlags // );
//方法2 SOCKET WSAAPI socket( _In_ int af,//地址家族 _In_ int type,//套接字类型 _In_ int protocol//协议字段 );
类型参考如
在类型里可以看到一个不常用的套接字,原始套接字。
原始套接字
raw socket,即原始套接字,可以接收本机网卡上的数据帧或者数据包,对与监听网络的流量和分析是很有作用的。
原始套接字一种通信,允许你把其他协议封装在 U D P数据包中,比如说“互联网控制消息协议”(I C M P)。I C M P的目的是投递互联网主机间的控制、错误和信息型消息。由于 I C M P不提供任何数据传输功能,因此不能把它与 U D P或T C P同等看待,但它和 I P本身属于同一个级别。
创建套接字常用的结构如下
struct sockaddr { ushort sa_family;//地址家族啊 char sa_data[14];//不同网络地址结构的大小 }; struct sockaddr_in{ short sin_family;//地址家族 unsigned short sin_port;//IP端口 struct in_addr sin_addr;//IP地址 char sin_zero[8];//填充结构,使其余SOCKETADDR的大小一致 };
还有一点需要注意在网络中传输的数据的顺序是网络字节顺序,和主机字节顺序不同,以下是几个相互转换的函数:
//将一个ip地址转换成一个32位无符号长整数。 unsigned long inet_addr( _In_ const char *cp ); //将主机字节顺序转换成网络字节顺序 u_long WSAAPI htonl( _In_ u_long hostlong ); //将主机字节顺序转换成16位网络字节顺序返回 u_short WSAAPI htons( _In_ u_short hostshort ); 一下两个刚好相反: //将16位网络字节顺序转换成主机字节顺序返回 u_short WSAAPI ntohs( _In_ u_short netshort ); //将网络字节顺序转换成主机字节顺序 u_long WSAAPI ntohl( _In_ u_long netlong );
名字解析
通过已知信息获取网络信息
//通过主机名获得主机的相关信息,返回值为hostent结构指针 struct hostent* FAR gethostbyname( _In_ const char *name ); //通过主机名获得主机ip地址的相关信息,返回值为hostent结构指针 struct hostent* FAR gethostbyaddr( _In_ const char *addr, _In_ int len, _In_ int type ); //hostent结构 typedef struct hostent { char FAR *h_name;//正式主机名 char FAR FAR **h_aliases;//主机的别名 short h_addrtype;//ip地址类型 short h_length;//IP地址长度 char FAR FAR **h_addr_list;//主机的IP地址(网络字节顺序) } HOSTENT, *PHOSTENT, FAR *LPHOSTENT; //获得已知服务的端口号 struct servent* FAR getservbyname( _In_ const char *name,//服务名 _In_ const char *proto//随便指向一个字串,这个字串表明name中的服务是在这个参数中的协议下面注册的。 );