linux C 网络编程基础
2016-07-05 11:01
399 查看
TCP/IP基础
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套接字编程
tcp套接字网络编程的主要实现过程:关于socket函数:
#include<sys/socket.h> 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.
socket的本义是插座。描述计算机之间不同程序之间的通信方式。TCP和UDP会遇到同时为多个应用程序同时并发进行通信的问题,为了区分不同的应用进程的应用和连接,需要使用应用程序与TCP/IP协议交互的套接字接口。
套接字:传输层协议+端口号+IP地址
域名和IP的相互转换
IP地址和网络地址:网络地址很大一部分是由地址掩码决定的。
IP 转 domain:
#include <stdio.h> #include <sys/socket.h> #include <netdb.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #include <string.h> 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 <stdio.h> #include <sys/socket.h> #include <netdb.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> 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 <stdio.h> #include <arpa/inet.h> 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<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>
char *inet_ntoa(struct in_addr in);
将整数型地址转化成点分十进制地址(在计算机中经常使用长整型数表示IP地址)
表头文件
#include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>
例子:
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> 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 <stdio.h> #include <netdb.h> 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 <stdio.h> #include <netdb.h> 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 <stdio.h> #include <netdb.h> 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 <stdio.h>
#include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h>
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 <stdio.h> #include <arpa/inet.h> 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定位
相关文章推荐
- 读书笔记-java网络编程-5URL和URI-URI类
- cordova支持私人HTTPS证书
- httpclient post
- 【Netty4.X】Unity客户端与Netty服务器的网络通信(一)
- uefi与win8 (根据网络资料整理)
- python学习:编写TCP服务器&UDP服务器
- 【百宝云网络验证】—易语言开发的系统
- 通信网络基础总结
- mysql-5.7.5-labs-http-documentation中文翻译-第三章 安装
- eclipse Exception in thread "http-bio-8080-exec-2" java.lang.OutOfMemoryError: PermGen space
- Java网络编程中inputStream.available()的使用方法
- Java网络编程socket
- 网络共享打印机只能打印第一页问题解决
- 网络参考模型
- The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path
- 深入理解HTTP Session
- HTTP基础:URL格式、 HTTP请求、响应、消息
- WebSocket 是什么原理?为什么可以实现持久连接?
- RESTful API 设计
- tcpdump