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地址,尤其在将他作为函数的参数时,因为编译器对传递结构和传递整数的处理是
完全不同的
<netinet/in.h>
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位整数 | <sys/types.h> |
uint8_t | 无符号的8位整数 | <sys/types.h> |
int16_t | 带符号的16位整数 | <sys/types.h> |
uinit16_t | 无符号的16位整数 | <sys/types.h> |
int32_t | 带符号的32位整数 | <sys/types.h> |
uint32_t | 无符号的32位整数 | <sys/types.h> |
sa_family_t | 套接字地址结构的地址族 | <sys/socket.h> |
socklen_t | 套接字地址结构的长度,一般为uint32_t | <sys/socket.h> |
in_addr_t | IPv4地址,一般为uint32_t |
<netinet/in.h> |
in_port_t | TCP或UDP端口,一般为uint16_t | <netinet/in.h> |
通用套接字地址结构
当作为一个参数传递进任何套接字函数时,套接字地址结构总是以引用形式(也就是以指向该结构的指针)来传递,然后以这样的指针作为参数之一的任何套接字必须处理来自所支持的任何协议族的套接字地址结构
在如何生命所传递的数据类型上存在一个问题,ANSI C后解决办法很简单 void * 是通用的指针类型,然而套接字函数是在ANSI C之前定义的,采用的办法是定义一个通用的套接字地址结构
<sys/socket.h>
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套接字地址结构
<netinet/in.h>
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域套接字和数据链路结构是可变长度的
值-结果参数
从近处到内核参数套接字地址结构的函数有3个,bind,connect,sendto
比如
struct sockaddr_in serv;
connect(sockfd,(struct sockaddr *)&serv, sizeof(serv));
从内核到进程传递套接字地址结构的函数有4个,accept,recvfrom,getsockname,getpeername
比如
struct sockaddr_un cli;
socklen_t len;
len = sizeof(cli);
getpeername(unixfd, (struct sockaddr *)&cli, &len);
字节排序函数
大端小端测试
打印当前机器是小头派还是大头派类型
注意,如果没有加这两个头文件
#include <stdio.h>
#include <stdlib.h>
编译时候会报 警告:隐式声明与内建函数‘printf’不兼容 这个错误
编译: gcc -o byteorder byteorder.c
#include <stdio.h>
#include <stdlib.h>
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;
}
10进制ip到二进制转换的例子
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
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);
}
一些扩展的辅助函数
一次读n个字节的read函数
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
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 <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
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 <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
ssize_t readline(int fd, void *vptr, size_t maxlen) {
ssize_t n,rc;
char c, *ptr;
ptr = vptr;
for(n=1;n<maxlen;n++) {
if( (rc=read(fd,&c,1)) ==1) {
*ptr++ = c;
if(c=='\n') {
break;
}
}
else if(rc == 0) {
*ptr = 0;
return (n-1);
}
else {
if(rc < 0) {
continue;
}
return -1;
}
}
*ptr = 0;
return n;
}
int main(int argc, char **argv) {
int r_fd=open("/data0/test/test.log",O_RDONLY);
char buf[100];
readline(r_fd,buf,20);
printf("%s\n",buf);
return 0;
}