linux socket编程学习
1. int socket(int domain, int type, int protocol);
返回套接字描述符,错误的情况下返回-1。Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。你也可以对socket执行这些操作。
参数1:domain-指定用于通信的协议族,常见的协议族有:AF_INET、AF_INET6、AF_LOCAL等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合,AF_UNIX决定了要用一个绝对路径名作为地址。
参数2:type-常用的socket类型有:SOCK_STREAM、SOCK_DGRAM等。
参数3:protocol-指定协议,常用的协议有:IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等。默认使用0,会根据type值的不同,有不同的默认值:type-SOCK_STREAM则默认为TCP协议,type-SOCK_DGRAM则默认UDP协议。
参:http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html
2. struct sockaddr_in
include <netinet/in.h>
// All pointers to socket address structures are often cast to pointers
// to this type before use in various functions and system calls:
struct sockaddr {
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};
// IPv4 AF_INET sockets:
struct sockaddr_in {
short sin_family; // e.g. AF_INET, AF_INET6
unsigned short sin_port; // e.g. htons(3490)
struct in_addr sin_addr; // see struct in_addr, below
char sin_zero[8]; // zero this if you want to
};
struct in_addr {
unsigned long s_addr; // load with inet_pton()
};
这几个结构体用于处理internet address信息。sockaddr和sockaddr_in都是16字节长,通常情况下他们是可以*转换的,在connect操作中有转换的操作。在sockaddr_in的sin_port和sin_addr存放网络字节序的数据。网络字节序和主机字节序的区别参见链接。sin_addr 和 sin_port 需要转换为网络字节序,而sin_family 不需要。因为 sin_addr 和 sin_port 分别封装在包的 IP 和 UDP 层。因此,它们必须 要 是网络字节序。但是 sin_family 域只是被内核 (kernel) 使用来决定在数 据结构中包含什么类型的地址,所以它必须是主机字节序。
3. 主机字节序(host byte order)和网络字节序(network byte order)的转换:
(1) . int inet_aton(const char *cp, struct in_addr *inp);
作用是把cp参数指定的字符串形式(IPV4 numbers-and-dots notation)的ip地址转换为整数形式(binary form)的且已经是网络字节序的数据。并把结果存入inp所指向的结构体。如果传入的地址合法,则返回非0整数,否则,返回0。不支持IPV6。
cp参数可以是由dot隔开的数字组成的IPV4地址,这些数字可以是十进制、八进制(以0开头)或者十六进制(以0X开头)的,如192.168.52.250和0XC0.0XA8.0X34.0XFA,也可以是不同进制混合的,如0XC0.0250.52.0XFA。这几个地址都是同一个地址192.168.52.250。这些形式的地址都可被命名为IPV4 numbers-and-dots notation。而如果四个数字全部是10进制的话,可以称为IPV4 dotted-decimal notation或者IPv4 dotted-quad notation。其实指的都是IP地址的字符串表示法。以上叫法不知道用什么词翻译好,所以直接列出。
源自:http://linux.die.net/man/3/inet_aton
用法:
char* ip = "192.168.52.250";
struct in_addr addr;
inet_aton(ip, &addr);
(2) . int inet_pton(int af, const char *src, void *dst);
作用:把src所指的地址转换为一个网络地址结构体(network address structure),并把这个结构体拷贝给dst。af参数的内容必须是AF_INET或者AF_INET6。成功的情况返回1;如果src字符串不符合af参数所指定协议族(address family)的要求地址格式,则返回0;如果传递的af参数不是有效地协议族,则返回-1。(支持IPV6地址)
这里的src参数,如果af是AF_INET的话,必须是dotted-decimal notaion,也就是说它只支持十进制表示的IP地址字符串。
源自:http://linux.die.net/man/3/inet_pton
用法:
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons( (unsigned short)port);
inet_pton(AF_INET, hostIp, (void*)&sin.sin_addr);
(3) . in_addr_t inet_addr(const char *cp);
作用:把cp参数指定的且格式为IPV4 numbers-and-dots notation(与inet_aton所要求的地址格式一样)的地址转换为long型整数地址。不支持IPV6。在参数非法的情况下返回-1。而巧合的是255.255.255.255所对应的网络字节序整数地址正好也是-1,所以并不能凭借返回值来判断转换是否成功。所以更推荐使用inet_pton、inet_aton或者getaddrinfo。当然在确保255.255.255.255不会出现的情况下也是可以使用的。
源自:http://linux.die.net/man/3/inet_addr
用法:
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons( (unsigned short)port);
sin.sin_addr.s_addr = inet_addr(hostIp);
(4) . int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
作用:根据给定的node和service信息,getaddrinfo()可以构造一个或多个addrinfo结构体,而其中的每一个addrinfo结构体所包含的互联网地址(internet address)都可用在bind()或者connect()函数调用时。也就是说getaddrinfo()构造了多个符合条件的互联网地址,这些多个地址信息单元组成一个链表,而由res指向这个链表的首元素。遍历每一个地址信息可以使用ai_next。
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
hints虽然也是一个addrinfo结构体,不过它起一个criteria的作用,或者说是过滤器,就是把符合条件的数据留下。hints的ai_flags、ai_family、ai_socktype和ai_protocol可以根据需要指定数值,但是其他的属性就不要去指定了,保证是0或者NULL就可以。如果hints是NULL,那么相当于是ai_socktype=0; ai_protocol=0; ai_family=AF_UNSPEC; ai_flags=(AI_V4MAPPED|AI_ADDRCONFIG)。ai_flags也起着限制的作用,它的值可以是单个值如:AI_ADDRCONFIG,也可以是多取值的,用“|”符号按位将每个flag分隔开,如(AI_V4MAPPED|AI_ADDRCONFIG)。
node可以是IPV4 numbers-and-dots notation形式的地址,也支持IPV6地址,这些通称numerical network address;域名地址(network hostname)也可以。而如果hints.ai_flags包含AI_NUMBERICHOST标志,那么node必须是numberical network address。因为AI_NUMBERICHOST阻止了任何潜在的对冗长的network host address的查找。
service指定端口。且node和service最多只能有一个是空的。
源自:http://linux.die.net/man/3/getaddrinfo
用法:
struct addrinfo hints;
struct addrinfo *result, *rp;
int sfd, s, j;
size_t len;
ssize_t nread;
/* Obtain address(es) matching host/port */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
hints.ai_flags = 0;
hints.ai_protocol = 0; /* Any protocol */
s = getaddrinfo(argv[1], argv[2], &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
/* getaddrinfo() returns a list of address structures.
Try each address until we successfully connect(2).
If socket(2) (or connect(2)) fails, we (close the socket
and) try the next address. */
for (rp = result; rp != NULL; rp = rp->ai_next) {
sfd = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sfd == -1)
continue;
if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
break; /* Success */
close(sfd);
}
关于网络自己序和主机字节序的转换可参见:http://blog.csdn.net/sunboy_2050/article/details/6100734 和http://blog.csdn.net/Sunboy_2050/article/details/6061528这两篇博客的作者很牛!
这个可做进一步的学习-网络socket编程指南:http://blog.csdn.net/hello_wyq/article/details/1180747
如果想做深一步的研究可以使用wireshark做协议抓取分析:
http://blog.csdn.net/hnney/article/details/5604677
上一篇: 从ipa中提取png文件
下一篇: 养生饮食禁忌 猕猴桃不能和七种食物一起吃