网络编程--协议、协议族、地址族

写在前面

这里先介绍下socket函数(Windows版本)的函数声明,后续内容均围绕该声明展开:

#include <winsock2.h>
//af: 指定该套接字的协议族
//type: 指定该套接字的数据传输方式
//protocol: 指定该套接字的最终协议
//返回值:失败返回INVALID_SOCKET,否则为成功
SOCKET socket(int af, int type, int protocol);

协议和协议族

协议:协议就是为了完成数据交换而定好的约定。

协议族: 多个相关协议的集合 。

红烧牛肉面和藤椒牛肉面都属于牛肉面的一种,与之类似,套接字通信中的协议也具有以下几类:

名称协议族
PF_INETIPv4互联网协议族
PF_INET6IPv6互联网协议族
PF_LOCAL本地通信的UNIX协议族
PF_PACKET底层套接字协议族
PF_IPXIPX Novell协议族

套接字中实际采用的最终协议信息是通过socket函数的第三个参数传递的。在第一个参数指定的协议族范围内通过第三个参数决定最终协议。

套接字类型

套接字类型指的是套接字的数据传输方式,通过socket函数的第二个参数传递,只有这样才能决定创建的套接字的数据传输方式。

已通过第一个参数传递了协议族信息,为什么还要决定数据传输方式?

问题就在于,决定了协议族并不能同时决定数据传输方式。换言之,socket函数第一个参数PF_INET协议族中也存在多种数据传输方式。

这里最常见的就是面向连接的TCP(SOCK_STREAM)和面向消息的UDP(SOCK_DGRAM)。

面向连接的套接字特性

**面向连接的套接字的特性如下:**可靠的、按序传递的、基于字节的面向连接的数据传输方式的套接字。

收发数据的套接字内部有缓冲(buffer),简言之就是字节数组。通过套接字传输的数据将保存到该数组。因此,收到数据并不意味着马上调用recv函数。只有不超过数组容量,则有可能在数据填充满缓冲后通过1次recv函数调用读取缓冲中的全部内容。当然也可以分多次recv调用读取。

也就是说,,在面向连接的套接字中,recv函数和send函数的调用次数并无太大意义。所以说面向连接的套接字并不存在数据边界。

缓冲区满了会发生什么?

首先调用recv函数从缓存区读取部分(或全部)数据,因此,缓冲并不总是满的。

但如果recv函数读取速度比接收数据慢,缓冲就有可能满。此时套接字将无法再接收数据,但即使这样也不会发生数据丢失,因为传输端套接字将停止传输。

也就是说,面向连接的套接字会根据接收端的状态传输数据,如果传输出错还会提供重传服务。因此,面向连接的套接字除特殊情况外不会发生数据丢失。

面向消息的套接字特性

面向消息的套接字特性如下:

①强调快速传输而非传输顺序

②传输的数据可能丢失也可能销毁

③传输的数据有数据边界

④限制每次传输的数据大小

即面向消息的套接字比面向连接的套接字具有更快的传输速度,但无法避免数据丢失或损毁。另外,每次传输的数据大小具有一定限制,并存在数据边界。存在数据边界意味着接收数据的次数应和传输次数相同。

面向消息的套接字特性总结如下:不可靠的、不按序传递的、以数据的高速传输为目的的套接字。

协议的最终选择

socket函数的第三个参数决定最终采用的协议。

前面已经通过socket函数的前两个参数传递了协议族信息和数据传输方式,这些信息还不足以决定采用的协议吗?为什么还需要传递第三个参数?

正如各位所想,传递前两个参数即可创建所需套接字。所以大部分情况下可以向第三个参数传递0,除非遇到以下这种情况:

同一协议族中存在多个数据传输方式相同的协议。

协议族相同、传输方式也相同,但协议不同。此时就需要通过第三个参数具体指定协议信息。

这里以PF_INET为例,PF_INET指IPv4网络协议族,SOCK_STREAM是面向连接的数据传输。满足这两个条件的只有IPPROTO_TCP,因此可以省略第三个参数创建面向连接的套接字:

int tcp_socket = socket(PF_INET, SOCK_STREAM, 0);
//或
int tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)

SOCK_DGRAM指的是面向消息的数据传输方式,满足上述条件的协议只有IPPROTO_UDP。因此可以通过以下方式创建面向消息的套接字:

int udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
//或
int udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

地址族和数据序列

IP地址

IP(Internet Protocol),即网络协议,是为了收发网络数据而分配给计算机的值。端口号并非赋予计算机的值,而是为了区分程序中创建的套接字而分配给套接字的序号。即IP地址是分配给计算机的值,端口号则是分配给计算机中各应用程序的套接字的值。

为使计算机连接到网络必须向其分配IP地址,IP地址分为两类:

IPv4(Internet Protocol version 4) 4字节地址族

IPv6(Internet Protocol version 6) 6字节地址族

二者主要的差别是在IP所用的字节数,目前通用的地址族是IPv4,IPv6是为了应对2010年前后IP地址耗尽的问题而提出的标准。

IPv4标准的4字节IP地址分为网络地址主机地址,其中网络地址又分为A、B、C、D、E等类型。如图:

因此只需通过IP地址的第一个字节即可判断网络地址占用的字节数:

A类地址的首字节范围: 0 ~ 127

B类地址的首字节范围: 128 ~ 191

B类地址的首字节范围: 192 ~ 223

还可以这样表示:

A类地址第一个字节的首位以0开始, 即00000000

B类地址第一个字节的前2位以10开始, 即1000 0000, 128(十进制)

C类地址第一个字节的前3位以110开始, 1100 0000, 192(十进制)

网络地址 和 主机地址

网络地址(网络ID)是为区分网络而设置的一部分IP地址。假设向某一地址www.baidu.com传输数据,该公司内部构建了局域网,把所有计算机连接起来。因此首先应该向www.baidu.com网络传输数据,也就是说,并非一开始就浏览所有4字节IP地址,进而找到目标主机。而是仅浏览4字节IP地址的网络地址,先把数据传到www.baidu.com的网络。然后www.baidu.com网络(构成网络的路由器)接收到数据后,流程传输数据的主机地址(主机ID)并将数据传给目标计算机。

端口号

IP地址用于区分计算机,只要有IP地址就能向目标主机传输数据。但仅凭IP地址无法传输给最终的应用程序,因此需要端口号来对应套接字

端口号就是在同一操作系统内为区分不同的套接字而设置的,因此无法将一个端口分配给不同的套接字。

端口号由16位构成,因此可分配的端口号范围是0 ~ 65535,其中0 ~ 1023这1024个端口是知名端口,一般分配给特定的应用程序,所以应该分配此范围之外的值。

虽然端口不可重复,但TCP套接字和UDP套接字不会共用端口,例:如果某TCP套接字使用9190端口,则其他TCP套接字就无法使用该端口,但UDP套接字可以使用。

总之,数据传输目标地址应同时包含IP地址和端口号,只有这样,数据才会被传输到最终的目的应用程序(应用程序套接字)。

地址信息的表示

应用程序中使用的IP地址和端口号以结构体的形式给出了定义,如下:

struct SOCKADDR_IN
{
    sa_family_t		sin_family;		//地址族(Address Family), unsigned short.
    uint16_t		sin_port;		//16位TCP/UDP端口号
    struct in_addr	 sin_addr;		//32位IP地址
    char			sin_zero[8];	//不使用
};

struct in_addr
{
    in_addr_t	s_addr;	//32位IPv4地址,typedef unsigned int in_addr_t
};

成员sin_family:每种协议族适用的地址族均不同,例IPv4使用4字节地址族,IPv6使用6字节地址族。

成员sin_port:该成员保存16位端口号,它以网络字节顺序保存。

成员sin_addr:该成员保存32位IP地址信息,也以网络字节顺序保存。

成员sin_zero:无特殊含义,只是为使结构体SOCKADDR_IN的大小与SOCKADDR结构体(bind,connect,accept中的第二个参数类型, 记得显示的类型转换)大小保持一致而插入的成员。

SOCKADDR结构体定义如下:

struct SOCKADDR
{
	sa_family_t		sin_family;		//地址族
	char		    sa_data[14];	//地址信息
};

此结构体成员sa_data保存的地址信息中需包含IP地址和端口号,剩余部分应填充0,而这对于包含地址信息来讲非常麻烦,继而就有了新的更方便结构体SOCKADDR_IN。因此只需填充SOCKADDR_IN结构体,然后转换成SOCKADDR类型传递给相应函数即可。

网络字节顺序与主机字节顺序

不同CPU中,4字节整数型值1在内存空间中的保存方式是不同的,如下:

第一种保存方式: 00000000 00000000 00000000 00000001

第一种保存方式: 00000001 00000000 00000000 00000000

保存顺序的不同意味着对接收数据的解析顺序也不同,因此CPU的数据解析方式也分为2种:

大端序(Big Endian):高位字节存放到低位地址

小端序(Little Endian):高位字节存放到高位地址

例:在0x20号开始的地址中保存4字节int类型数0x12345678,两种保存方式如图:

如果两台保存方式不同的计算机进行套接字通信的时候,数据解析方式就会不一致,从而导致相关错误。因此,在通过网络传输数据时约定了一种统一方式,这种约定称为网络字节顺序(Network Byte Order),非常简单的统一为大端序

因此,主机在想网络传输数据时应将以主机字节顺序的数据(即使该主机是以大端序保存的)转换成网络字节数据在进行网络传输,

这就是为什么要在填充SOCKADDR_IN结构体前将数据转换成网络字节顺序的原因了。

字节顺序的转换

常用的字节顺序转换的API函数:

unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned long ntohl(unsigned long);

htons中的h代表主机(host)字节顺序。

htons中的n代表网络(network)字节顺序。

另外s表示short,l表示long,因此htons是 h、to、n、s的组合,可以解释为“把short类型主机字节顺序转换成网络字节顺序”,同理ntohs表示“把short型的网络字节顺序转换成主机字节顺序”。

网络地址的初始化

将字符串信息转换为网络字节顺序的整数型

SOCKADDR_IN中保存地址信息的成员是32位整数型的。意味着需将常见的IP地址(201.211.124.36)转换成4字节整数型数据,这要如何转。

好在有相应的函数帮助我们将字符串形式的IP地址转换成32位整数型数据,这些转换函数在转换类型的的同时还会自动进行网络字节顺序的转换。

in_addr_t inet_addr(const char* string);
/成功时返回32位大端序整型数据,失败时返回INADDR_NONE
int inet_aton(const char* string, struct in_addr* addr);
//string: 含有序转换的IP地址信息的字符串地址
//addr: 将保存转换结果的in_addr结构体变量的地址值
//返回值:成功时返回1(true),失败时返回0(false)

与之对应的将32位整数型转换成字符串形式的函数:

char* inet_ntoa(struct in_addr adr);
//成功时返回转换的字符串地址, 失败时返回-1

一般的网络地址初始化如下:

SOCKET srvSock;
struct SOCKADDR_IN addr;
memset(&addr, 0, sizeof(addr));	//初始化该结构体
char* srvIP = "211.217.168.13"; //服务器IP
char* srvPort = "9190";			//服务器端口
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(srvIP);
addr.sin_port = htons(atoi(srvPort));
bind(srvSock, (SOCKADDR*)&SOCKADDR_IN, sizeof(SOCKADDR));

INADDR_ANY

每次创建服务器都要输入IP地址会有些繁琐,因此可以使用INADDR_ANY常量分配服务器端的IP地址,即可自动获取运行服务器端的计算机的IP地址。

总结

通过socket函数声明展开了解协议族、数据传输方式以及最终协议的相关知识,此外还学习了IP的分类规则,知道IP和端口分别标识计算机和套接字,以及初始化时的地址的初始化相关的API说明。

为了统一数据传输时的解析,这里引出了主机字节顺序和网络字节顺序,知道数据统一使用网络字节顺序传输,并介绍了主机字节顺序和网络字节顺序相互转换的API接口。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目 录 第1篇 Linux网络开发基础 第1章 Linux操作系统概述 2 1.1 Linux发展历史 2 1.1.1 Linux的诞生和发展 2 1.1.2 Linux名称的由来 3 1.2 Linux的发展要素 3 1.2.1 UNIX操作系统 4 1.2.2 Minix操作系统 4 1.2.3 POSIX 标准 4 1.3 Linux与UNIX的异同 5 1.4 操作系统类型选择和内核版本的选择 5 1.4.1 常见的不同公司发行的Linux异同 6 1.4.2 内核版本的选择 6 1.5 Linux的系统架构 7 1.5.1 Linux内核的主要模块 7 1.5.2 Linux的文件结构 9 1.6 GNU通用公共许可证 10 1.6.1 GPL许可证的历史 10 1.6.2 GPL的自由理念 10 1.6.3 GPL的基本条款 11 1.6.4 关于GPL许可证的争议 12 1.7 Linux软件开发的可借鉴之处 12 1.8 小结 13 第2章 Linux编程环境 14 2.1 Linux环境下的编辑器 14 2.1.1 vim使用简介 14 2.1.2 使用vim建立文件 15 2.1.3 使用vim编辑文本 16 2.1.4 vim的格式设置 18 2.1.5 vim配置文件.vimrc 19 2.1.6 使用其他编辑器 19 2.2 Linux下的GCC编译器工具集 19 2.2.1 GCC简介 19 2.2.2 编译程序的基本知识 21 2.2.3 单个文件编译成执行文件 22 2.2.4 编译生成目标文件 22 2.2.5 多文件编译 23 2.2.6 预处理 24 2.2.7 编译成汇编语言 24 2.2.8 生成和使用静态链接库 25 2.2.9 生成动态链接库 26 2.2.10 动态加载库 29 2.2.11 GCC常用选项 31 2.2.12 编译环境的搭建 33 2.3 Makefile文件简介 34 2.3.1 一个多文件的工程例子 34 2.3.2 多文件工程的编译 36 2.3.3 Makefile的规则 37 2.3.4 Makefile中使用变量 39 2.3.5 搜索路径 43 2.3.6 自动推导规则 44 2.3.7 递归make 44 2.3.8 Makefile中的函数 46 2.4 用GDB调试程序 47 2.4.1 编译可调试程序 48 2.4.2 使用GDB调试程序 49 2.4.3 GDB常用命令 52 2.4.4 其他的GDB 59 2.5 小结 60 第3章 文件系统简介 61 3.1 Linux下的文件系统 61 3.1.1 Linux下文件的内涵 61 3.1.2 文件系统的创建 62 3.1.3 挂接文件系统 64 3.1.4 索引节点inode 65 3.1.5 普通文件 66 3.1.6 设备文件 66 3.1.7 虚拟文件系统VFS 68 3.2 文件的通用操作方法 72 3.2.1 文件描述符 72 3.2.2 打开创建文件open()、create()函数 72 3.2.3 关闭文件close()函数 76 3.2.4 读取文件read()函数 77 3.2.5 写文件write()函数 79 3.2.6 文件偏移lseek()函数 80 3.2.7 获得文件状态fstat()函数 83 3.2.8 文件空间映射mmap()函数 85 3.2.9 文件属性fcntl()函数 88 3.2.10 文件输入输出控制ioctl()函数 92 3.3 socket文件类型 93 3.4 小结 93 第4章 程序、进程和线程 94 4.1 程序、进程和线程的概念 94 4.1.1 程序和进程的差别 94 4.1.2 Linux环境下的进程 95 4.1.3 进程和线程 96 4.2 进程产生的方式 96 4.2.1 进程号 96 4.2.2 进程复制fork() 97 4.2.3 system()方式 98 4.2.4 进程执行exec()函数系列 99 4.2.5 所有用户态进程的产生进程init 100 4.3 进程间通信和同步 101 4.3.1 半双工管道 101 4.3.2 命名管道 107 4.3.3 消息队列 108 4.3.4 消息队列的一个例子 114 4.3.5 信号量 116 4.3.6 共享内存 121 4.3.7 信号 124 4.4 Linux下的线程 127 4.4.1 多线程编程实例 127 4.4.2 Linux下线程创建函数pthread_create() 129 4.4.3 线程的结束函数pthread_join()和pthread_exit() 129 4.4.4 线程的属性 130 4.4.5 线程间的互斥 132 4.4.6 线程中使用信号量 133 4.5 小结 136 第2篇 Linux用户层网络编程 第5章 TCP/IP协议简介 138 5.1 OSI网络分层介绍 138 5.1.1 OSI网络分层结构 138 5.1.2 OSI的7层网络结构 139 5.1.3 OSI参考模型中的数据传输 140 5.2 TCP/IP协议栈 141 5.2.1 TCP/IP协议栈参考模型 141 5.2.2 主机到网络协议 143 5.2.3 IP协议 144 5.2.4 网际控制报文协议(ICMP) 146 5.2.5 传输控制协议(TCP) 150
目 录 第1篇 Linux网络开发基础 第1章 Linux操作系统概述 2 1.1 Linux发展历史 2 1.1.1 Linux的诞生和发展 2 1.1.2 Linux名称的由来 1.2 Linux的发展要素 3 1.2.1 UNIX操作系统 4 1.2.2 Minix操作系统 4 1.2.3 POSIX 标准 4 1.3 Linux与UNIX的异同 5 1.4 操作系统类型选择和内核版本的选择 5 1.4.1 常见的不同公司发行的Linux异同 6 1.4.2 内核版本的选择 6 1.5 Linux的系统架构 7 1.5.1 Linux内核的主要模块 7 1.5.2 Linux的文件结构 9 1.6 GNU通用公共许可证 10 1.6.1 GPL许可证的历史 10 1.6.2 GPL的自由理念 10 1.6.3 GPL的基本条款 11 1.6.4 关于GPL许可证的争议 12 1.7 Linux软件开发的可借鉴之处 12 1.8 小结 13 第2章 Linux编程环境 14 2.1 Linux环境下的编辑器 14 2.1.1 vim使用简介 14 2.1.2 使用vim建立文件 15 2.1.3 使用vim编辑文本 16 2.1.4 vim的格式设置 18 2.1.5 vim配置文件.vimrc 19 2.1.6 使用其他编辑器 19 2.2 Linux下的GCC编译器工具集 19 2.2.1 GCC简介 19 2.2.2 编译程序的基本知识 21 2.2.3 单个文件编译成执行文件 22 2.2.4 编译生成目标文件 22 2.2.5 多文件编译 23 2.2.6 预处理 24 2.2.7 编译成汇编语言 24 2.2.8 生成和使用静态链接库 25 2.2.9 生成动态链接库 26 2.2.10 动态加载库 29 2.2.11 GCC常用选项 31 2.2.12 编译环境的搭建 33 2.3 Makefile文件简介 34 2.3.1 一个多文件的工程例子 34 2.3.2 多文件工程的编译 36 2.3.3 Makefile的规则 37 2.3.4 Makefile中使用变量 39 2.3.5 搜索路径 43 2.3.6 自动推导规则 44 2.3.7 递归make 44 2.3.8 Makefile中的函数 46 2.4 用GDB调试程序 47 2.4.1 编译可调试程序 48 2.4.2 使用GDB调试程序 49 2.4.3 GDB常用命令 52 2.4.4 其他的GDB 59 2.5 小结 60 第3章 文件系统简介 61 3.1 Linux下的文件系统 61 3.1.1 Linux下文件的内涵 61 3.1.2 文件系统的创建 62 3.1.3 挂接文件系统 64 3.1.4 索引节点inode 65 3.1.5 普通文件 66 3.1.6 设备文件 66 3.1.7 虚拟文件系统VFS 68 3.2 文件的通用操作方法 72 3.2.1 文件描述符 72 3.2.2 打开创建文件open()、create()函数 72 3.2.3 关闭文件close()函数 76 3.2.4 读取文件read()函数 77 3.2.5 写文件write()函数 79 3.2.6 文件偏移lseek()函数 80 3.2.7 获得文件状态fstat()函数 83 3.2.8 文件空间映射mmap()函数 85 3.2.9 文件属性fcntl()函数 88 3.2.10 文件输入输出控制ioctl()函数 92 3.3 socket文件类型 93 3.4 小结 93 第4章 程序、进程和线程 94 4.1 程序、进程和线程的概念 94 4.1.1 程序和进程的差别 94 4.1.2 Linux环境下的进程 95 4.1.3 进程和线程 96 4.2 进程产生的方式 96 4.2.1 进程号 96 4.2.2 进程复制fork() 97 4.2.3 system()方式 98 4.2.4 进程执行exec()函数系列 99 4.2.5 所有用户态进程的产生进程init 100 4.3 进程间通信和同步 101 4.3.1 半双工管道 101 4.3.2 命名管道 107 4.3.3 消息队列 108 4.3.4 消息队列的一个例子 114 4.3.5 信号量 116 4.3.6 共享内存 121 4.3.7 信号 124 4.4 Linux下的线程 127 4.4.1 多线程编程实例 127 4.4.2 Linux下线程创建函数pthread_create() 129 4.4.3 线程的结束函数pthread_join()和pthread_exit() 129 4.4.4 线程的属性 130 4.4.5 线程间的互斥 132 4.4.6 线程中使用信号量 133 4.5 小结 136 第2篇 Linux用户层网络编程 第5章 TCP/IP协议简介 138 5.1 OSI网络分层介绍 138 5.1.1 OSI网络分层结构 138 5.1.2 OSI的7层网络结构 139 5.1.3 OSI参考模型中的数据传输 140 5.2 TCP/IP协议栈 141 5.2.1 TCP/IP协议栈参考模型 141 5.2.2 主机到网络协议 143 5.2.3 IP协议 144 5.2.4 网际控制报文协议(ICMP) 146 5.2.5 传输控制协议(TCP) 150 5.2.6 用户数据报文协议(UDP) 154 5.2.7 地址解析协议(ARP) 156 5.3 IP地址分类与TCP/UDP端口 158 5.3.1 因特网中IP地址的分类 159 5.3.2 子网掩码(subnet mask address) 161 5.3.3 IP地址的配置 162 5.3.4 端口 163 5.4 主机字节序和网络字节序 163 5.4.1 字节序的含义 164 5.4.2 网络字节序的转换 164 5.5 小结 166 第6章 应用层网络服务程序简介 167 6.1 HTTP协议和服务 167 6.1.1 HTTP协议概述 167 6.1.2 HTTP协议的基本过程 168 6.2 FTP协议和服务 170 6.2.1 FTP协议概述 170 6.2.2 FTP协议的工作模式 172 6.2.3 FTP协议的传输方式 172 6.2.4 一个简单的FTP过程 173 6.2.5 常用的FTP工具 173 6.3 TELNET协议和服务 174 6.3.1 远程登录的基本概念 174 6.3.2 使用TELNET协议进行远程登录的工作过程 174 6.3.3 TELNET协议 174 6.4 NFS协议和服务 176 6.4.1 安装NFS服务器和客户端 176 6.4.2 服务器端的设定 176 6.4.3 客户端的操作 177 6.4.4 showmount命令 177 6.5 自定义网络服务 177 6.5.1 xinetd/inetd 178 6.5.2 xinetd服务配置 178 6.5.3 自定义网络服务 179 6.6 小结 180 第7章 TCP网络编程基础 181 7.1 套接字编程基础知识 181 7.1.1 套接字地址结构 181 7.1.2 用户层和内核层交互过程 183 7.2 TCP网络编程流程 184 7.2.1 TCP网络编程架构 184 7.2.2 创建网络插口函数socket() 186 7.2.3 绑定一个地址端口对bind() 189 7.2.4 监听本地端口listen 192 7.2.5 接受一个网络请求accept() 194 7.2.6 连接目标网络服务器connect() 199 7.2.7 写入数据函数write() 200 7.2.8 读取数据函数read() 201 7.2.9 关闭套接字函数close() 201 7.3 服务器/客户端的简单例子 202 7.3.1 例子功能描述 202 7.3.2 服务器网络程序 203 7.3.3 服务器读取和显示字符串 205 7.3.4 客户端的网络程序 205 7.3.5 客户端读取和显示字符串 206 7.3.6 编译运行程序 206 7.4 截取信号的例子 207 7.4.1 信号处理 207 7.4.2 信号SIGPIPE 208 7.4.3 信号SIGINT 208 7.5 小结 208 第8章 服务器和客户端信息的获取 210 8.1 字节序 210 8.1.1 大端字节序和小端字节序 210 8.1.2 字节序转换函数 212 8.1.3 一个字节序转换的例子 214 8.2 字符串IP地址和二进制IP地址的转换 217 8.2.1 inet_xxx()函数 217 8.2.2 inet_pton()和inet_ntop()函数 219 8.2.3 使用8.2.1节地址转换函数的例子 220 8.2.4 使用函数inet_pton()和函数inet_ntop()的例子 223 8.3 套接字描述符判定函数issockettype() 223 8.3.1 进行文件描述符判定的函数issockettype() 224 8.3.2 main()函数 224 8.4 IP地址与域名之间的相互转换 225 8.4.1 DNS原理 225 8.4.2 获取主机信息的函数 226 8.4.3 使用主机名获取主机信息的例子 228 8.4.4 函数gethostbyname()不可重入的例子 230 8.5 协议名称处理函数 232 8.5.1 xxxprotoxxx()函数 232 8.5.2 使用协议函数的例子 233 8.6 小结 236 第9章 数据的IO和复用 237 9.1 IO函数 237 9.1.1 使用recv()函数接收数据 237 9.1.2 使用send()函数发送数据 239 9.1.3 使用readv()函数接收数据 240 9.1.4 使用writev()函数发送数据 240 9.1.5 使用recvmsg()函数接收数据 242 9.1.6 使用sendmsg()函数发送数据 244 9.1.7 IO函数的比较 246 9.2 使用IO函数的例子 246 9.2.1 客户端处理框架的例子 246 9.2.2 服务器端程序框架 248 9.2.3 使用recv()和send()函数 249 9.2.4 使用readv()和write()函数 251 9.2.5 使用recvmsg()和sendmsg()函数 253 9.3 IO模型 256 9.3.1 阻塞IO模型 256 9.3.2 非阻塞IO模型 257 9.3.3 IO复用 257 9.3.4 信号驱动IO模型 258 9.3.5 异步IO模型 258 9.4 select()函数和pselect()函数 259 9.4.1 select()函数 259 9.4.2 pselect()函数 261 9.5 poll()函数和ppoll()函数 262 9.5.1 poll()函数 263 9.5.2 ppoll()函数 264 9.6 非阻塞编程 264 9.6.1 非阻塞方式程序设计介绍 264 9.6.2 非阻塞程序设计的例子 264 9.7 小结 266 第10章 基于UDP协议的接收和发送 267 10.1 UDP编程框架 267 10.1.1 UDP编程框图 267

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值