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地址,尤其在將他作為函式的引數時,因為編譯器對傳遞結構和傳遞整數的處理是
完全不同的
<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;
}