牛首山下

[转]应用程序 编程套接口 -- Sockets(称伯克利套接口)

昕竹轩 之 技术博客

 

应用程序 编程套接口 -- Sockets(称伯克利套接口)

标准服务提供接口 -- SPI
1.Windows平台上的Winsock(Sockets)是由UNIX的Berkeley Socket移植过去的)
2.在Windows系统中有一组扩展库函数(通常以字母WSA打头)
3.其中Winsock1.1版本中,不同的Tcp/IP协议栈供应商需提供自己Winsock接口实现动态链接库.而后来微软推出了Winsock2.0在Winsock接口与协议栈间定义了标准服务提供接口(SPI)使得同一个Winsock动态链接库可同时访问多个不同提供商提供的协议栈.
4.微软在Winsock2.0标准中提供了一个统一的链接库Ws2_32.dll,但其真正使用的是对应的静态链接库文件Ws2_32.lib
5.另外Winsock内核驱动afd.sys是其网络数据缓存的管理器.(程序的提取数据及网络的数据发送都依赖于它) Winsock2支持多种底层网络协议如:TCP/IP、ATM等。
  
   地址与地址操作函数

  
INET 协议族地址结构 - sockaddr_in // in--地址结构名中的最后两个字母"in"
  
   /* Socket地址,internet类型 */
   struct sockaddr_in{
             short                sin_family;       // 地址族,一般为AF_INET.或AF_XXX
             u_short             sin_port;         // 16位IO端口
             struct in_addr sin_addr;         // 32位IPv4地址。
             char                  sin_zero[8];     // 8个字节的0值填充,作用是使用sockaddr_in结构大小与通用地址结构sockaddr相同。
    }
   
   使用Win32平台的两个清零函数
    VOID ZeroMemory(PVOID destination, SIZE_T length);
    void *memst( void *dest, int c, size_t count );
   
   IPv4 地址结构 -- in_addr

   用于存储32位IPv4地址的数据结构,其定义如下:
  
   /* Internet address (old style... should be updated) */
   struct in_addr {
       union {
                 struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
                 struct { u_short s_w1,s_w2; } S_un_w;
                 u_long S_addr;
        } S_un;
       
    #define s_addr      S_un,S_addr       // can be used for most tcp & ip code
    #define s_host      S_un.S_un_b.s_b2 /* host on imp */
    #define s_net        S_un.S_un_b.s_b1 /* network */
    #define s_imp       S_un.S_un_w.s_w2   /* imp */
    #define s_impno   S_un.S_un_b.s_b4    /* imp # */
    #define s_lh          S_un.s_un_b.s_b3    /* logical host */
    }
   
    结构提供3种赋值接口 S_addr 、S_un_b和S_un_w,最常用的两种。
   
    S_addr:32位的无符号整数,对应32位IPv4地址。如将202.199.9.199赋给in_addr结构。可用一下方式。
        in_addr addr;
        addr.S_un.S_addr = inet_addr("202.199.9.199");
       
        其中,inte_addr 函数用于转换点串IP地址
        如果有以下定义:
        #define s_addr S_un.S_addr
        因此上面代码可简写为:
        in_addr addr;
        addr.S_addr = inet_addr("202.199.9.199");
       
        假设主机上有多块网卡,每块网卡都配有IP地质,不关心应用程序使用哪个接口,
        在给addr.s_addr赋值时可以使用常量--INADDR_ANY。它在Winsock2.h中被定义
        为(u_long)0×000000000,即本地的任意以太网接口IP地址。代码如下
        in_addr addr;
        addr.s_addr = INADDR_ANY;
       
        S_un_b: 包含4个8位无符号整数,组合起来表示IPv4地址:s_b1.s_b2.s_b3.s_b4。
        下面的例子同样将IPv4地址赋给addr。
       
        in_addr addr;
        addr.S_un.S_un_b.s_b1 = 202;
        addr.S_un.S_un_b.s_b2 = 119;
        addr.S_un.S_un_b.s_b3 = 9;
        addr.S_un.S_un_b.s_b4 = 119;

通用地址结构——sockaddr

/* Structure used by kernel to store most addresses */
struct sockaddr {
           u_short sa_family;         /* address family */
           char       sa_data[14]      /* up to 14 bytes of direct address */
}
由于网络底层协议的多样性,研究人员在最初设计套接口函数时,面临着这样的选择:是
专门开发一套为TCP/IP协议所用的API,还是提供一种通用的编程接口以服务于多种网络协议。
两者之间的差别非常明显,如果是采用前者,那么提供的函数接口就会相对简单,而对于后者,
程序员在使用时必须提供足够的信息(参数)来告诉接口自己所采用的协议簇。
以connect函数为例(该函数一般用于主动建立TCP连接):
int connect(SOCKET s, const struct sockaddr FAR *name, int namelen);
    为了使其适用于不同的网络协议环境,它的第二个参数并不是 struct sockadr_in*,而是struct sockaddr*
在使用涉及到这种地址结构的函数接口时,必须强制将struct sockadr_in指针转化为struct sockaddr指针。
很多人很奇怪为何不采用ANSI C的通用指针void *答案很简单:套结构函数口是在ANSI C标准之前制定的。
通用地址结构很少直接使用通用地址结构,但务必进行强制地址转换.

地址操作函数
  
   介绍三个常用3个地址函数 inet_addr、inet_ntoa 以及 gethostbyname。需强调的是,
   在使用winsock函数之前,应用程序必须首先强调WSAStartup函数初始化ws2_32.dll,
   而在应用结束后必须调用WSACleanup函数。
  
  
1.函数inet_addr将包括点分格的IPv4地址字符串转换为in_addr地址结构适用的32位整数。
   定义如下:
      unsigned long inet_addr(const char FAR *cp);
       cp:[IN]*,NULL结尾的点分IPv4字符串。
       返回值:如果没有错误发生,函数返回32位的地址信息。
       如果cp字符串包含的不是合法的IP地址,那么函数返回INADDR_NONE。
   
   
2.与inet_addr相反,函数inet_ntoa将一个in_addr地址值转换为标准的点分IP地址字符串
    定义如下:
    char FAR * inet_ntoa(struct in_addr in);
    in:[IN],IPv4地址结构。
    返回值:如果没有错误发生,函数inet_ntoa返回一指向包含点分IP地址的静态存储区字符指针:否则返回NULL
   
    一个简单例子结束对套接字接口地址结构的介绍,下面的代码片段演示了如何为INET地址结构赋值,并在connect函数
    中使用Winsock库之前都必须调用的:socket函数用于创建接口;connect函数用于连接服务器;
    WSACleanup函数用于结束Winsock的使用。这些函数的使用在后文中都将蒋加以详细介绍。
            WSAStartup(...);
            .....
            SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
            struct sockaddr_in to;
            memst(&to , 0,sizeof(to));
            to.sin_addr.s_addr = inet_addr("202.199.9.199");
            to.sin_port = htons(5555);
            connect(sock, (struct sockaddr *) &to, sizeof(to));
            .......
            WSACleanup();
    
    
3.与上述两个函数不同,gethostbyname完成的是域名解析功能。函数定义如下:
      struct hostent FAR *gethostname(const char FAR *name);
       name:[IN],待解析的NULL结尾的域名字符串。
       返回值:如果没有错误发生,函数返回包含域名地址信息的HOSTENT结构函数 ;
       否则返回空指针(NULL),可以调用WSAGetLastError函数来获得具体的错误代码。
      
       HOSTENT结构中:h_addr_list域,是一个NULL结尾的IP地址列表。
       示例:下面代码对于
www.seu.edu.con域名解析,并输出获得IP地址。
      
       WSAStartup(...);
       ....
       NOSTENT *host = gethostbyname("www,seu.edu.com");
       struct in_addr addr;
         if (host != NULL){
             for(int i=0; host->h_addr_list[i] != NULL; i++){
             memset(&addr, 0,sizeof(addr));
             memcpy(&addr.S_un.Saddr,
                           host->h_addr_list[i],
                           host->h_length;
              printf("%s: %s\n",host->h_name, inet_ntoa(addr));
         }
         }
         .....
         WSACleanup();
         输出结果:
         seic22.seu.edu.cn: 202.119.24.32

posted on 2010-04-16 12:05  牛首山下  阅读(527)  评论(0)    收藏  举报

导航