美文网首页
读pdcp_lte_logger.c代码学会的新知识

读pdcp_lte_logger.c代码学会的新知识

作者: natsumi | 来源:发表于2015-02-22 22:47 被阅读135次

    1. sockaddr_in , sockaddr , in_addr区别

    整理自http://blog.csdn.net/jackychu/article/details/4461927
    另一篇相关文章以备参考:struct sockaddr与struct sockaddr_in的区别和联系

    1.1 sockaddr:通用的socket地址

    struct sockaddr {
        unsigned   short   sa_family;     
        char   sa_data[14];     
    };  
    

    1.2 sockaddr_in:Internet socket

    sockaddr和sockaddr_in可以进行类型转换

    struct sockaddr_in {  
        short   int   sin_family;     
        unsigned   short   int   sin_port;     
        struct   in_addr   sin_addr;     
        unsigned   char   sin_zero[8];     
    };
    

    1.3 in_addr:32位IP地址。

    struct   in_addr   {  
        union {
            struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
            struct { u_short s_w1,s_w2; } S_un_w;
            u_long S_addr; 
        } S_un;
        #define s_addr  S_un.S_addr
    };
    

    inet_addr()是将一个点分制的IP地址(如192.168.0.1)转换为上述结构中需要的32位IP地址(0xC0A80001)。

    1.4 用法

    填值的时候使用sockaddr_in结构,而作为函数(如socket, listen, bind等)的参数传入的时候转换成sockaddr结构就行了,毕竟都是16个字符长。

    通常的用法是:

    int   sockfd;  
    struct   sockaddr_in   my_addr;  
    sockfd   =   socket(AF_INET,   SOCK_STREAM,   0);     
       
    my_addr.sin_family   =   AF_INET;     
    my_addr.sin_port   =   htons(MYPORT);     
    my_addr.sin_addr.s_addr   =   inet_addr("192.168.0.1");  
       
    bzero(&(my_addr.sin_zero),   8);     
        
    bind(sockfd,   (struct   sockaddr   *)&my_addr,   sizeof(struct   sockaddr));
    

    可以用C++做个不太准确的假设。
    sockaddr是base class
    sockaddr_in等是derived(派生) class
    如此一来,bind, connect, sendto, recvfrom等函数就可以使用base class
    来处理多种不同的derived class了。
    但是实际上,这是没有继承关系数据结构(C嘛),所以需要强制造型来转换数据类型。正因为如此,在sendto的时候需要给出len长度,因为不同的sockaddr_xx实现长度并不相同。

    1.5 P.S.名词解析:

    • 主机字节序:
      不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。最常见的有两种 1.Little endian:低字节存高地址,高字节存低地址 2.Big endian:低字节存低地址,高字节存高地址
    • 网络字节序:
      网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。
    • 字节序转换
      为了进行转换bsd socket提供了转换的函数,有下面四个网络与主机字节转换函数:htons ntohs htonl ntohl
      s==>short
      l==>long
      h==>host
      n==>network
    • htons:把unsigned short类型从主机序转换到网络序
    • htonl:把unsigned long类型从主机序转换到网络序
    • ntohs:把unsigned short类型从网络序转换到主机序
    • ntohl:把unsigned long类型从网络序转换到主机序

    在使用little endian的系统中 这些函数会把字节序进行转换
    在使用big endian类型的系统中这些函数会定义成空宏

    2. gethostbyname()的返回值类型struct hostent

    整理自http://blog.csdn.net/wangjie5540/article/details/23429083

    2.1 hostent数据结构:

    struct hostent { 
       char *h_name; 
       char **h_aliases; 
       int h_addrtype; 
       int h_length; 
       char **h_addr_list; 
    }; 
    #define h_addr h_addr_list[0]
    

    这个数据结构的详细资料:
    struct hostent:

    • h_name – 地址的正式名称。
    • h_aliases – 空字节-地址的预备名称的指针。
    • h_addrtype –地址类型; 通常是AF_INET。
    • h_length – 地址的比特长度。
    • h_addr_list – 零字节-主机网络地址指针。网络字节顺序。
    • h_addr - h_addr_list中的第一地址。

    2.2 gethostbyname()的使用

    相当简单,你只是传递一个保存机器名的字符串(例如 "whitehouse.gov") 给gethostbyname(),然后从返回的数据结构struct hostent中获取信息。

    gethostbyname()成功时返回一个指向结构体hostent的指针,或者是个空(NULL) 指针。(但是和以前不同,不设置errno,h_errno设置错 误信息。请看下面的 herror()。)

    如何使用呢? 这个函数可不象它看上去那么难用。
    这里是个例子:

    int main(int argc, char *argv[]) { 
        struct hostent *h; 
        if (argc != 2) { /* 检查命令行 */ 
            fprintf(stderr,"usage: getip address "); 
            exit(1); 
        } 
        if ((h=gethostbyname(argv[1])) == NULL) { /* 取得地址信息 */ 
            herror("gethostbyname"); 
            exit(1); 
        } 
        printf("Host name : %s ", h->h_name); 
      printf("IP Address : %s ",inet_ntoa(*((struct in_addr *)h->h_addr))); 
        return 0; 
    } 
    

    注意
    在使用 gethostbyname() 的时候,你不能用perror() 打印错误信息 (因为 errno 没有使用),你应该调用 herror()。

    唯一也许让人不解的是输出 IP 地址信息。h->h_addr 是一个 char *, 但是 inet_ntoa() 需要的是 struct in_addr。因此,我转换 h->h_addr 成 struct in_addr *,然后得到数据。

    3. sendto():经socket传送数据

    整理自http://blog.csdn.net/tdk_root/article/details/7888574

    • 相关函数
      send , sendmsg,recv , recvfrom , socket
    • 表头文件
      #include < sys/types.h >
      #include < sys/socket.h >
    • 定义函数
    int sendto ( socket s , const void * msg, int len, unsigned int flags, const struct sockaddr * to , int tolen ) ;
    
    • 函数说明
      sendto() 用来将数据由指定的socket传给对方主机。
    • 参数们:
      s:已建好连线的socket,如果利用UDP协议则不需经过连线操作
      msg:指向欲连线的数据内容
      flags 一般设0,详细描述请参考send()。
      to:用来指定欲传送的网络地址,结构sockaddr请参考bind()。
      tolen:sockaddr的结构长度。
    • 返回值:
      成功则返回实际传送出去的字符数,失败返回-1,错误原因存于errno 中。
    • 错误代码
        EBADF 参数s非法的socket处理代码。
        EFAULT 参数中有一指针指向无法存取的内存空间。
        WNOTSOCK 参数 s为一文件描述词,非socket。
        EINTR 被信号所中断。
        EAGAIN 此动作会令进程阻断,但参数s的socket为不可阻断的。
        ENOBUFS 系统的缓冲内存不足。
        EINVAL 传给系统调用的参数不正确。
    • 范例
    #include < sys/types.h >
    #include < sys/socket.h >
    #include <arpa.inet.h>
    #define PORT 2345 /*使用的port*/
    main(){
      int sockfd,len;
      struct sockaddr_in addr;
      char buffer[256];
      /*建立socket*/
      if(sockfd=socket (AF_INET,SOCK_DGRAM,0))<0){
          perror (“socket”);
          exit(1);
      }
      /*填写sockaddr_in 结构*/
      bzero ( &addr, sizeof(addr) );
      addr.sin_family=AF_INET;
      addr.sin_port=htons(PORT);
      addr.sin_addr=hton1(INADDR_ANY) ;
      if (bind(sockfd, &addr, sizeof(addr))<0){
          perror(“connect”);
          exit(1);
      }
      while(1){
          bzero(buffer,sizeof(buffer));
          len = recvfrom(socket,buffer,sizeof(buffer), 0 , &addr &addr_len);
          /*显示client端的网络地址*/
          printf(“receive from %s\n “ , inet_ntoa( addr.sin_addr));
          /*将字串返回给client端*/
          sendto(sockfd,buffer,len,0,&addr,addr_len);
      }
    }
    

    执行 请参考recvfrom()

    相关文章

      网友评论

          本文标题:读pdcp_lte_logger.c代码学会的新知识

          本文链接:https://www.haomeiwen.com/subject/jesuxttx.html