UNIX网络编程-结构体和相关函数

目录

相关结构体

值-结果参数

字节排序函数

字节操纵函数

一些扩展的辅助函数


 

 

相关结构体

IPv4结构体

除非涉及路由套接字,否则不用设置和检查 sin_len 字段
POSIX规范只要求结构中的3个字段,sin_family,sin_addr,sin_port

sin_family对应的是 sa_family_t
sin_port  对应的是 in_port_t
sin_addr  对应的是in_addr结构体,in_addr包含了唯一一个字段,类型是 in_arr_t

其他类型的 u_char,u_shrot,u_int,u_long都是无符号的,但都过时了
IPv4地址和TCP或UDP端口号再套接字地址中总是以网络字节序来存储的
32位IPv4地址存在两种访问方式
1.serv.sin_addr 将按in_addr结构音乐其中的32位IPv4地址
2.serv.sin_addr_s_addr将按in_addr_t(通常是一个无符号的32位整数)引用同一个32位IPv4地址
我们必须正确的使用IPv4地址,尤其在将他作为函数的参数时,因为编译器对传递结构和传递整数的处理是
完全不同的


struct in_addr {
    in_addr_t s_addr;  /* 32-bit IPv4 address, network byte ordered */
}

struct sockaddr_in {
    uint8-t      sin_len;     /*  length of structure */
    sa_family_t  sin_family;  /*  AF_INET  */
    in_port_t    sin_port     /* 16 bit TCP or UDP port number network byte ordered */
    struct in_addr sin_addr;  /* 32-bit IPv4 address network byte ordered */
    char        sin_zero[8];  /* unused */
}

POXIS规范要求的数据类型

数据类型 说明 头文件
int8_t 带符号的8位整数
uint8_t 无符号的8位整数
int16_t 带符号的16位整数
uinit16_t 无符号的16位整数
int32_t 带符号的32位整数
uint32_t 无符号的32位整数
sa_family_t 套接字地址结构的地址族
socklen_t 套接字地址结构的长度,一般为uint32_t
in_addr_t

IPv4地址,一般为uint32_t

in_port_t TCP或UDP端口,一般为uint16_t

 


通用套接字地址结构

当作为一个参数传递进任何套接字函数时,套接字地址结构总是以引用形式(也就是以指向该结构的指针)来传递,然后以这样的指针作为参数之一的任何套接字必须处理来自所支持的任何协议族的套接字地址结构
在如何生命所传递的数据类型上存在一个问题,ANSI C后解决办法很简单 void * 是通用的指针类型,然而套接字函数是在ANSI C之前定义的,采用的办法是定义一个通用的套接字地址结构


struct sockaddr {
    uint8_t      sa_len;
    sa_family_t  sa_family;      /* address family: AF_xxx value */
    char         sa_data[14];    /* protocol-specific address */
}

bind 函数的原型就是

int bind(int, struct sockaddr *, socklen_t);


IPv6套接字地址结构


struct in6_addr {
    unit8_t  sa_addr[16];
};

#define SIN6_LEN    /* required for compile-time tests */

struct sockaddr_in6 {
    uint8_t      sin6_len;     /* length of this struct */   
    sa_family_t  sin6_family;  /* AF_INT6 */
    in_port_t    sin6_port;    /* transport layer port network byte ordered */
    uint32_t     sin6_flowinfo;/* flow information, undefined */
    struct in6_addr sin_addr;  /* IPv6 address network byte ordered */
    uint32_t     sin6_scope_id /* set of interface for a scope */
};

 

套接字地址结构比较

对比IPv4,IPv6,unix域套接字,数据链路和存储
前两种长度是固定的,unix域套接字和数据链路结构是可变长度的
UNIX网络编程-结构体和相关函数_第1张图片

 


值-结果参数

从近处到内核参数套接字地址结构的函数有3个,bind,connect,sendto
比如

struct sockaddr_in serv;
connect(sockfd,(struct sockaddr *)&serv, sizeof(serv));

UNIX网络编程-结构体和相关函数_第2张图片

从内核到进程传递套接字地址结构的函数有4个,accept,recvfrom,getsockname,getpeername
比如

struct sockaddr_un cli;
socklen_t len;
len = sizeof(cli);
getpeername(unixfd, (struct sockaddr *)&cli, &len);

UNIX网络编程-结构体和相关函数_第3张图片

 

 

字节排序函数

大端小端测试

UNIX网络编程-结构体和相关函数_第4张图片
打印当前机器是小头派还是大头派类型
注意,如果没有加这两个头文件
#include
#include
编译时候会报  警告:隐式声明与内建函数‘printf’不兼容   这个错误
编译: gcc -o byteorder byteorder.c

#include   
#include   
  
int main(int argc,char **argv) {  
    union {  
        short s;  
        char c[sizeof(short)];  
    }un;  
  
    un.s=0x0102;  
    if(sizeof(short) ==2) {  
        if(un.c[0]==1 && un.c[1]==2) {  
            printf("big-endian\n");  
        }  
        else if(un.c[0]==2 && un.c[1]==1) {  
            printf("little-endian\n");  
        }  
        else {  
            printf("unknown\n");  
        }  
    }  
    else {  
        printf("sizeof(short) = %d\n",sizeof(short));  
    }  
    return 0;  
  
}  


主机自己和网络字节序之间的相互转换 函数

#include 
uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(uint32_t host32bitvalue);
uint16_t ntohs(uint16_t net16bitvalue);
uint32_t ntohl(uint32_t net32bitvalue);

n代表host,n代表network,s代表short,l代表long

 

 


字节操纵函数

Berkely的函数

#include 
void bzero(void *dest, size_t nbytes);
void bcopy(const void *str, void *dest, size_t nbytes);
int bcmp(const void *ptr1, const void *prt2, size_t nbytes);

ANSI C的函数

#include 
void *memset(void *dest, int c, size_t len);
void *memcpy(void *dest, const void *str, size_t nbytes);
int memcmp(const void *ptr1, const void *ptr2, size_t nbytes);


地址转换函数

#include 

//将strptr所指的字符串换成32位的网络字节序二进制值,并保存到到addrptr中
int inet_aton(const char *strptr, struct in_addr *addrptr);

//将字符串转为32位二进制网络字节序的IPv4地址
in_addr_t inet_addr(const char *strptr);

//返回指向一个点分十进制数串的指针
char *inet_ntoa(struct in_addr inadr);

新的地址转换函数

#include 
//family可以是AF_INET,也可以使AF_INET6,将strptr指针指向的字符串,换成二进制值保存到addrptr中
int inet_pton(int family, const char *strptr, void *addrptr);

//将数值格式addrptr转换到哦表达式格式strptr,len是目标存储单元的大小
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

UNIX网络编程-结构体和相关函数_第5张图片

 

10进制ip到二进制转换的例子

#include   
#include   
#include   
#include   
#include   
#include   
int main (void) {  
    char IPdotdec[20]; /* 存放点分十进制IP地址  */
    struct in_addr s; /* IPv4地址结构体  */
    /* 输入IP地址  */
    printf("Please input IP address: ");  
    scanf("%s", IPdotdec);  
    /* 转换  */
    inet_pton(AF_INET, IPdotdec, (void *)&s);  
    printf("inet_pton: 0x%x\n", s.s_addr); /* 注意得到的字节序  */
    /* 反转换  */
    inet_ntop(AF_INET, (void *)&s, IPdotdec, 16);  
    printf("inet_ntop: %s\n", IPdotdec);  
  
}  


设置sin_addr 变量的列子,从结果看
sockaddr_in.sin_addr 和 sockaddr_in.sin_addr.s_addr 这两种方式读取结果是一样的

#include 
#include 
#include 

int main(int argc, char *argv[]) {

    //struct sockaddr_in *addr = malloc(sizeof(struct sockaddr_in));
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9527);
    //addr->sin_addr = inet_pton(
    inet_pton(AF_INET, "47.98.18.8",&addr.sin_addr.s_addr);

    printf("af->port %d\n",addr.sin_port);
    printf("af-addr %d\n", addr.sin_addr);   
    printf("af-addr %d\n", addr.sin_addr.s_addr);   
  
    printf("############################################\n");
    struct sockaddr_in addr_1;
    addr_1.sin_family = AF_INET;
    addr_1.sin_port = htons(8080);
    inet_pton(AF_INET, "192.168.1.100", &addr_1.sin_addr);
    printf("af->port %d\n", addr_1.sin_port);
    printf("af->addr %d\n", addr_1.sin_addr);
    printf("af->addr %d\n", addr_1.sin_addr.s_addr);
    
    
    struct sockaddr_in *addr_2 = malloc(sizeof(struct sockaddr_in));
    addr_2->sin_family = AF_INET;
    addr_2->sin_port = htons(1234);
    inet_pton(AF_INET, "1.2.3.4", &(addr_2->sin_addr));

    printf("======================\n");
    printf("af->port %d\n",addr_2->sin_port);
    printf("af-addr %d\n", addr_2->sin_addr.s_addr);   
    printf("af->addr %d\n",addr_2->sin_addr);
    return 0;
}

//执行结果
af->port 14117
af-addr 135422511
af-addr 135422511
############################################
af->port 36895
af->addr 1677830336
af->addr 1677830336
======================
af->port 53764
af-addr 67305985
af->addr 67305985

 

一些扩展的辅助函数

一次读n个字节的read函数

#include   
#include   
#include   
#include   
#include   
  
ssize_t readn(int fd,void *vptr, size_t n) {  
    size_t nleft;  
    ssize_t nread;  
    char *ptr;  
  
    ptr = vptr;  
    nleft = n;  
    while(nleft > 0) {  
        if( (nread=read(fd,ptr,nleft)) < 0) {  
            if(nread > 0) {  
                nread = 0;  
            } else {  
                return -1;  
            }  
        }  
        else if(nread == 0) {  
            break;  
        }  
        nleft -= nread;  
        ptr += nread;     
    }         
    return (n-nleft);  
}             
                      
int main(int argc, char **argv) {  
                
    int r_fd=open("/data0/test/test.log",O_RDONLY);  
    char buf[100];  
    readn(r_fd,buf,20);   
    printf("%s\n",buf);  
      
    return 0;  
}  

 

一次写入n个字节的write函数

#include   
#include   
#include   
#include   
#include   
  
  
ssize_t writen(int fd, const void *vptr, size_t n) {  
    size_t nleft;  
    ssize_t nwritten;  
    const char *ptr;  
  
    ptr = vptr;  
    nleft = n;  
    while(nleft > 0) {  
        if( (nwritten = write(fd,ptr,nleft)) <= 0) {  
            if(nwritten <0) {  
                nwritten = 0;  
            }  
            else {  
                return -1;  
            }  
        }  
        nleft -= nwritten;  
        ptr += nwritten;  
    }  
    return n;  
}  
  
int main(int argc, char **argv) {  
    int w_fd = open("/data0/test/write.log",O_WRONLY|O_CREAT,0755);  
    writen(w_fd,"test he he",10);  
    return 0;  
}  

 

一次读取一行的read函数

#include   
#include   
#include   
#include   
#include   
  
ssize_t readline(int fd, void *vptr, size_t maxlen) {  
    ssize_t n,rc;  
    char c, *ptr;  
  
    ptr = vptr;  
    for(n=1;n

一次读一行的read函数(加入缓存的改进版)

#include   
#include   
#include   
#include   
#include   
#include   
#define MAX_LINE 10240

static int read_cnt;
static char *read_ptr;
static char read_buf[MAX_LINE];

size_t my_read(int fd, char*ptr) {
    if(read_cnt <= 0) {
        again:
        if( (read_cnt = read(fd,read_buf,sizeof(read_buf))) < 0 ) {
            if(errno == EINTR) {
                goto again;
            }
            return (-1);
        } else if( read_cnt == 0) {
            return (0);
        }
        read_ptr = read_buf;
    }
    read_cnt--;
    *ptr = *read_ptr++;
    return(1);
}
 
ssize_t readline(int fd, void *vptr, size_t maxlen) {  
    ssize_t n,rc;  
    char c, *ptr;  
  
    ptr = vptr;  
    for(n=1;n

 

你可能感兴趣的:(网络,Linux)