UDP提供無連接服務
UDP的數據格式:
#ifdef __FAVOR_BSD
struct udphdr {
u_int16_t uh_sport; /* source port */
u_int16_t uh_dport; /* destination port */
u_int16_t uh_ulen; /* udp length */
u_int16_t uh_sum; /* udp checksum */
};
#else
struct udphdr {
u_int16_t source;
u_int16_t dest;
u_int16_t len;
u_int16_t check;
};
#endif
TCP是面向連接的,切實全雙工的。
TCP協議數據段:
struct tcphdr {
WORD SourPort;
WORD DestPort;
DWORD SeqNo;
DWORD AckNo;
BYTE HLen;
BYTE Flag;
WORD Window;
WORD ChkSum;
WORD UrgPtr;
/* Put options here. */
};
TCP的time_wait:
執行主動關閉的那端進入這種狀態。這個端點在該狀態的持續時間是2MSL(最長分節生命周期)。
網絡中的幾類地址:
物理地址:即MAC地址 (數據鏈路層)
邏輯地址:即IP地址
端口地址:應用程序端口號
域名地址:取代IP地址記憶
IPv4的套接字地址:
/* Internet address. */
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
IPv6套接字地址:
struct in6_addr
{
union
{
uint8_t __u6_addr8[16];
#ifdef __USE_MISC
uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
#endif
} __in6_u;
#define s6_addr __in6_u.__u6_addr8
#ifdef __USE_MISC
# define s6_addr16 __in6_u.__u6_addr16
# define s6_addr32 __in6_u.__u6_addr32
#endif
};
/* Ditto, for IPv6. */
struct sockaddr_in6
{
__SOCKADDR_COMMON (sin6_);
in_port_t sin6_port; /* Transport layer port # */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* IPv6 scope-id */
};
tcp套接字網絡編程的主要實現過程:
關於socket函數:
#include
int socket(int family,int type,int protocol)
family指明了協議族。例如:AF_INET(IPv4協議);AF_INET6(IPV6協議);AF_ROUTE(路由套接口)
type指明了套接字類型。例如:
SOCK_STREAM字節流套接口(TCP);
SOCK_DGRAM數據包套接口(UDP);
SOCK_RAW原始套接口
protocol指明協議標志
端口:訪問網絡不同計算機程序的編號
IP地址標記了不同的電腦,而域名則是iP地址便於記憶的別名
edemon@ubuntu:~$ ping www.baidu.com
PING www.a.shifen.com (119.75.218.70) 56(84) bytes of data.
vcq9oaNUQ1C6zVVEULvh0/a1vc2syrHOqrbguPbTptPDs8zQ8s2syrGyoreivfjQ0M2o0MW1xM7KzOKjrM6qwcvH+LfWsrvNrLXE06bTw734s8y1xNOm08O6zcGsvdOjrNDo0qrKudPD06bTw7PM0PLT61RDUC9JUNCt0um9u7ultcTM173T19a907/aoaM8YnIgLz4NCszXvdPX1qO6tKvK5LLj0K3S6Su2y7/ausUrSVC12Na3PC9wPg0KPGgzIGlkPQ=="域名和ip的相互轉換">域名和IP的相互轉換
IP地址和網絡地址:
網絡地址很大一部分是由地址掩碼決定的。
IP 轉 domain:
#include
#include
#include
#include
#include
#include
#include
int main()
{
struct hostent *host;
char addr[]="208.108.249.216"; //202.108.249.216 61.135.169.121 http://61.135.169.125/
struct in_addr in;
struct sockaddr_in addr_in;
host=gethostbyaddr(addr,sizeof(addr),AF_INET);
if(host!=(struct hostent *)NULL)
{
memcpy(&addr_in.sin_addr.s_addr,host->h_addr,4);
in.s_addr=addr_in.sin_addr.s_addr;
printf("Domain name: %s \n",host->h_name);
printf("IP length: %d\n",host->h_length);
printf("Type: %d\n",host->h_addrtype);
printf("IP : %s \n",inet_ntoa(in));
}
else
{
char *buffer = strerror(errno);
printf("%s\n",buffer);
}
}
/*
Domain name: 50-48-56-46.dr05.nrwc.ny.frontiernet.net
IP length: 16
Type: 2
IP : 50.48.56.46
*/
domain 轉IP:
#include
#include
#include
#include
#include
#include
int main()
{
struct hostent *host;
char hostname[]="www.baidu.com";
char hostname2[]="www.012www.com";
struct in_addr in;
struct sockaddr_in sock_in;
extern int h_errno;
if((host=gethostbyname(hostname))!=NULL){
//printf("%s\n",host->h_addr);
memcpy(&sock_in.sin_addr.s_addr, host->h_addr, 4);
in.s_addr=sock_in.sin_addr.s_addr;
printf("Domain name: %s \n",hostname);
printf("IP length: %d\n",host->h_length);
printf("Type: %d\n",host->h_addrtype);
/*inet_ntoa()用來將參數in所指的網絡二進制的數字轉換成網絡地址,
然後將指向此網絡地址字符串的指針返回。*/
printf("IP : %s \n",inet_ntoa(in));
}
else{
printf("Domain name: %s \n",hostname);
printf("error: %d\n",h_errno);
printf("%s\n",hstrerror(h_errno));
}
if((host=gethostbyname(hostname2))!=NULL){
memcpy(&sock_in.sin_addr.s_addr,host->h_addr,4);
in.s_addr=sock_in.sin_addr.s_addr;
printf("Domain name: %s \n",hostname2);
printf("IP : %s \n",inet_ntoa(in));
printf("IP length: %d\n",host->h_length);
printf("Type: %d\n",host->h_addrtype);
}
else{
printf("Domain name: %s \n",hostname2);
printf("error: %d\n",h_errno);
printf("%s\n",hstrerror(h_errno));
}
}
/*
Domain name: www.baidu.com
IP length: 4
Type: 2
IP : 61.135.169.121
Domain name: www.012www.com
error: 1
Unknown host
*/
對於不同的socket domain定義了一個通用的數據結構——套接字結構:
struct sockaddr
{
unsigned short int sa_family;
char sa_data[14];
};
sa_family 為調用socket()時的domain參數,即AF_xxxx值。
sa_data 最多使用14個字符長度。
此sockaddr結構會因使用不同的socket domain而有不同結構定義,例如使用AF_INET domain,其
socketaddr結構定義便為
struct socketaddr_in
{
unsigned short int sin_family;
uint16_t sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr
{
uint32_t s_addr;
};
sin_family 即為sa_family
sin_port 為使用的port編號
sin_addr.s_addr 為IP 地址
sin_zero 未使用。
struct hostent{
char * h_name;
char ** h_aliases;
short h_addrtype;
short h_length;
char ** h_addr_list;
};
主機字節序和網絡字節序的轉換:
unsigned long int htonl(unsigned long int hostlong);
將32位主機字符順序轉換成網絡字符順序
unsigned short int htons(unsigned short int hostshort);
將16位主機字符順序轉換成網絡字符順序
unsigned long int ntohl(unsigned long int netlong);
將32位網絡字符順序轉換成主機字符順序
unsigned short int ntohs(unsigned short int netshort);
將16位網絡字符順序轉換成主機字符順序
#include
#include
int main()
{
long local;
int port;
local =123456;
port=123456;
printf("host 32 %ld -> net 32: %d\n",local,htonl(local));
printf("host 16 %d -> net 16: %d\n",port,htons(port));
printf("net 32 %d -> host 32: %d\n",htonl(local),ntohl(htonl(local)));
printf("net 16 %d -> host 16: %d\n",htons(port),ntohs(htons(port)));
return 0;
}
/*
host 32 123456 -> net 32: 1088553216
host 16 123456 -> net 16: 16610
net 32 1088553216 -> host 32: 123456
net 16 16610 -> host 16: 57920
*/
long inet_addr(char *str)
將網絡IP地址轉化成10進制長整型數
頭文件:
#include
#include
#include
char *inet_ntoa(struct in_addr in);
將整數型地址轉化成點分十進制地址(在計算機中經常使用長整型數表示IP地址)
表頭文件
#include
#include
#include
例子:
#include
#include
#include
#include
int main()
{
struct in_addr ip;
ip.s_addr=16885952;
char *str=inet_ntoa(ip);
printf("%s\n",str);
printf("%u\n", inet_addr(str));
}
/*
192.168.1.1
16885952
*/
cat /etc/protocols
……
daytime 13/tcp
daytime 13/udp
netstat 15/tcp
qotd 17/tcp quote
msp 18/tcp # message send protocol
msp 18/udp
chargen 19/tcp ttytst source
chargen 19/udp ttytst source
ftp-data 20/tcp
ftp 21/tcp
……
獲取服務信息:
#include
#include
int main()
{
struct servent *ser;
if( ser=getservbyname("http","tcp")) //通過名字獲取網絡服務
{
printf("name : %s\n",ser->s_name);
printf("net port: %d ", ser->s_port);
printf("host port : %d\n",ntohs(ser->s_port)); //將16位網絡字符順序轉換成主機字符順序
printf("protocol:%s\n",ser->s_proto);
printf("alias: %s\n",ser->s_aliases[0]);
}
else
{
printf("there is no such a service.\n");
}
puts("---------------------------------------------");
if( ser=getservbyname("hehe","tcp"))
{
printf("name : %s\n",ser->s_name);
printf("net port: %d ", ser->s_port);
printf("host port : %d\n",ntohs(ser->s_port));
printf("protocol:%s\n",ser->s_proto);
printf("alias: %s\n",ser->s_aliases[0]);
}
else
{
printf("there is no such a service.\n");
}
puts("---------------------------------------------"); //通過端口號獲取網絡服務
if( ser=getservbyport(htons(21),"tcp")) //將16位主機字符順序轉換成網絡字符順序
{
printf("name : %s\n",ser->s_name);
printf("net port: %d ", ser->s_port);
printf("host port : %d\n",ntohs(ser->s_port));
printf("protocol:%s\n",ser->s_proto);
printf("alias: %s\n",ser->s_aliases[0]);
}
else
{
printf("there is no such a service.\n");
}
}
/*
name : http
net port: 20480 host port : 80
protocol:tcp
alias: www
---------------------------------------------
there is no such a service.
---------------------------------------------
name : ftp
net port: 5376 host port : 21
protocol:tcp
alias: (null)
*/
void herror(const char *s);
可以顯示網絡函數上出現的錯誤。先輸出這個字符串,然後輸出錯誤信息。
extern int h_errno
可以捕獲錯誤編號。
char *hstrerror(int err)
返回錯誤編碼對應的錯誤信息。
三個和錯誤相關的函數或變量都與頭文件netdb.h相關
#include
#include
int main()
{
herror("result : ");
extern int h_errno;
printf("%s\n", hstrerror(h_errno));
}
系統支持的協議:
ip 0 IP # internet protocol, pseudo protocol number
hopopt 0 HOPOPT # IPv6 Hop-by-Hop Option [RFC1883]
icmp 1 ICMP # internet control message protocol
igmp 2 IGMP # Internet Group Management
ggp 3 GGP # gateway-gateway protocol
ipencap 4 IP-ENCAP # IP encapsulated in IP (officially ``IP'')
st 5 ST # ST datagram mode
tcp 6 TCP # transmission control protocol
egp 8 EGP # exterior gateway protocol
igp 9 IGP # any private interior gateway (Cisco)
pup 12 PUP # PARC universal packet protocol
udp 17 UDP # user datagram protocol
hmp 20 HMP # host monitoring protocol
xns-idp 22 XNS-IDP # Xerox NS IDP
rdp 27 RDP # "reliable datagram" protocol
iso-tp4 29 ISO-TP4 # ISO Transport Protocol class 4 [RFC905]
dccp 33 DCCP # Datagram Congestion Control Prot. [RFC4340]
xtp 36 XTP # Xpress Transfer Protocol
ddp 37 DDP # Datagram Delivery Protocol
idpr-cmtp 38 IDPR-CMTP # IDPR Control Message Transport
ipv6 41 IPv6 # Internet Protocol, version 6
ipv6-route 43 IPv6-Route # Routing Header for IPv6
ipv6-frag 44 IPv6-Frag # Fragment Header for IPv6
idrp 45 IDRP # Inter-Domain Routing Protocol
rsvp 46 RSVP # Reservation Protocol
gre 47 GRE # General Routing Encapsulation
esp 50 IPSEC-ESP # Encap Security Payload [RFC2406]
ah 51 IPSEC-AH # Authentication Header [RFC2402]
skip 57 SKIP # SKIP
ipv6-icmp 58 IPv6-ICMP # ICMP for IPv6
ipv6-nonxt 59 IPv6-NoNxt # No Next Header for IPv6
ipv6-opts 60 IPv6-Opts # Destination Options for IPv6
rspf 73 RSPF CPHB # Radio Shortest Path First (officially CPHB)
vmtp 81 VMTP # Versatile Message Transport
eigrp 88 EIGRP # Enhanced Interior Routing Protocol (Cisco)
ospf 89 OSPFIGP # Open Shortest Path First IGP
ax.25 93 AX.25 # AX.25 frames
ipip 94 IPIP # IP-within-IP Encapsulation Protocol
etherip 97 ETHERIP # Ethernet-within-IP Encapsulation [RFC3378]
encap 98 ENCAP # Yet Another IP encapsulation [RFC1241]
# 99 # any private encryption scheme
pim 103 PIM # Protocol Independent Multicast
ipcomp 108 IPCOMP # IP Payload Compression Protocol
vrrp 112 VRRP # Virtual Router Redundancy Protocol [RFC5798]
l2tp 115 L2TP # Layer Two Tunneling Protocol [RFC2661]
isis 124 ISIS # IS-IS over IPv4
sctp 132 SCTP # Stream Control Transmission Protocol
fc 133 FC # Fibre Channel
mobility-header 135 Mobility-Header # Mobility Support for IPv6 [RFC3775]
udplite 136 UDPLite # UDP-Lite [RFC3828]
mpls-in-ip 137 MPLS-in-IP # MPLS-in-IP [RFC4023]
manet 138 # MANET Protocols [RFC5498]
hip 139 HIP # Host Identity Protocol
shim6 140 Shim6 # Shim6 Protocol [RFC5533]
wesp 141 WESP # Wrapped Encapsulating Security Payload
rohc 142 ROHC # Robust Header Compression
獲取協議信息的例子:
#include
#include
int main()
{
struct protoent *pro;
pro=getprotobyname("tcp"); // by protocol name
printf("protocol name : %s\n",pro->p_name);
printf("protocol number : %d\n",pro->p_proto);
printf("protocol alias: %s\n",pro->p_aliases[0]);
puts("------------------------------");
pro=getprotobynumber(2); // by protocol number
printf("protocol name : %s\n",pro->p_name);
printf("protocol number : %d\n",pro->p_proto);
printf("protocol alias: %s\n",pro->p_aliases[0]);
}
protocol name : tcp
protocol number : 6
protocol alias: TCP
------------------------------
protocol name : igmp
protocol number : 2
protocol alias: IGMP
自己的主機IP地址:
ifconfig
下面是ANSI C 中的字節操作函數:
1.
void *memset(void *s, int c, size_t n);
memset()會將參數s所指的內存區域前n個字節以參數c填入,然後返回指向s的指針。
2.
void *memcpy(void *dest, const void *src, size_t n);
memcpy()用來拷貝src所指的內存內容前n個字節到dest所指的內存地址上。與strcpy()不同的是,
memcpy()會完整的復制n個字節,不會因為遇到字符串結束’\0’而結束。
3.
int memcmp(const void *s1, const void *s2, size_t n);
memcmp()用來比較s1和s2所指的內存區間前n個字符。
字符串IP和二進制IP的相互轉換(僅能處理IPv4):
int inet_aton(const char *cp, struct in_addr *inp);
函數說明
inet_aton()用來將參數cp所指的網絡地址字符串轉換成網絡使用的二進制的數字,然後存於參數inp所
指的in_addr結構中。
返回值
成功則返回非0值,失敗則返回0。
#include
#include
#include
#include
int main(){
struct in_addr numstr;
char IP[20]="192.160.8.60";
if(inet_aton(IP,&numstr)){
printf("0x%x\n",numstr.s_addr);
}
return 0;
}
/*
0x3c08a0c0
*/
inet_pton
int inet_pton(int af, const char *src, void *dst);
用於點分十進制轉成二進制整數(16進制)。
成功返回1,af無效返回0,出錯返回-1,指針dst存儲得到的IP地址。
inet_ntop
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
二進制整數(16進制)轉換成點分十進制。
兩者可以同時處理IPv4, IPv6.
AF_INET 是一個IPv4 網絡協議的套接字類型
#include
#include
int main ()
{
char IPstr1[20]="192.18.0.9";
char IPstr2[20];
struct in_addr s; // IPv4地址結構體
printf("%d\n",AF_INET);
inet_pton(AF_INET, IPstr1, (void *)&s);
printf("inet_pton: 0x%x\n", s.s_addr); //二進制整數(16進制)字節序
inet_ntop(AF_INET, (void *)&s, IPstr2, sizeof(IPstr2));
printf("inet_ntop: %s\n", IPstr2); //點分十進制
return 0;
}
/*
2
inet_pton: 0x90012c0
inet_ntop: 192.18.0.9
*/
從進程到內核傳遞套接字地址結構的函數常有3個——bind, connect, sendto
從內核到進程傳遞套接字地址結構的函數常有四個accept, recvfrom, getsockname, getpeername
bind()對socket定位