Windows下与Linux下套接字结构体区别
套接字地址结构
大多数套接字函数都需要一个指向套接字地址结构的指针作为参数。每个协议族都定义它自己的套接字地址结构,以sockaddr_开头,并以对应每个协议族的唯一后缀结尾
IPv4套接字地址结构
IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名
在unix下:定义在<netinet/in.h>头文件中
在Windows下:定义在<winsock.h>头文件中
//UNIX下: typedef unsigned int in_addr_t;//至少是32位无符号整数类型 typedef unsigned short int in_port_t;//至少是16位无符号整数类型 typedef unsigned short int sa_family_t;//可以是任何无符号整数类型 struct in_addr { in_addr_t s_addr;//32位的IPv4地址网络字节排序 }; struct sockaddr_in { sa_family_t sin_family;//协议族 in_port_t sin_port;//16位TCP或UDP端口号,网络字节排序 struct in_addr sin_addr; char sin_zero[8];//不使用 };
//Windows下 using u_short = unsigned short; //该地址用于划分A,B,C类地址时期所用 typedef struct in_addr { union { //访问IPv4地址的所有4个字节 struct { UCHAR s_b1, s_b2, s_b3, s_b4; }S_un_b; //访问IPv4地址的2个16位值 struct { USHORT s_w1, s_w2; }S_un_w; //格式化为u_long的IPv4地址 ULONG S_addr; } S_un; #define s_addr S_un.S_addr // 可以用于大多数tcp和ip代码 #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 } IN_ADDR, * PIN_ADDR, FAR* LPIN_ADDR; struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8];//未使用,通常设为0; };
32位IPv4地址存在两种不同的访问方法。如果使用sin_addr将按in_addr结构引用其中的32位IPv4地址,而sin_addr.s_addr将按in_addr_t(无符号32位整数)引用同一个32位IPv4地址
Windows下与Unix下并无任何区别,在不同主机之间的通信中,这个结构不在主机之间传递
通用套接字接口
在作为一个参数传递进任何套接字函数时,套接字地址总是以指针的形式来传递,但是必须先强制转换成通用套接字结构(支持所有协议族的套接字地址结构)
//Unix下:bits/socket.h using sa_family_t = unsigned short int; struct sockaddr { sa_family_t sa_family;//协议族 char sa_data[14];//地址数据 }; using u_short = unsigned short; //Windows下 struct sockaddr { u_short sa_family; //协议族 char sa_data[14];//地址数据 }; //套接字函数原型 int bind(int, const struct sockaddr*, int);
所以,在使用套接字函数的时候,需要将指向特定协议的套接字结构强制转换成通用套接字,而通用套接字在这两个环境下并无区别
IPv6套接字地址结构
//Unix下:bits/socket.h struct in6_addr { unsigned char u6_addr[16];//128位IPv6地址 }; struct sockaddr_in6 { unsigned short int sin6_famliy;//AF_INET6 unsigned short int sin6_port; // 端口号 unsigned int sin6_flowinfo; //IPv6流信息 struct in6_addr sin6_addr; /* IPv6 地址 */ unsigned int sin6_scope_id; //IPv6范围标识 }; //Windows下<ws2ipdef.h> typedef struct in6_addr { union {//128位IPv6地址 unsigned char Byte[16]; unsigned short Word[8]; } u; }; typedef struct sockaddr_in6 { unsigned short sin6_family; // AF_INET6. unsigned short sin6_port; //端口号 unsigned long sin6_flowinfo; // IPv6流信息。 in6_addr sin6_addr; // IPv6 地址. union { unsigned long sin6_scope_id; // IPv6范围标识 SCOPE_ID sin6_scope_struct; }; } SOCKADDR_IN6_LH;
IPv6的地址族是AF_INET6,而IPv4的地址族是AF_INET
结构中字段的先后顺序做过编排,使得该结构本身是64位对齐的
sin6_flowinfo字段分成两个字段:
- 低序20位是流标
- 高序12位保留
sin6_scope_id字段对于具备范围的地址标识其范围;
socket套接字在Windows与Linux之间并无太大差异,只是所在头文件以及套接字结构体成员的数据类型有点区别
而在Windows下,创建一个套接字需要先加载socket库
#include <windows.h>
#include <winsock.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main
{
WSADATA wsd;
int Ret = 0;
//通知程序,加载socket库
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
cout << "socket初始化失败: " << WSAGetLastError() << endl;
return -1;
}
/*创建套接字。。。。
*
*
*/
//通过WSACleanup()关闭该动态库
WSACleanup();//关闭socket库
return 0;
}
在Linux下只需要直接创建套接字即可:
#include <netinet/in.h>
int main(int argc, char **argv)
{
sockaddr_in servaddr;
/* 创建套接字
*
*
*/
return 0;
}