linxu套接字头文件: #include 创建套接字: int socket(int domain,int type,int protocol); domain: AF_INET IPv4 AF_INET6 IPv6 AF_UNIX Unix域 AF_UNSPEC 未指定 type: SOCK_DGRAM 长度固定,无连接,不可靠传递 默认协议是UDP SOCK_RAW ip协议的数据报接口 SOCK_SEQPACKET 长度固定,有连接,可靠的传递 SOCK_STREAM 有序,可靠,双向的连接字节流 默认协议是TCP protocol: 一般为0,表示按给定的域和套接字类型选择默认协议 调用socket和open类似,均可以使用输入/输出文件描述符(但并非所有,例如lseek),不再使用时候用close关闭 可以使用shutdown禁止套接字上的输入输出 int shutdown(int sockfd,int how); how: SHUT_RD 关闭读端,无法从套接字上读取数据 SHUT_WR 关闭写端,无法从套接字上发送数据 SHUT_RDWR 无法读和写 关闭套接字: close(int sockfd); 字节排序: 字节排序有两种,大端和小端,在32位机器上,大端排序为:n,n+1,n+2,n+3,小端排序为:n+3,n+2,n+1,n,但是不管字节排序如何,数字最高位总是在左边,最小位总是在右边,比如0x04030301,数字最高位必定是4,最低位必定是1 #include uint32_t htonl(uint32_t hostint32); 返回值:网络字节序表示的32位整数 uint16_t htons(uint16_t hostint16); 返回值:网络字节序表示的16位整数 uint32_t ntohl(uint32_t netint32); 返回值:主机字节序表示的32位整数 uint16_t ntohs(uint16_t netint16); 返回值:主机字节序表示的16位整数 记忆方法: h=host,n=network,l=long(表示4个字节),s=short(表示两个字节); 注意: 有些系统将其定义在 地址格式: 为使不同格式地址能够被传入套接字函数,地址被强制转换成sockaddr表示: struct sockaddr { sa_family_t sa_family; char sa_data[]; ...... }; 套接字可以自由添加额外成员并自定义sa_data的大小,在linxu中,被定义为: struct sockaddr { sa_family_t sa_family; char sa_data[14]; }; FreeBSD: struct sockaddr { unsigned char sa_len; sa_family_t sa_family; char sa_data[14]; }; #include IPv4中(AF_INET)中,套接字地址结构为: struct in_addr { in_addr_t s_addr; in_addr_t为uint32_t }; struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; in_port_t为uint16_t struct in_addr sin_addr; }; uint32_t和uint16_t定义在 IPv6(AF_INET6): struct in6_addr { uint8_t s6_addr[16]; }; struct sockaddr_in6 { sa_family_t sin6_family; in_port_t sin6_port; uint32_t sin6_flowinfo; struct in6_addr sin6_addr; uint32_t sin6_scope_id; }; 以上为必须定义,可以自由添加额外的字段: struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; //扩充字段,必须全部置为0 }; 地址格式化: #include const char *inet_ntop(int doman,const void *restrict addr,char *restrict str,socklen_t size); 如果成功,返回地址字符串,否则返回NULL; 将网络字节序的二进制地址转换成文本字符串 size指定网络文本字符串缓冲区大小,INET4_ADDRSTRLEN和INET6定义足够大用来存放文本字符串 int inet_pton(int domain,const char *restrict str,void *rstrict addr); 如果成功返回1,无效返回0,出错返回-1 将文本字符串转换成网络字节序的二进制 如果domain是(AF_INET)IPv4,缓冲区addr有足够大存放32位地址,对于IPv6(AF_INET6),则需要足够大的空间存放128地址 以上两个函数均支持IPv4和IPv6,inet_addr和inet_ntoa仅支持IPv4 in_addr_t inet_addr(const char *cp); char *inet_ntoa(struct in_addr in); 地址查询: #include struct hostent *gethostent(void); 成功返回指针,失败NULL 打开数据文件,如果没有打开,会打开它 void sethostent(int stayopen); 如果没有打开,回绕 void endhostend(void); 关闭数据文件 struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_lenght; char **h_addr_list; }; 注意:返回的地址采用网络字节顺序 还有两个函数gethostbyname,gethostbyaddr,不过被认为是过时的,将会有替代函数 struct netent *getnetbyaddr(uint32_t net,int type); struct netent *getnetbyname(const char *name); struct netent *getnetent(void); 三个函数若成功返回指针,失败NULL void setnetent(int stayopen); void entnetent(void); struct netent { char *n_name; char **n_aliases; int n_addrtype; uint32_t n_net; ..... }; 按照网络字节返回,n_addrtype是一个地址足常量(例如AF_INET); 可以将协议号采用以下函数映射: struct protoent *getprotobyname(const char *name); struct protoent *getprotobynumber(int proto); struct protoent *getprotoent(void); void setprotoent(int stayopen); void endprotoent(void); struct protoent { char *p_name; char **p_aliases; int p_proto; ..... }; getservebyname可以将一个服务名字映射到一个端口号,getservbyport反之,也可以采用getservent顺序扫描服务数据库; struct servent *getservbyname(const char *name,const char *proto); struct servent *getservbyport(int port,const char *proto); struct servent *getservent(void); void setservent(int stayopen); void endservent(void); struct servent { char *s_name; char **s_aliases; int s_port; char *s_proto; ..... }; 这几个函数代替gethostbyname和gethostbyaddr #include #include int getaddrinfo(const char *restrict host,const char *restrict service,const struct addrinfo *restrict hint,struct addrinfo **restrict res); 成功返回0,出错返回非0 需要提供主机名字,服务名字,或者两者都提供,如果仅仅提供一个名字,另外一个必须使空指针,主机名字可以使一个节点名或者十进制计法表示的主机地址. void freeaddrinfo(struct addrinfo *ai); getaddrinfo返回一个addrinfo的链表,用freeaddrinfo释放. struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; struct sockaddr *ai_addr; char *ai_canonname; struct addrinfo *ai_next; ..... }; ai_flags: AI_ADDRCONFIG 查询配置的地址类型(IPv4 or IPv6) AI_ALL 查找IPv4 and IPv6 地址(仅用于AI_V4MAPPED) AI_CANONNAME 需要一个规范名 AI_NUMERICHOST 数字格式返回主机地址 AI_NUMERICSERV 以端口号返回服务 AI_PASSIVE 套接字地址用于监听绑定 AI_V4MAPPED 如果没有IPv6,返回映射到IPv4的地址 PS: 如果getaddrinfo失败,不能用perror or strerror生成错误消息,替代调用gai_strerror将错误码转换错误消息 const char *gai_strerror(int error); int getnameinfo(const struct sockaddr *restrict addr,socklen_t alen,char *restrict host,socklen_t hostlen,char *restrict service,socklen_t servlen,unsigned servlen,unsigned int flags); 将地址转换成主机名或者服务名 flags: NI_DGRAM 服务基于数字报而非流 NI_NAMEREQD 如果找不到主机,作为一个错误对待 NI_NOFQDN 对于本地主机,仅返回完全限定域名的节点名字部分 NI_NUMERICHOST 数字形式返回主机地址 NI_NUMERICSERV 数字形式返回服务地址 代码 //示例代码: #include #include #include #include #include #include #include void print_family(struct addrinfo *aip) { printf(" family "); switch(aip->ai_family) { case AF_INET: printf("inet"); break; case AF_INET6: printf("inet6"); break; case AF_UNIX: printf("unix"); break; case AF_UNSPEC: printf("unspecified"); break; default: printf("unknown"); } } void print_type(struct addrinfo *aip) { printf(" type "); switch(aip->ai_socktype) { case SOCK_STREAM: printf("stream"); break; case SOCK_DGRAM: printf("datagram"); break; case SOCK_SEQPACKET: printf("seqpacket"); break; case SOCK_RAW: printf("raw"); break; default: printf("unknown %d",aip->ai_socktype); } } void print_protocol(struct addrinfo *aip) { printf(" protocol "); switch(aip->ai_protocol) { case 0: printf("default"); break; case IPPROTO_TCP: printf("TCP"); break; case IPPROTO_UDP: printf("UDP"); break; case IPPROTO_RAW: printf("raw"); break; default: printf("unknown %d",aip->ai_protocol); } } void print_flags(struct addrinfo *aip) { printf(" flags "); if(aip->ai_flags==0) { printf(" 0"); } else { if(aip->ai_flags & AI_PASSIVE) printf("passive"); if(aip->ai_flags & AI_CANONNAME) printf(" canon "); if(aip->ai_flags & AI_NUMERICHOST) printf(" numhost "); if(aip->ai_flags & AI_NUMERICSERV) printf(" numserv"); } } int main(int argc,char *argv[]) { struct addrinfo *ailist,*aip; struct addrinfo hint; struct sockaddr_in *sinp; const char *addr; int err; char abuf[INET_ADDRSTRLEN]; if(argc != 3) { printf("%s nodename service",argv[0]); return 1; } hint.ai_flags=AI_CANONNAME; hint.ai_family=0; hint.ai_socktype=0; hint.ai_protocol=0; hint.ai_addrlen=0; hint.ai_canonname=NULL; hint.ai_addr=NULL; hint.ai_next=NULL; if((err=getaddrinfo(argv[1],argv[2],&hint,&ailist)) !=0) { printf("error:%s\n",gai_strerror(err)); return 1; } for(aip=ailist;aip != NULL;aip=aip->ai_next) { print_flags(aip); print_family(aip); print_type(aip); print_protocol(aip); printf("\n\thost %s\n",aip->ai_canonname?aip->ai_canonname:"-"); if(aip->ai_family==AF_INET) { sinp=(struct sockaddr_in *)aip->ai_addr; addr=inet_ntop(AF_INET,&sinp->sin_addr,abuf,INET_ADDRSTRLEN); printf("address %s\n",addr?addr:"unknown"); printf("port %d",ntohs(sinp->sin_port)); } printf("\n"); } return 0; } (责任编辑:IT) |