免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1151 | 回复: 0
打印 上一主题 下一主题

Winsock编程入门 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-06-12 22:17 |只看该作者 |倒序浏览

什么是Winsock
    Winsock是Windows下的网络编程接口,它是由Unix下的BSD Socket发展而来,是一个与网络协议无关的编程接口。
构建编程环境
    Winsock在常见的Windows平台上有两个主要的版本,即Winsock1和Winsock2。编写与Winsock1兼容的程序你需要引用头文件WINSOCK.H,如果编写使用Winsock2的程序,则需要引用WINSOCK2.H。此外还有一个MSWSOCK.H头文件,它是专门用来支持在Windows平台上高性能网络程序扩展功能的。使用WINSOCK.H头文件时,同时需要库文件WSOCK32.LIB,使用WINSOCK2.H时,则需要WS2_32.LIB,如果使用MSWSOCK.H中的扩展API,则需要MSWSOCK.LIB。正确引用了头文件,并链接了对应的库文件,你就构建起编写WINSOCK网络程序的环境了。
初始化Winsock
    每个Winsock程序必须使用WSAStartup载入合适的Winsock动态链接库,如果载入失败,WSAStartup将返回SOCKET_ERROR,这个错误就是WSANOTINITIALISED,WSAStartup的定义如下:
int WSAStartup(
    WORD wVersionRequested,
    LPWSADATA lpWSAData
);
wVersionRequested指定了你想载入的Winsock版本,其高字节指定了次版本号,而低字节指定了主版本号。你可以使用宏MAKEWORD(x, y)来指定版本号,这里x代表主版本,而y代表次版本。lpWSAData是一个指向WSAData结构的指针,WSAStartup会向该结构中填充其载入的Winsock动态链
库的信息。
lpWSAData是一个指向WSAData结构的指针,WSAStartup会向该结构中填充其载入的Winsock动态链
库的信息。
lpWSAData是一个指向WSAData结构的指针,WSAStartup会向该结构中填充其载入的Winsock动态链
库的信息。
typedef struct WSAData
{
    WORD           wVersion;
    WORD           wHighVersion;
    char           szDescription[WSADESCRIPTION_LEN + 1];
    char           szSystemStatus[WSASYS_STATUS_LEN + 1];
    unsigned short iMaxSockets;
    unsigned short iMaxUdpDg;
    char FAR *     lpVendorInfo;
} WSADATA, * LPWSADATA;
    wVersion为你将使用的Winsock版本号,wHighVersion为载入的Winsock动态库支持的最高版本,注意,它们的高字节代表次版本,低字节代表主版本。
    szDescription与szSystemStatus由特定版本的Winsock设置,实际上没有太大用处。
    iMaxSockets表示最大数量的并发Sockets,其值依赖于可使用的硬件资源。
    iMaxUdpDg表示数据报的最大长度;然而,获取数据报的最大长度,你需要使用WSAEnumProtocols对协议进行查询。
    最大数量的并发Sockets并不是什么神奇的数字,它是由可用的物理资源来决定的.
    lpVendorInfo是为Winsock实现而保留的制造商信息,这个在Windows平台上并没有什么用处.
    自Windows 95以后的操作系统都支持Winsock 2.2的版本.即使是这样,你也不能认为这些Windows平台支持最新的Winsock版本,为了让你的程序能够运行于大多数平台,最好使用Winsock1.1规范.
   
    当你使用完Winsock接口后,要调用下面的函数对其占用的资源进行释放:
    int WSACleanup(void);
    如果调用该函数失败也没有什么问题,因为操作系统为自动将其释放,对应于每一个WSAStartup调用都应该有一个WSACleanup调用.
错误处理
   
   Winsock函数调用失败大多会返回 SOCKET_ERROR(实际上就是-1),你可以调用WSAGetLastError得到错误的详细信息:
    int WSAGetLastError (void);
    对该函数的调用将返回一个错误码,其码值在WINSOCK.H或WINSOCK2.H(根据其版本)中已经定义,这些预定义值都以WSAE开头.同时你还可以使用WSASetLastError来自定义错误码值.
以下是一个使用上述知识初始化Winsock的一个例子(记得要引用Winsock的lib库).
#include
void main(void)
{
   WSADATA wsaData;
   // Initialize Winsock version 2.2
   if ((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
   {
      // NOTE: Since Winsock failed to load we cannot use
      // WSAGetLastError to determine the specific error for
      // why it failed. Instead we can rely on the return
      // status of WSAStartup.
      printf("WSAStartup failed with error %d\n", Ret);
      return;
   }
   // Setup Winsock communication code here
   // When your application is finished call WSACleanup
   if (WSACleanup() == SOCKET_ERROR)
   {
      printf("WSACleanup failed with error %d\n", WSAGetLastError());
   }
}

Winsock是与协议无关的接口,这里我们主要介绍广泛使用的IP协议,其版本为当前的IPv4(IPv6暂不介绍).
IPv4寻址
    IPv4中,计算机的地址用32位的二进制表示,称为IP地址.当客户机使用TCP或UDP与服务器通讯时,必须指定其IP地址及通讯端口.服务器想要侦听客户机请求时,也要指定IP地址与端口.Winsock中使用SOCKADDR_IN结构来设置这些信息:
struct sockaddr_in
{
short           sin_family;
u_short         sin_port;
struct in_addr  sin_addr;
char            sin_zero[8];
};
    sin_family必须为AF_INET,设定Winsock使用IP地址族.
    sin_port定义TCP或UDP的通讯端口. 端口要认真选择,一些知名服务使占用了特定的端口,如FTP及HTTP
    sin_addr记录了IPv4的4字节地址,它是无符号长整型,IP地址通常以"a.b.c.d"表示,每个字母表示一个数字与IP地址的每个字节对应(从左到右)
   
    sin_zero只是用来占用字节空间使得SOCKADDR_IN结构与SOCKADDR结构一样.
   函数 inet_addr可以将字符串"a.b.c.d"格式的IP地址转换为32位无符号长整型的IP地址.
   
    unsigned long inet_addr(const char FAR *cp);
    cp就是指向null结尾的字符串形式IP地址.返回以网络字符顺序表示的IP地址.
字节顺序
   
    不同计算机的处理器以big-endian或little-endian形式来表示数字.而IP地址与端口需要多个字节来表示,它们在不同计算机上的表示顺序称为主机字节顺序.而IP地址与端口在网络中的表示不应该有差异,必须以big-endian形式表示,称为网络字节顺序.
    下面的Winsock API可以将主机字节顺序转换为网络字节顺序:
    u_long htonl(u_long hostlong);
   
    int WSAHtonl(SOCKET s, u_long hostlong, u_long FAR * lpnetlong);
    u_short htons(u_short hostshort);
    int WSAHtons( SOCKET s, u_short hostshort, u_short FAR * lpnetshort);
    而下而的API则将网络字节顺序转换为主机字节顺序:
    u_long ntohl(u_long netlong);
    int WSANtohl(SOCKET s,u_long netlong, u_long FAR * lphostlong);
    u_short ntohs(u_short netshort);
    int WSANtohs(SOCKET s, u_short netshort, u_short FAR * lphostshort);
    下面代码说明了如何用SOCKADDR_IN结构来设置地址与端口:
     SOCKADDR_IN InternetAddr;
     INT nPortId = 5150;
     InternetAddr.sin_family = AF_INET;
     // Convert the proposed dotted Internet address 136.149.3.29
     // to a four-byte integer, and assign it to sin_addr
     InternetAddr.sin_addr.s_addr = inet_addr("136.149.3.29");
     // The nPortId variable is stored in host-byte order. Convert
     // nPortId to network-byte order, and assign it to sin_port.
     InternetAddr.sin_port = htons(nPortId);
IP地址不是那么容易记忆,可以使用更友好的主机名来代替IP地址,比如
www.somewebsite.com
,用FTP来表示文件传输协议端口号21.与之相关的函数有:getaddrinfo, getnameinfo, gethostbyaddr, gethostbyname, gethostname, getprotobyname, getprotobynumber, get-servbyname, 及getservbyport. 这些函数的异步版本有: WSAAsyncGetHostByAddr, WSAAsyncGetHostByName, WSAAsyncGetProtoByName, WSAAsyncGetProtoByNumber, WSAAsyncGetServBy- Name,及WSAAsyncGetServByPort.
使用socket函数来创建一个socket,其类型SOCKET在WINSOCK.H中定义:
SOCKET socket (int af,int type,int protocol);
af为协议地址族,这里我们使用IPv4,必须为AF_INET
type为socket类型,如果使用TCP/IP,type设为SOCK_STREAM,如果使用UDP,则为SOCK_DGRAM
protocol在给定的地址族及socket类型有多个入口的情况下用来限定特定的传输,对于TCP其值为IPPROTO_TCP,而UDP则为IPPROTO_UDP
Winsock通过函数setsockopt, getsockopt, ioctlsocket及WSAIoctl来控制socket的各种选项及行为,对于简单的Winsock应用,我们可能不需要用到.一旦成功建立了socket,你就可以准备在socket上进行通讯收发数据.Winsock有两种通讯方式:面向连接的通讯方式与无连接的通讯方式.


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/23275/showart_320189.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP