Linux网络编程系列一:全球IP因特网

之前一直都没有做过网络编程的相关学习,昨天突然翻了翻《深入浅出计算机系统》,发现是个入门的好方法,因此在此做一下学习笔记,同时学习了linux相关命令,何乐而不为。

客户端-服务器编程模型

所有的网络应用都是基于相同的基本编程模型,有着相似的整体逻辑结构,并且依赖相同的编程接口。

一个应用由一个服务器进程或者多个客户端进程组成,要记住这里的客户端和服务器指的都是进程,而不是常常提到的机器或者主机,这是非常重要的。

全球IP因特网

TCP/IP协议(Transmission Control Protocol/Internet Protocol,传输控制协议/互联网络协议)

  • IP协议提供基本的命名方法和递送机制
  • UDP (Unreliable Datagram Protocol,不可靠数据报协议)稍微扩展了IP协议,使得包可以在进程间而不是在主机间传送。
  • TCP是构建在IP之上的复杂协议,提供进程间可靠的全双公(双向的)连接

IP地址

IP地址结构:

struct in_addr{
   unsigned  int s_addr;
};

网络-主机地址翻译

 不同的主机可以有不同的主机字节顺序,因此TCP/IP为任意整数项定义了同意的网络字节顺序(大端字节顺序),因此便产生了以下函数

#include <netinet/ in.h>
// 将32位整数由主机字节顺序转换为网络字节顺序
unsigned  long  int htonl(unsigned  long  int hostlong);
unsigned  short  int htons(unsigned  short  int hostshort);

// 将32位整数从网络字节顺序转换为主机字节
unsigned  long  int ntohl(unsigned  long  int netlong);
unsigned  short  int ntohs(unsigned  short  int netshort);

IP地址和点分十进制转换

学到这里,我感觉网络编程,其实就是在记忆各种系统提供的函数调用,学的全是函数呵呵。

#include<arpa/inet.h>
// 将点分十进制串(cp)转换为网络字节顺序的IP地址(inp),传递的是指向IP结构的指针
int inet_aton( const  char*cp,  struct in_addr* inp);

// 将网络字节顺序的IP地址转换为对应的点分十进制串
char* inet_ntoa( struct in_addr  in);

// 其中,n表示network,a表示application,to表示转换

IP地址结构/网络主机地址翻译/IP与点分十进制转换的2个例子

例子1:

#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/ in.h>

// 将十六进制的字符串转换成整数  
int htoi( char s[])  
{  
     int i;  
     int n =  0;  
     if (s[ 0] ==  ' 0 ' && (s[ 1]== ' x ' || s[ 1]== ' X '))  
    {  
            i =  2;  
    } else {  
        i =  0;  
    }  
     for (; (s[i] >=  ' 0 ' && s[i] <=  ' 9 ') || (s[i] >=  ' a ' && s[i] <=  ' z ') || (s[i] >= ' A ' && s[i] <=  ' Z ');++i)  
    {  
         if (tolower(s[i]) >  ' 9 ')  
        {  
            n =  16 * n + ( 10 + tolower(s[i]) -  ' a ');  
        }   else      {  
            n =  16 * n + (tolower(s[i]) -  ' 0 ');  
        }  
    }  
     return n;  
}  

int main( int argc,  char** argv){
     if(argc< 2return  0;
     struct in_addr  in;
     // 第一步,将十六进制字符串转换成十进制
    
// 第二步,将网络字节顺序,转换成主机字节顺序
    
// 第三步,利用inet_ntoa函数将网络字节顺序地址,转换成点分十进制串
     in.s_addr = ntohl(htoi(argv[ 1]));
    printf( " %s \n ", inet_ntoa( in));
     return  0;
}

运行结果:

 

crazyant@crazyant-virtual-machine:~/CPPWorkBench$ ./hex2dd 0x8002c2f2
128.2.194.242

 

例子2:

/*
* 将点分十进制串转换成十六进制串并打印出结果 
*/

#include <arpa/inet.h>
#include <netinet/ in.h>
#include <stdio.h>

int main( int argc,  char** argv){
     struct in_addr inp;
     if(argc< 2return  0;
     // int inet_aton(const char*cp, struct in_addr *inp);
    
// 将一个点分十进制串(cp)转换为一个网络字节顺序的IP地址(inp)
    inet_aton(argv[ 1],&inp);
    
     // unsigned long int htonl(unsigned long int hostlong);
    
// 将32位整数由网络字节顺序转换为主机自己顺序,这样输出才不会相反
    printf( " %x \n ", ntohl(inp.s_addr));
     return  0;
}

运行结果:

 

crazyant@crazyant-virtual-machine:~/CPPWorkBench$ ./dd2hex 128.2.194.242
8002c2f2

因特网域名

域名组成了层次结构,从上到下依次是.com,自定义,www 从下到上,依次连接变成了最终的域名

DNS数据库由大量的如下的主机条目结构,其中每条定义了一组域名(一个官方名字和一组别名)和一组IP地址之间的映射,其实每个主机条目就是一个域名和IP地址的等价类

/* DNS主机条目结构 */
struct hostent{
     char *h_name;  // 官方的主机名字
     char **h_aliases;  // 主机名字数组
     int h_addrtype;  // 主机地址类型
     int h_length;   // 主机地址长度,按位计算
     char **h_addr_list;  // 网络地址结构体数组
};

因特网应用程序,可以通过两个函数对DNS进行检索:

#include<netdb.h>
// 返回和域名name相关的主机条目
struct hostent *gethostbyname( const  char* name);

// 返回和IP地址addr相关联的主机条目 ,第二个参数给出了IP地址的字节长度,当前都为4,第三个参数一直是0
struct hostent *gethostbyaddr( const  char* addr,  int len,  0);

DNS结构体/获取域名的DNS信息例子

#include  " csapp.h "
#include <netdb.h>
#include <stdio.h>

int main( int argc,  char** argv){
     char **pp;
     struct in_addr addr; // ip地址结构体
     struct hostent *hostp; // dns上的主机条目的结构体
    
     if(argc!= 2){
         // 参数必须为域名或者IP地址
        fprintf(stderr,  " usage: %s <domain name or dotted-decimal>\n ", argv[ 0]    );
        exit( 0);
    }
    
     if(inet_aton(argv[ 1],&addr)!= 0){
         // 说明参数是一个IP地址
        hostp = gethostbyaddr(( const  char*)&addr,  sizeof(addr), AF_INET);
    }  else {
        hostp = gethostbyname(argv[ 1]); // 说明是域名 
    }
    printf( " official hostname:%s \n ", hostp->h_name);
     for( pp=hostp->h_aliases; *pp!=NULL; pp++){
        printf( " alias: %s\n ",*pp);
    }
     for(pp=hostp->h_addr_list; *pp!=NULL; pp++){
        addr.s_addr=(( struct in_addr*)*pp)->s_addr;
        printf( " address: %s\n ", inet_ntoa(addr));    
    }
    exit( 0);
}

运行结果:

 

crazyant@crazyant-virtual-machine:~/CPPWorkBench$ ./hostinfo google.com
official hostname:google.com
address: 74.125.128.100
address: 74.125.128.138
address: 74.125.128.113
address: 74.125.128.102
address: 74.125.128.139
address: 74.125.128.101
crazyant@crazyant-virtual-machine:~/CPPWorkBench$ ./hostinfo google.com
official hostname:google.com
address: 74.125.128.101
address: 74.125.128.139
address: 74.125.128.102
address: 74.125.128.113
address: 74.125.128.138
address: 74.125.128.100
crazyant@crazyant-virtual-machine:~/CPPWorkBench$ ./hostinfo google.com
official hostname:google.com
address: 74.125.128.138
address: 74.125.128.100
address: 74.125.128.101
address: 74.125.128.139
address: 74.125.128.102
address: 74.125.128.113
运行了三次,大家仔细观察会发现,google.com的地址列表每次以一种不同的/轮转(round robin)的顺序返回,这可以用来对一个大量使用的域名的请求做 负载平衡

 

因特网连接

因特网上的数据是双向/全双工进行发送和接受的,每个主机可以用一个套接字来标识

套接字 = IP地址:端口号

客户端的套接字和服务器段的套接字共同组成了一个套接字对,它标识了唯一的一个连接

一个连接 = (客户端IP:客户端端口, 服务器IP:服务器端口)

 

小结

本次主要是整理了客户端服务器模型/IP地址结构/网络主机地址翻译/IP和16进制转换/DNS条目结构体与检索/套接子概念等内容,下次将会学习创建网络应用的套接子接口(又一套函数)。

 

 

你可能感兴趣的:(linux)