正在加载……
专注、离线、切勿分心

1.1. 套接口概念

         套接口,也叫“套接字”。是操作系统内核中的一个数据结构,它是网络中的节点进行相互通信的门户。它是网络进程的ID两个进程通信时,首先要确定各自所在的网络节点的网络地址。但是,网络地址只能确定进程所在的计算机,而一台计算机上很可能同时运行着多个进程,所以仅凭网络地址还不能确定到底是和网络中的哪一个进程进行通信,因此套接口中还需要包括其他的信息,也就是端口号(PORT)。在一台计算机中,一个端口号一次只能分配给一个进程,也就是说,在一台计算机中,端口号和进程之间是一一对应关系所以,使用端口号和网络地址的组合可以唯一的确定整个网络中的一个网络进程。
      (如网络中某一台计算机的IP10.92.20.160,操作系统分配给计算机中某一应用程序进程的端口号为1500,则此时10.92.20.160  1500就构成了一个套接口。)
端口号的概念:
     在网络技术中,端口大致有两种意思:一是物理意义上的端口,如集线器、交换机、路由器等用于连接其他网络设备的接口。二是指TCP/IP协议中的端口,端口号的范围从0~65535,一类是由互联网指派名字和号码公司ICANN负责分配给一些常用的应用程序固定使用的“周知的端口”,其值一般为0~1023.例如http的端口号是80,ftp为21,ssh为22,telnet为23等。还有一类是用户自己定义的,通常是大于1024的整型值。
ip地址的表示:
    通常用户在表达IP地址时采用的是点分十进制表示的数值(或者是为冒号分开的十进制Ipv6地址),而在通常使用的socket编程中使用的则是二进制值,这就需要将这两个数值进行转换。
ipv4地址:32bit, 4字节,通常采用点分十进制记法。
例如对于:10000000 00001011 00000011 00011111
点分十进制表示为:128.11.3.31
ip地址的分类:
           

1.2. socket概念

   Linux中的网络编程是通过socket接口来进行的。socket是一种特殊的I/O接口,它也一种文件描述符。它是一种常用的进程之间通信机制,通过它不仅能实现本地机器上的进程之间的通信,而且通过网络能够在不同机器上的进程之间进行通信。

   每一个socket都用一个半相关描述{协议、本地地址、本地端口}来表示一个完整的套接字则用一个相关描述{协议、本地地址、本地端口、远程地址、远程端口}来表示socket也有一个类似于打开文件的函数调用,该函数返回一个整型的socket描述符,随后的连接建立、数据传输等操作都是通过socket来实现的



1.3. socket类型

1流式socketSOCK_STREAM      用于TCP通信

       流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性

2数据报socketSOCK_DGRAM     用于UDP通信

       数据报套接字定义了一种无连接的服务数据通过相互独立的报文进行传输是无序的并且不保证是可靠、无差错的。使用数据报协议UDP

3原始socketSOCK_RAW         用于新的网络协议实现的测试等

       原始套接字允许对底层协议如IPICMP进行直接访问,它功能强大但使用较为不便,主要用于一些协议的开发



1.4. 数据存储优先顺序的转换

计算机数据存储有两种字节优先顺序:高位字节优先(称为大端模式)和 低位字节优先(称为小端模式)

eg:对于内存中存放的数0x12345678来说

如果是采用大端模式存放的,则其真实的数是:0x12345678

如果是采用小端模式存放的,则其真实的数是:0x78563412

如果称某个系统所采用的字节序为主机字节序,则它可能是小端模式的,也可能是大端模式的。而端口号和IP地址都是以网络字节序存储的,不是主机字节序,网络字节序都是大端模式。要把主机字节序和网络字节序相互对应起来,需要对这两个字节存储优先顺序进行相互转化。
//发送数据,数据在内存中小端,发过去对面接收的电脑也是小端就没关系,如果发送方和接收方大小端不一样就要存在转换
X86架构,ARM架构(都是小端)  小端 
POWERPC架构 (IBM的,主要用于服务器) MPIS架构(龙芯,一个俄罗斯人发明的)  大端

端口号和IP地址都是以网络字节序存储的,不是主机字节序,网络字节序都是大端模式

intel 都是小端的

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);   //成功返回字节序,失败返回-1
相互转化的四个函数:htons(),  ntohs(),  htonl()  和 ntohl()
             h代表host , n代表network , s代表short , l代表long。 通常16位的IP端口号用s代表,而IP地址用l来代表。

不用考虑自己的电脑是不是小端还是大端,直接调用接口,接口里面会有判断;

hton.c
#include<arpa/inet.h>
#include<stdio.h>
int main(void)
{
        uint32_t val = 0x12345678;
        uint32_t i ;
        uint32_t j;
        i = htonl(val); //0x78563412 , 主机字节序是小端模式
        j = ntohl(val); //0x78563412
        printf("i = %x , j = %x\n",i,j);
        uint16_t val2 = 0x1234;
        uint16_t k;
        uint16_t z;
        k = htons(val2);  //0x3412
        z = ntohs(val2);  //0x3412
        printf("k = %x , z = %x\n",k,z);
        return 0;
}


 


1.5. socket信息数据结构

socket信息数据结构
struct sockaddr
{
        unsigned short sa_family;   /*地址族*/
        char sa_data[14];           /*14字节的协议地址,包含该socket的IP地址和端口号。*/
};
struct sockaddr_in
{
        short int sa_family;             /*地址族*/
        unsigned short int sin_port;     /*端口号*/
        struct in_addr sin_addr;         /*IP地址*/
        unsigned char sin_zero[8];       /*填充0 以保持与struct sockaddr同样大小*/
};
typedef uint32_t in_addr_t;
struct in_addr
{
        unsigned long int  s_addr;  /* 32位IPv4地址,网络字节序 */
};
头文件<netinet/in.h>
sa_family:AF_INET    ☞IPv4协议  
AF_INET6  ☞IPv6协议





1.6. 地址格式转化

    通常用户在表达地址时采用的是点分十进制表示的数值(或者是为冒号分开的十进制Ipv6地址),而在通常使用的socket编程中使用的则是32位的网络字节序的二进制值,这就需要将这两个数值进行转换。ipv4中用到的函数有inet_aton()inet_addr()inet_ntoa(),而IPV4Ipv6兼容的函数有inet_pton()inet_ntop()

IPv4的函数原型:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int  inet_aton(const char *straddr, struct in_addr *addrptr);     //主机地址到网络地址
char  *inet_ntoa(struct in_addr inaddr);      //网络地址到主机地址
in_addr_t  inet_addr(const char *straddr);    // in_addr_t 就是 unsigned long int ,代表 s_addr 传入主机地址(点分十进制)转换成网络地址
函数inet_aton(): 将点分十进制数的IP地址转换成为网络字节序的32位二进制数值。返回值:成功,则返回1,不成功返回0.
   参数straddr:  存放输入的点分十进制数IP地址字符串。
   参数addrptr:  传出参数,保存网络字节序的32位二进制数值。
函数inet_ntoa(): 将网络字节序的32位二进制数值转换为点分十进制的IP地址。
函数inet_addr(): 功能与inet_aton相同,但是结果传递的方式不同。inet_addr()若成功则返回32位二进制的网络字节序地址。

IPv4IPv6的函数原型:

#include <arpa/inet.h>
int inet_pton(int family, const char *src, void *dst);     //主机地址到网络地址
const char *inet_ntop(int family, const void *src, char *dst, socklen_t len);    //网络地址到主机地址
函数inet_pton()跟inet_aton实现的功能类似,只是多了family参数,该参数指定为AF_INET,表示是IPv4协议,如果是AF_INET6,表示 IPv6协议。
函数inet_ntop()跟inet_ntoa类似其中len表示表示转换之后的长度(字符串的长度)。dst是一个字符串数组,用来存放转换好的主机地址。


inet_aton.c
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
int main(int argc,char **argv)
{
        if(2!=argc)
        {
                printf("error args\n");
                return -1;
        }
        struct in_addr addr;
        int ret = inet_aton(argv[1],&addr);  //主机地址->网络地址
        if(0==ret)
        {
                perror("inet_aton");
                return -1;
        }
        printf("net addr %x\n",addr.s_addr);
        printf("host addr %s\n",inet_ntoa(addr));   //网络地址->主机地址
        printf("net addr %x\n",inet_addr(argv[1]));   //主机地址->网络地址
        return 0;
}


// 87 64 a8 c0  //计算机按小端存储的

// 192.168.100.135 -> c0 a8 64 87 //主机字节序小端存储就是 87 64 a8 c0 ,转成网络字节序就是 c0 a8 64 87 , 输出就是87 64 a8 c0






1.7. 名字地址转化

     主机名与域名的区别:主机名通常在局域网里面使用,通过/etc/hosts文件,主机名可以解析到对应的ip;域名通常是再internet上使用。

    众所周知,百度的域名为:www.baidu.com ,而这个域名其实对应了一个百度公司的IP地址,那么百度公司的IP地址是多少呢?我们可以利用ping www.baidu.com来得到百度公司的ip地址

    在linux中,有一些函数可以实现主机名和地址的转化,最常见的有gethostbyname()gethostbyaddr()等,它们都可以实现IPv4IPv6的地址和主机名之间的转化。其中gethostbyname()是将主机名转化为IP地址gethostbyaddr()则是逆操作,是将IP地址转化为主机名

函数原型:

#include <netdb.h>
struct hostent *gethostbyname(const char* hostname);
struct hostent *gethostbyaddr(const char* addr,  size_t len,  int family);    //失败返回NULL
struct hostent
           {
                   char *h_name;            /*正式主机名*/
                   char **h_aliases;        /*主机别名*/
                   int h_addrtype;          /*主机IP地址类型 IPv4为AF_INET*/
                   int h_length;            /*主机IP地址字节长度,对于IPv4是4字节,即32位*/
                   char **h_addr_list;      /*主机的IP地址列表*/   都是网络地址
           }
函数gethostbyname():用于将域名(www.baidu.com)或主机名转换为IP地址。参数hostname指向存放域名或主机名的字符串。
函数gethostbyaddr():用于将IP地址转换为域名或主机名。参数addr是一个IP地址,此时这个ip地址不是普通的字符串,而是要通过函数inet_aton()转换。len为IP地址的长度,AF_INET为4。family可用AF_INET:Ipv4 或AF_INET6:Ipv6。必须在/etc/hosts中有配置。

gethostbyname.c

#include<netdb.h>
#include<stdio.h>
#include<arpa/inet.h>
#include<string.h>
#include<strings.h>
int main(int argc,char **argv)
{
        if(2!=argc)
        {
                printf("error argcs\n");
                return -1;
        }
        struct hostent *p = NULL;
        p = gethostbyname(argv[1]);
        printf("正式主机名: %s\n",p->h_name);
        char *pstr = NULL;
        for(pstr=*(p->h_aliases);pstr!=NULL;pstr=*(++p->h_aliases))
        {
                printf("主机别名: %s\n",pstr);
        }
        char dst[64];
        char *pstr1 = *(p->h_addr_list);
        while(NULL!=pstr1)
        {
                bzero(dst,sizeof(dst));
                inet_ntop(p->h_addrtype,pstr1,dst,sizeof(dst));
                printf("IP地址 %s\n",dst);
                pstr1 = *(++p->h_addr_list);
        }
        bzero(dst,sizeof(dst));
        p = gethostbyname(argv[1]);   //不知道为什么,这里必须从新指向开头,不然下面段错误,显示p->h_addr_list = nil;
//原因是上面++p->h_addr_list已经加到最后NULL的位置了;++p->h_addr_list就相当于修改了这个值。
//结构体里获得的只是一个char *的数组首地址。 
        inet_ntop(p->h_addrtype,*(p->h_addr_list),dst,sizeof(dst));
        printf("%s\n",dst);
        return 0;
}









                                                                                           




posted on 2018-03-12 09:04  正在加载……  阅读(402)  评论(0编辑  收藏  举报