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

1. 什么是socket

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

套接口是全双工的通信,它不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程间通信。

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

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];

};

说明:

  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];

};

说明:

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

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

3. 网络字节序

字节序可以分为大端字节序与小端字节序:

  • 大端字节序(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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏好好学java的技术栈

java基础提升篇:深入浅出Java多线程

1272
来自专栏Linyb极客之路

Java虚拟机:Java内存区域及对象

为以后写文章考虑,也为巩固自己的知识和一些基本概念,这里要理清楚几个计算机中的概念。

1292
来自专栏开源优测

BeautifulSoup的使用

参考资料地址:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/#id28

1241
来自专栏技术博客

Asp.Net Web API 2第十四课——Content Negotiation(内容协商)

阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.h...

941
来自专栏企鹅号快讯

Java后台编程初学者,这些常识你都知道吗?

Java编程中的一些常识,希望有心学习的可以多看一眼,如果你是高手欢迎指点文中小编的不足,感谢支持。以下这些编程常识均由小编结合个人水平以及搜寻相关资料整理编辑...

20210
来自专栏Java大联盟

Java面试手册:线程专题 ③

1241
来自专栏老马说编程

(67) 线程的基本协作机制 (上) / 计算机程序的思维逻辑

上节介绍了多线程之间竞争访问同一个资源的问题及解决方案synchronized,我们提到,多线程之间除了竞争,还经常需要相互协作,本节就来介绍Java中多线程协...

2126
来自专栏DannyHoo的专栏

iOS开发中OC与H5网页交互之OC传值给JS(WKWebView)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

3592
来自专栏JMCui

MongoDB系列二(介绍).

一、特点     学习一个东西,至少首先得知道它能做什么?适合做什么?有什么优缺点吧?     传统关系型数据库,遵循三大范式。即原子性、唯一性、每列与主键直接...

2988
来自专栏高爽的专栏

Java线程(四):线程中断、线程让步、线程睡眠、线程合并

最近在Review线程专栏,修改了诸多之前描述不够严谨的地方,凡是带有Review标记的文章都是修改过了。本篇文章是插进来的,因为原来没有写,现在...

2670

扫码关注云+社区

领取腾讯云代金券