套接字是通讯端点的抽象
创建一个套接字
#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol); 返回值:成功文件(套接字)描述符,失败-1
domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。 协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。 type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等(socket的类型有哪些?)。 protocal:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等 它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
套接字通信是双向的。可以禁止一个套接字的I/O
#include <sys/socket.h> int shutdown(int sockfd, int how); 返回值:成功0,出错-1 sockfd:套接字的描述符 how:三种SHUT_RD(0)关闭sockfd上的读功能 SHUT_WR(1)关闭sockfd上的写功能 SHUT_RDWR(2)关闭sockfd的读写功能
用来在处理器字节序和网络字节序之间实施转换的函数
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
返回值:以网络字节序表示的32位整数
uint16_t htons(uint16_t hostshort);
返回值:以网络字节序表示的16位整数
uint32_t ntohl(uint32_t netlong);
返回值:以主机字节序表示的32位整数
uint16_t ntohs(uint16_t netshort);
返回值:以主机字节序表示的16位整数
h表示主机字节序,n表示网络字节序,l表示长整型,s表示短整型
打印出能被人理解而不是计算机所理解的地址格式。同时支持IPv4和IPv6地址
#include <arpa/inet.h> const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); 返回值:成功地址字符串指针,出错NULL af:地址簇,仅支持AF_INET和AF_INET6 src:来源地址结构指针 dst:接收转换后的字符串 size:保存文本字符串的缓冲区大小 int inet_pton(int af, const char *src, void *dst); 返回值:成功1,格式无效0,出错-1 af:地址簇,仅支持AF_INET和AF_INET6 src:转换的地址字符串 dst:转换后的地址结构指针
这个很常用,所有使用socket都需要用这两个函数转换,p应该是protocol,n应该是net吧。
例子
1 #include <stdio.h> 2 #include <string.h> 3 #include <sys/types.h> 4 #include <sys/ioctl.h> 5 #include <stdlib.h> 6 #include <netdb.h> 7 #include <arpa/inet.h> 8 #include <netinet/in.h> 9 #include <string.h> 10 11 int main(int argc, char *argv[]) 12 { 13 char dst[100]; 14 int sockfd = socket(AF_INET, SOCK_STREAM, 0); 15 16 struct sockaddr_in serv; 17 memset(&serv, 0 ,sizeof(struct sockaddr_in)); 18 19 serv.sin_family = AF_INET; 20 serv.sin_port = htons(5555); 21 22 if((inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr)) == 0) 23 printf("inet_pton error "); 24 if((inet_ntop(AF_INET, &serv.sin_addr.s_addr, dst, sizeof(dst))) == NULL) 25 printf("inet_ntop error "); 26 printf("dst=%s,sizeof(dst)=%d ", dst, sizeof(dst)); 27 28 bind(sockfd, (struct sockaddr *)&serv, sizeof(serv)); 29 listen(sockfd, 15); 30 return 0; 31 }
找到给定计算机系统的主机信息
#include <netdb.h> struct hostent *gethostent(void); 返回值:成功返回指针,出错NULL void sethostent(int stayopen);
stayopen:true就是TCP,否则UDP void endhostent(void);
能够采用一套相似的接口来获得网络名字和网络编号
#include <netdb.h> struct netent *getnetbyaddr(uint32_t net, int type); struct netent *getnetbyname(const char *name); struct netent *getnetent(void); 返回值:成功返回指针,出错NULL void setnetent(int stayopen); void endnetent(void);
在协议名字和协议编号之间进行映射
#include <netdb.h> struct protoent *getprottobyname(const char *name); struct protoent *getprotobynumber(int proto); struct protoent *getprotoent(void); 返回值:成功返回指针,出错NULL void setprotoent(int stayopen); void endprotoent(void);
服务是由地址的端口号部分表示的,每个服务由一个唯一的众所周知的端口号来支持。可以使用getservbyname将一个服务名映射到一个端口号,使用函数getservbyport将一个端口号映射到一个服务名,使用函数getservent顺序扫描服务数据库。
#include <netdb.h> struct servent *getservbyname(const char *name, const char *proto); struct servent *getservbyport(int port, const char *proto); struct servent *getservent(void); 返回值:成功返回指针,出错NULL void setservent(int stayopen); void endservent(void);
将一个主机和一个服务名映射到一个地址
#include <sys/socket.h> #include <netdb.h> int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); 返回值:成功0,出错非0错误码
node:向一个主机名(域名)或者地址串(IPv4的点分十进制串或者IPv6的16进制串)。
service:指向一个服务名或者10进制端口号数串。
hints:可以是一个空指针,也可以是一个指向某个addrinfo结构的指针
res:指向的变量已被填入一个指针,它指向的是由其中的ai_next成员串联起来的addrinfo结构链表 void freeaddrinfo(struct addrinfo *res);
addrinfo结构体
struct addrinfo { int ai_flags; int ai_family; //AF_INET,AF_INET6,UNIX etc int ai_socktype; //STREAM,DATAGRAM,RAW int ai_protocol; //IPPROTO_IP, IPPROTO_IPV4, IPPROTO_IPV6 etc size_t ai_addrlen;//length of ai_addr char* ai_canonname; //full hostname struct sockaddr* ai_addr; //addr of host struct addrinfo* ai_next; }
错误码需要调用函数来转换成错误消息
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> const char *gai_strerror(int errcode);
将一个地址转换成一个额主机名和一个服务名
#include <sys/socke.h> #include <netdb.h> int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags);
返回值:成功0,出错非0值
sa:指向包含协议地址的套接口地址结构,它将会被转换成可读的字符串,
salen:结构的长度,这个结构长度通常由accept、recvfrom、getsockname、getpeername
host:主机字符串
hostlen:长度
serv:服务器字符串
servlen:长度
flags:标志位可以有多个用与或
sockaddr结构体
struct sockaddr
{
unsigned short sa_family; /*addressfamily,AF_xxx*/
char sa_data[14]; /*14bytesofprotocoladdress*/
};
16-9.c
1 #include "apue.h" 2 #if defined(SOLARIS) 3 #include <netinet/in.h> 4 #endif 5 #include <netdb.h> 6 #include <arpa/inet.h> 7 #if defined(BSD) 8 #include <sys/socket.h> 9 #include <netinet/in.h> 10 #endif 11 12 void print_family(struct addrinfo *aip) 13 { 14 printf(" family "); 15 switch(aip->ai_family) { 16 case AF_INET: 17 printf("inet"); 18 break; 19 case AF_INET6: 20 printf("inet6"); 21 break; 22 case AF_UNIX: 23 printf("unix"); 24 break; 25 case AF_UNSPEC: 26 printf("unspecified"); 27 break; 28 default: 29 printf("unknown"); 30 } 31 } 32 33 void print_type(struct addrinfo *aip) 34 { 35 printf(" type "); 36 switch(aip->ai_socktype) { 37 case SOCK_STREAM: 38 printf("stream"); 39 break; 40 case SOCK_DGRAM: 41 printf("datagram"); 42 break; 43 case SOCK_SEQPACKET: 44 printf("seqpacket"); 45 break; 46 case SOCK_RAW: 47 printf("raw"); 48 break; 49 default: 50 printf("unknown (%d)", aip->ai_socktype); 51 } 52 } 53 54 void print_protocol(struct addrinfo *aip) 55 { 56 printf(" protocol "); 57 switch(aip->ai_protocol) { 58 case 0: 59 printf("default"); 60 break; 61 case IPPROTO_TCP: 62 printf("TCP"); 63 break; 64 case IPPROTO_UDP: 65 printf("UDP"); 66 break; 67 case IPPROTO_RAW: 68 printf("raw"); 69 break; 70 default: 71 printf("unknown (%d)", aip->ai_protocol); 72 } 73 } 74 75 void print_flags(struct addrinfo *aip) 76 { 77 printf("flags"); 78 if(aip->ai_flags == 0) { 79 printf(" 0"); 80 } else { 81 if(aip->ai_flags & AI_PASSIVE) 82 printf(" passive"); 83 if(aip->ai_flags & AI_CANONNAME) 84 printf(" canon"); 85 if(aip->ai_flags & AI_NUMERICHOST) 86 printf(" numhost"); 87 if(aip->ai_flags & AI_NUMERICSERV) 88 printf(" numserv"); 89 if(aip->ai_flags & AI_V4MAPPED) 90 printf(" v4mapped"); 91 if(aip->ai_flags & AI_ALL) 92 printf(" all"); 93 } 94 } 95 96 int main(int argc, char *argv[]) 97 { 98 struct addrinfo *ailist, *aip; 99 struct addrinfo hint; 100 struct sockaddr_in *sinp; 101 const char *addr; 102 int err; 103 char abuf[INET_ADDRSTRLEN]; 104 105 if(argc != 3) 106 err_quit("usage: %s nodename service", argv[0]); 107 hint.ai_flags = AI_CANONNAME; 108 hint.ai_family = 0; 109 hint.ai_socktype = 0; 110 hint.ai_protocol = 0; 111 hint.ai_addrlen = 0; 112 hint.ai_canonname = NULL; 113 hint.ai_addr = NULL; 114 hint.ai_next = NULL; 115 if((err = getaddrinfo(argv[1], argv[2], &hint, &ailist)) != 0) 116 err_quit("getaddrinfo error %s", gai_strerror(err)); 117 for(aip = ailist; aip != NULL; aip = aip->ai_next) { 118 print_flags(aip); 119 print_family(aip); 120 print_type(aip); 121 print_protocol(aip); 122 printf(" host %s", aip->ai_canonname?aip->ai_canonname:"-"); 123 if(aip->ai_family == AF_INET) { 124 sinp = (struct sockaddr_in *)aip->ai_addr; 125 addr = inet_ntop(AF_INET, &sinp->sin_addr, abuf, INET_ADDRSTRLEN); 126 printf(" address %s", addr?addr:"unknown"); 127 printf(" port %d", ntohs(sinp->sin_port)); 128 } 129 printf(" "); 130 } 131 exit(0); 132 }