winsock编程框架

IP地址结构:

typedef 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;
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;

socket结构:包含IP地址和端口号

struct sockaddr {
        ushort  sa_family;
        char    sa_data[14];
};

struct sockaddr_in {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
};

主机信息结构:专用于getaddrinfo函数,调用它可获取一个addrinfo结构链表。

typedef struct addrinfo {
  int             ai_flags;
  int             ai_family;
  int             ai_socktype;
  int             ai_protocol;
  size_t          ai_addrlen;
  char            *ai_canonname;
  struct sockaddr  *ai_addr;//获得socket信息
  struct addrinfo  *ai_next;
} ADDRINFOA, *PADDRINFOA;

主机字节顺序与网络字节顺序转换函数:常用

u_long WSAAPI htonl(
  _In_ u_long hostlong
);
u_short WSAAPI htons(
  _In_ u_short hostshort
);
u_long WSAAPI ntohl(
  _In_ u_long netlong
);
u_short WSAAPI ntohs(
  _In_ u_short netshort
);

IP地址转换:

unsigned long inet_addr(
  _In_ const char *cp
);
char* FAR inet_ntoa(
  _In_ struct   in_addr in
);


通知对方不再发送或接收数据:

int shutdown(
  _In_ SOCKET s,
  _In_ int    how
);


socket的理解(winsock):我把socket看成是一个用于通信的抽象对象,通信都是经过它来收发数据,但在它能正常工作之前需对它进行一些参数设置(在一些书中称做给socket命名),其实就是调用bind()函数给其配置为使用TCP/IP协议、自身的IP地址(一般应用程序不关心自身IP地址,所以可设为ADDR_ANY)、使用的端口号(如果端口号指定为0,那么系统自动分配1024~5000),这3个参数就是socket的名称了,也就是一个sockaddr数据结构。socket命名后就可以正常工作了(因为现在为止socket只记录了本机的地址和端口信息,所以我把它称为半个socket),在服务器端调用listen()将这个socket对象设为监听接入连接状态,在调用这个函数之后,只要客户端发起TCP层连接,TCP层可随时和客户端建立连接,这是在后台进行的,在你代码的执行过程中可能有无数个TCP层的连接已经建立。针对已建立的TCP层的连接你的程序应该调用accept()函数取出最前面的一个连接来处理(没有连接会阻塞),这个过程抽象的表达就是“接受客户端的连接”,这个函数调用之后返回一个新的完整的socket,即它既包含了本机地址和端口,也包涵了连接端的地址和端口。现在使用完整的socket就可以真正的通信了。

recv()和recvfrom()可以用半个或完整socket工作,无数据时会阻塞,后者能返回连接端的名字。

send()和sendto()两者都能在完整socket上工作,但后者也可以在半个socket上工作。

完整的socket的断开分为优雅断开和中断断开,前者是先调用shutdown()停止继续发送,再调用closesocket()断开,后者直接调用closesocket()断开。两者的区别是后者缓冲区的数据可能没发完。

为了完整性,前面讲的是流式套接字,现在讲非流式套接字(基于UDP)。在socket对象建立后(建立对象的时候决定是流式还是非流式socket),服务器端只需要调用bind()为其命名后形成一半个套接字就可以用sendto()和recv()和recvfrom()通信了。

前面讲socket的建立只是简单的描述,其实socket有许多选项,在建立的过程中默认了大部分,调用getsockopt()可查询这些选项,调用setsockopt()可设置这些选项。

上面的过程已经能完成通信了,但是很死板,因为在你写程序时必须知道一些参数,比如本机IP地址。其实要实现网络通信需要配置一些参数例如本机IP地址、子网掩码网关等,这些配置大部分是针对网络的本机信息,在命令提示符界面可以执行ipconfig或ipconfig /all来查看,也可已通过控制面板的图形界面查看或设置。但我们在编写程序过程中怎样获取和修该这些信息,答案是IP Helper APIs:

GetAdaptersInfo()查询适配器信息XP之后用GetAdaptersAddresses()代替;

GetNetworkParams()可以获取本地网络参数信息,包括本地主机名、域名和DNS服务器列表;

GetNumberOfInterfaces()获取本机网络接口数量,GetInterfaceInfo()获取本机网络接口信息。网络接口:是IP层的一个概念,在程序中是一个索引号,程序中可以选择使用哪个网络接口传输数据,网络接口一般是和网络适配器一一对应的,也就是一个网络接口就是一个适配器,但网络接口一般比适配器多一个,即环回接口。

GetIpAddrTable()获取本机的网络接口的IP地址的映射表。这个表里的内容和适配器信息里的内容有重合的内容,但包含的信息更多。因为有这个表,所以一个适配器可以有多个IP地址。AddIPAddress()和DeleteIPAddress()来添加或删除IP地址。


socket模式:阻塞和非阻塞。默认创建的socket是阻塞模式,用ioctlsocket()可改为非阻塞模式。

socketI/O模型:

1、select模型:在使用socket前要用select()查询socket集合的状态。

2、WSAAsyncSelect模型:为每个socket绑定一个消息,用消息通知应用程序。

3、WSAEventSelect模型:将一个事件与网络事件集合绑定,用事件来通知应用程序。

4、重叠I/O模型:每次操作绑定一个Overlapped结构,操作完成时可用事件通知或编写一个完成例程(回掉函数)。

5、完成端口模型:将socket添加到一个完成端口,一次操作完成时系统自动分配一个工作线程来处理。


你可能感兴趣的:(Windows库使用,网络)