Socket编程实践(1) 基本概念

1. 什么是socket

socket能够当作是用户进程与内核网络协议栈的编程接口。TCP/IP协议的底层部分已经被内核实现了,而应用层是用户须要实现的,这部分程序工做在用户空间。用户空间的程序须要经过套接字来访问内核网络协议栈。html

套接口是全双工的通讯,它不只能够用于本机的进程间通讯,还能够用于网络上不一样主机的进程间通讯。编程

套接字还能够异构系统间进行通讯,异构系统指的是在硬件或软件上有所差异的系统,例如安卓系统的手机与windows系统的PC机上均可以实现QQ通讯,套接字能够实如今这两个设备上的通讯。windows

2. IPV4套接口地址结构

套接口既然可以链接两个端系统,那它就须要一个地址来标记该端系统,例如两个电话须要电话号码来标记才能够进行拨号。这抽象成套接口的地址结构。IPV4套接口地址结构一般也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在头文件< netinet/in/h >中。网络

struct sockaddr_in{

    uint8_t sin_len;

    sa_family_t sin_family;

    in_port_t sin_port;

    struct in_addr sin_addr;

    char sin_zero[8];

};

说明:socket

  1. sin_len:整个sockaddr_in结构体的长度,在4.3BSD-Reno版本以前的第一个成员是sin_family。
  2. sin_family:指定带地址家族,在这里必须设置为AF_INET。socket在设计时不只能够用于TCP/IP协议,它还能够用于其余协议,例如unix域协议,地址家族用于指定该套接字用于哪一种协议。AF_INET表示用于IPV4协议。
  3. sin_port:端口号,16位的无符号整数,可以表示到65535。2个字节。
  4. sin_addr: IPV4的地址。4个字节的整数。
  5. sin_zero:暂不使用,通常将其设置为0。

其中,struct in_addr仅仅是一个32位的无符号整数,能够在终端下输入man 7 ip进行查看:函数

接下来看一下通用的地址结构。上面说过,socket能够用于不一样的协议上,通用的地址结构能够用于任何协议的socket编程。测试

struct sockaddr{

    uint8_t sin_len;

    sa_family sin_family;

    char sa_data[14];

};

说明:ui

  1. sin_len:整个sockaddr结构大小
  2. sin_family:指定该地址家族
  3. sa_data:由sin_family决定它的形式

能够看到,在通用地址结构中sa_data是14个字节,而在IPV4的地址结构中,sin_port、sin_addr、sin_zero三个变量加起来也等于14个字节。也便是说,这两种结构是兼容的。设计

3. 网络字节序

字节序能够分为大端字节序与小端字节序:unix

  • 大端字节序(Big Endian) :最高有效位存储于最低内存地址处,最低有效位存储于最高地址内存处。
  • 小端字节序(Little Endian):恰好与大端字节序倒过来,最高有效位存于最高内存地址处,最低有效位存储于最低内存地址处。

这样提及来挺抽象,经过一幅图来讲明:

上面说过,socket能够用于异构系统之间的通讯。而不一样的系统采用的字节序多是不一样的,有的系统采用大端字节序,例如Motorola 6800;有的采用小端字节序,如X86。所以,在进行字节传输时,应该同一一个字节序,称为网络字节序。网络字节序采用大端字节序。若是主机A为小端字节序的系统,那么在传输时须要先将小端字节序转换成网络字节序。这须要一些字节序的转换函数。

咱们能够编写程序来测试本身的主机是什么字节序:

#include<stdio.h>

int main(void)

{

        unsigned int x = 0x12345678;

        unsigned char *p = (unsigned char*)&x;

        printf("%0x,%0x,%0x,%0x\n",p[0],p[1],p[2],p[3]);

        return 0;

}

在个人电脑上输出结果为:78,56,34,12. 所以个人主机为小端字节序。

4. 字节序转换函数

若是主机的字节序与网络字节序不一样,那么须要进行字节序的转换。下面是一些字节序转换函数:

# 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);

说明:h表明host;n表明network;s表明short;l表明long

描述:

  • htonl()函数将无符号整数hostlong从主机字节序转换成网络字节序。
  • htons()函数将无符号短整型hostshort从主机字节序转换成网络字节序。
  • ntohl()函数功能与 htonl()函数相反
  • ntohs()函数功能与htons()函数相反

咱们能够进行验证,刚才已经经过程序测试出个人主机是小端字节序,接下来使用函数 htonl()将整数0x12345678转换成网络字节序。

#include<stdio.h>

#include <arpa/inet.h>

int main(void)

{

        unsigned int x = 0x12345678;

        unsigned char *p = (unsigned char*)&x;

        printf("转换前:%0x,%0x,%0x,%0x\n",p[0],p[1],p[2],p[3]);

        unsigned int y = htonl(x);

        p = (unsigned char *) &y;

        printf("转换后:%0x,%0x,%0x,%0x\n",p[0],p[1],p[2],p[3]);

        return 0;

}

结果输出:

转换前:78,56,34,12

转换后:12,34,56,78

5. 地址转换函数

对于IP地址,咱们一般采用点分十进制的形式进行直观的认识,而程序更多的时候是处理32位的地址,所以须要有函数在点分十进制与32位地址这两种形式间进行转换。

# include < sys/socket.h>

   # include < netinet/in.h>

   # include < arpa/inet.h>



   int inet_aton(const char *cp, struct in_addr *inp);

   in_addr_t inet_addr(const char *cp);

   char *inet_ntoa(struct in_addr in);

描述:

  • inet_addr()函数:表示将点分十进制的IP地址转换成32位的ip地址(整数)。
  • inet_ntoa()函数:将32位ip地址(网络字节序)转换成点分十进制的ip之地。

例程:

#include<stdio.h>

#include<arpa/inet.h>

int main()

{

        unsigned long addr = inet_addr("192.168.0.100");//将点分十进制转换为32bit地址

        printf("addr = %u\n",htonl(addr)); 



        struct in_addr ipaddr;

        ipaddr.s_addr = addr;

        printf("ipaddr = %s\n",inet_ntoa(ipaddr)); //网络字节序地址转换为点分十>进制

        return 0;

}

输出:

addr = 3232235620

ipaddr = 192.168.0.100

6. 套接字类型

套接字类型主要有三种:

  1. 流方套接字(SOCK_STREAM):它对应TCP协议,它提供面向链接的、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收。
  2. 数据报套接字(SOCK_DGREAM):提供无链接服务。不提供无错保证,数据可能丢失或重复,而且接收顺序混乱。
  3. 原始套接字(SOCK_RAW):它提供一种能力,让咱们直接跨越传输层,直接对IP层进行数据封装,经过该套接字,咱们能够直接将数据封装成IP层可以认识的协议格式。

文章链接:http://www.cnblogs.com/QG-whz/p/5426634.html

相关文章
相关标签/搜索