linux C 网络编程基础

来源:互联网 发布:linuxmysql源码安装 编辑:程序博客网 时间:2024/05/16 08:45

TCP/IP基础

UDP提供无连接服务
UDP的数据格式:

#ifdef __FAVOR_BSDstruct 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 */};#elsestruct 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:    16Type:    2IP          : 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:    4Type:    2IP          : 61.135.169.121 Domain name: www.012www.com error: 1Unknown 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: 1088553216host 16 123456 -> net 16: 16610net 32 1088553216 -> host 32: 123456net 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.116885952*/

系统支持的网络服务

cat /etc/protocols……daytime     13/tcpdaytime     13/udpnetstat     15/tcp   qotd        17/tcp      quotemsp     18/tcp              # message send protocolmsp     18/udpchargen     19/tcp      ttytst sourcechargen     19/udp      ttytst sourceftp-data    20/tcpftp     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 : httpnet port: 20480  host port : 80protocol:tcpalias: www---------------------------------------------there is no such a service.---------------------------------------------name : ftpnet port: 5376  host port : 21protocol:tcpalias: (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 numberhopopt  0   HOPOPT      # IPv6 Hop-by-Hop Option [RFC1883]icmp    1   ICMP        # internet control message protocoligmp    2   IGMP        # Internet Group Managementggp 3   GGP     # gateway-gateway protocolipencap 4   IP-ENCAP    # IP encapsulated in IP (officially ``IP'')st  5   ST      # ST datagram modetcp 6   TCP     # transmission control protocolegp 8   EGP     # exterior gateway protocoligp 9   IGP     # any private interior gateway (Cisco)pup 12  PUP     # PARC universal packet protocoludp 17  UDP     # user datagram protocolhmp 20  HMP     # host monitoring protocolxns-idp 22  XNS-IDP     # Xerox NS IDPrdp 27  RDP     # "reliable datagram" protocoliso-tp4 29  ISO-TP4     # ISO Transport Protocol class 4 [RFC905]dccp    33  DCCP        # Datagram Congestion Control Prot. [RFC4340]xtp 36  XTP     # Xpress Transfer Protocolddp 37  DDP     # Datagram Delivery Protocolidpr-cmtp 38    IDPR-CMTP   # IDPR Control Message Transportipv6    41  IPv6        # Internet Protocol, version 6ipv6-route 43   IPv6-Route  # Routing Header for IPv6ipv6-frag 44    IPv6-Frag   # Fragment Header for IPv6idrp    45  IDRP        # Inter-Domain Routing Protocolrsvp    46  RSVP        # Reservation Protocolgre 47  GRE     # General Routing Encapsulationesp 50  IPSEC-ESP   # Encap Security Payload [RFC2406]ah  51  IPSEC-AH    # Authentication Header [RFC2402]skip    57  SKIP        # SKIPipv6-icmp 58    IPv6-ICMP   # ICMP for IPv6ipv6-nonxt 59   IPv6-NoNxt  # No Next Header for IPv6ipv6-opts 60    IPv6-Opts   # Destination Options for IPv6rspf    73  RSPF CPHB   # Radio Shortest Path First (officially CPHB)vmtp    81  VMTP        # Versatile Message Transporteigrp   88  EIGRP       # Enhanced Interior Routing Protocol (Cisco)ospf    89  OSPFIGP     # Open Shortest Path First IGPax.25   93  AX.25       # AX.25 framesipip    94  IPIP        # IP-within-IP Encapsulation Protocoletherip 97  ETHERIP     # Ethernet-within-IP Encapsulation [RFC3378]encap   98  ENCAP       # Yet Another IP encapsulation [RFC1241]#   99          # any private encryption schemepim 103 PIM     # Protocol Independent Multicastipcomp  108 IPCOMP      # IP Payload Compression Protocolvrrp    112 VRRP        # Virtual Router Redundancy Protocol [RFC5798]l2tp    115 L2TP        # Layer Two Tunneling Protocol [RFC2661]isis    124 ISIS        # IS-IS over IPv4sctp    132 SCTP        # Stream Control Transmission Protocolfc  133 FC      # Fibre Channelmobility-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 Protocolshim6   140 Shim6       # Shim6 Protocol [RFC5533]wesp    141 WESP        # Wrapped Encapsulating Security Payloadrohc    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 : tcpprotocol number : 6protocol alias: TCP------------------------------protocol name : igmpprotocol number : 2protocol 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;}/*2inet_pton: 0x90012c0inet_ntop: 192.18.0.9*/

从进程到内核传递套接字地址结构的函数常有3个——bind, connect, sendto
从内核到进程传递套接字地址结构的函数常有四个accept, recvfrom, getsockname, getpeername
bind()对socket定位

2 0