linux socket编程学习

来源:互联网 发布:c语言如何运用函数库 编辑:程序博客网 时间:2024/05/17 22:57

    针对上篇博客的代码做一些研究和分析。

    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这两篇博客的作者很牛!

    4. close(sockfd)和shutdown(sockfd, 2)

    这两种用法类似,他们最大的不同就是在套接字被多个进程共享时,shutdown()会影响到所有进程中socket的使用,close只影响本进程的文件描述符。

    参考:http://stackoverflow.com/questions/4160347/close-vs-shutdown-socket 和 http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#closedown

    这个可做进一步的学习-网络socket编程指南:http://blog.csdn.net/hello_wyq/article/details/1180747

    如果想做深一步的研究可以使用wireshark做协议抓取分析:

    http://blog.csdn.net/hnney/article/details/5604677

 

原创粉丝点击