Linux程序设计-网络编程
Linux程序设计-网络编程
网络的基本概念
协议
- 计算机网络中实现通信必须有一些约定,如对速率、传输代码、代码结构、传输控制步骤和出错控制等约定,这些约定即被称为通信协议。
- 在两个节点之间要成功地进行通信,两个节点之间必须约定使用共同的“语言”,这些被通信各方共同遵守的约定、语言、规则被称为协议。
- 在Internet中,最为通用的网络协议是TCP/IP协议。
网路分层模型
OSI模型与TCP/IP模型对应关系
TCP/IP协议族
- TCP/IP 实际上一个一起工作的通信家族,为网际数据通信提供通路。
- TCP/IP协议族大体上分为三部分
lnternet协议(IP)
传输控制协议(TCP)和用户数据报文协议(UDP)
处于TCP和UDP之上的一组协议专门开发的应用程序。它们包括:远程登录(TELNET)、文件传送协议(FTP)、域名服务(DNS)和简单的邮件传送程序(SMTP)等许多协议。
网络层协议
- lnternet协议(IP)
该协议被设计成互联分组交换通信网,以形成一个网际通信环境。它负责在源主机和目的地主机之间传输来自其较高层软件的称为数据报文的数据块,它在源和目的地之间提供非连接型传递服务。 - 网际控制报文协议(ICMP)
ICMP实际上不是IP层部分,但直接同IP层一起工作,报告网络上的某些出错情况。允许网际路由器传输差错信息或测试报文。 - 地址识别协议(ARP)
ARP实际上不是网络层部分,它处于IP和数据链路层之间,它是在32位lP地址和48位局域网物理地址之间执行翻译的协议。
传输层协议
- 传输控制协议(TCP)
可靠的面向连接的传输层服务
主要功能:
监听输入对话建立
请求请求另一网络站点对话
可靠的发送和接收数据
适度的关闭对话 - 用户数据报文协议(UDP)
UDP提供不可靠的非连接型传输层服务
它允许在源和目的地站点之间传送数据,而不必在传送数据之前建立对话。
不使用TCP使用的端对端差错校验。
传输层功能全都发挥,而开销却比较低。
主要用于那些不要求TCP协议的非连接型的应用程序。例如,名字服务、网络管理、视频点播和网络会议等。
应用层协议
- Telnet
远程登录 - FTP和TFTP
文件传送协议 - SMTP
简单的文件传送协议 - DNS
域名服务
数据封装
Internet协议(IP)
- IP的主要目的是为数据输入/输出网络提供基本算法,为高层协议提供无连接的传送服务。这意味着在IP将数据递交给接收站点以前不在传输站点和接收站点之间建立对话(虚拟链路)。它只是封装和传递数据,但不向发送者或接收者报告包的状态,不处理所遇到的故障。
- IP协议有以下四个主要功能
数据传送
寻址
路由选择
数据报文的分段 - IP协议不注意包内的数括类型,'所知是的一0xB须将某些称为IP帧头的控制协议加到高层协议(TCP或者UDP)所接受的数据上。
IP地址
- 在TCP/IP网络中,每个主机都有唯一的地址,它是通过IP协议来实现的。
- IP协议要求在每次与TCP/IP网络建立连接时,每台主机都必须为这个连接分配一个唯一的32位地址,因为在这个32位IP地址中,不但可以用来识别某一台主机,而且还隐含着网际间的路径信息。
- 主机是指网络上的一个节点,不能简单地理解为一台计算机,实际上IP地址是分配给计算机的网络适配器(即网卡)的,一台计算机可以有多个网络适配器,就可以有多个IP地址,一个网络适配器就是一个节点。
- IP地址为32位地址,一般以4个字节表示。每个字
节的数字又用十进制表示,即每个字节的数的范围是
0~255,且每个数字之间用点隔开,例如:
192.168.0.112,这种记录方法称为"点-分”十进制记号法。IP地址的结构如下所示:
IP地址分类
- Internet地址可以分为五类
- A、B、C三类由lnterNIC (Internet网络信息中心)在全球范围内统一分配,D、E类为特殊地址。
端口号
- TCP/UDP协议使用16位整数存储端口号,所以每个主机拥有65,535个端口
- —些端口被IANA分配给指定应用
21: FTP
23: Telnet
80: HTTP
RFC 1700(大约有2000个保留端口)
传输控制协议(TCP)
- TCP(传输控制协议Transmission Control Protocol)是重要的传输层协议,TCP提供一种面向连接的、可靠的字节流服务。
- TCP协议的目的是允许数据同网络上的另外站点进行可靠的交换。它能提供端口编号的译码,以识别主机的应用程序,而且完成数据的可靠传输。
- TCP协议具有严格的内装差错检验算法确保数据的完整性。
- TCP协议是面向字节的顺序协议,这意味着包内的每个字节被分配一个顺序编号,并分配给每包一个顺序编号。
用户数据报文协议(UDP)
- UDP(用户数据报协议User Datagram Protocol)也是TCP/IP的传输层协议,它是无连接的,不可靠的传输服务。当接收数据时它不向发送方提供确认信息,它不提供输入包的顺序,如果出现丢失包或重份包的情况,也不会向发送方发出差错报文。
它允许在源和目的地站点之间传送数据,而不必在传送数据之前建立对话。
不使用TCP使用的端对端差错校验
传输层功能全都发挥,而开销却比较低。 - 由于它执行功能时具有较低的开销,因而执行速度比TCP快。它多半用于不需要可靠传输的应用程序,例如网络视频点播和视频会议等。
TCP和UDP协议区别
- TCP以连接为基础,即两台电脑必须先建立一个连接,然后才能传输数据。事实上,发送和接受的电脑必须一直互相通讯和联系。
- UDP是一个无连接服务,数据可以直接发送而不必在两台电脑之间建立一个网络连接。它和有连接的TCP相比,占用带宽少,但是无法确认数据是否真正到达了客户端,而客户端收到的数据也不知道是否还是原来的发送顺序。
Socket(套接字)
概念
- Socket(套接字)是一种通讯机制,它包含一整套的调用接口和数据结构的定义,它给应用进程提供了使用如TCP/UDP等网络协议进行网络通讯的手段。
- Linux中的网络编程通过Socket接口实现,Socket既是一种特殊的lO,提供对应的文件描述符。一个完整的Socket都有一个相关通还{协议,s地出二个木出的,远程地址,远程端口};每一个Socket有一个本地的唯—Socket,由操作系统分配。
创建Socket
#include <syslsocket.h>
int socket(int domain, int type, int protocol)
//返回:成功返回描述符,出错返回-1
-
Socket创建在内核中,若创建成功返回内核文件描述表中的socket描述符。
-
参数
domain
AF_INET lPv4因特网域
AF_INET6 IPv6因特网域
AF_UNIX unix域
AF_UNSPEC 未指定
protocol
通常为0,表示按给定的域和套接字类型选择默认协议。 -
参数type
sOCk_STREAM
流式的套接字可以提供可靠的、面向连接的通讯流。它使用了TCP协议。TCP保证了数据传输的正确性和顺序性。
sOCK_DGRAM
数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错。使用数据报协议UDP协议。
sOCK_RAW
原始套接字允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议实现的测试等。
sOCK_SEQPACKET
长度固定、有序、可靠的面向链接报文传递
字节序
概念
- 不同体系结构的主机使用不同的字节序存储器中保存多学节整数。学节存储顺序不同,有的系统是高位在前低位在后,而有的系统是低位在前,高位在后。
- 字节序分为大端和小端字节序
- 网络协议使用网络字节序即大端字节序
字节序转换函数
网络传输的数据大家是一定要统一顺序的。所以对于内部字节表示顺序和网络字节顺序不同的机器,就一定要对数据进行转换。
uint32_t htonl(uint32_t hostlong);
//将一个32位整数由主机字节序转换成网络字节序
uint1 6_t htons(uint16_t hostshort);
//将一个16位整数由主机字节序转换成网络字节序
uint32_t ntohl(uint32_t netlong);
//将一个32位整数由网络字节序转换成主机字节序
uint16_t ntohs(uint16_t netshort);
//将一个16位整数由网络字节序转换成主机字节序
通用地址结构
#include <sys/socket.h>
struct sockaddr {
unsigned short sa_family;//地址族,AF_xxx
char sa_data[14]; //14字节的协议地址
};
- sa_data包含了一些远程电脑的地址、端口和套接字的数目,它里面的数据是杂溶在一起的。
- sa_family一般来说,IPV4使用AF_INET。
- 在传递给需要地址结构的函数时,把指向该结构的指针转换成(struct sockaddr *)传递进去。
因特网地址结构
struct in_addr{
in_addr_t s_addr;
/*ipv4地址*/
};
struct sockaddr_in {
short int sin_family;
/*Internet地址族如AF_INET(主机字节序)*/
unsigned short int sin_port;
/*端口号,16位值(网络字节序)*/
struct in_addr sin_addr;
/*Internet地址,32位IPv4地址(网络字节序)*/
unsigned char sin_zero[8];
/*添0(为了格式对齐的填充位)*/
};
这两个数据类型是等效的,可以相互转换,通常使用sockaddr_in更为方便。
IPV4地址族和字符地址间的转换
#include <arplinet.h>
const char *inet_ntop(int domain, const void *restrict addr,
char *restrict str, socklen_t size);
//返回:成功返回地址字符串指针,出错返回NULL
//功能:网络字节序转换成点分十进制
int inet_pton(int domain, const char*restrict str,
void *restrict addr);
//返回:成功返回1,无效格式返回0,出错返回-1
//功能:点分十讲制转换为网络字节序
参数
domain: Internet地址族,如AF_INET
addr: Internet地址,32位IPv4地址(网络字节序)
str:地址字符串(点分十进制)指针
size:地址字符串大小
TCP客户端服务器编程模型
客户端调用序列
调用socket函数创建套接字调用connect连接服务器端调用I/0函数(read/write)与服务器端通讯
调用close关闭套接字
服务器端调用序列
调用socket函数创建套接字调用bind绑定本地地址和端口调用listen启动监听
调用accept从已连接列队中提取客户连接
调用I/0函数(read/write)与客户端通讯
调用close关闭套接字
套接字与地址绑定
- 绑定地址
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t len);
//返回:成功返回0,出错返回-1
- 查找绑定到套接字的地址
#include <sys/socket.h>
int getsockname(int sockfd,struct sockaddr *restrict addr,
socklen_t *restrict alenp);
//返回:成功返回o,出错返回-1
- 获取对方地址
#include <sys/socket.h>
int getpeername(int sockfd,struct sockaddr*restrict addr,
socklen_t *restrict alenp);
//返回:成功返回o,出错返回-1
建立连接
- 服务器端
#include <sys/socket.h>
int listen(int sockfd, int backlog);
//返回:成功返回o,出错返回-1。backlog指定进行客户端连接排队的队列长度
int accept(int sockfd , struct sockaddr*restrict addr,
socklen_t *restrict len);
- 客户端
#include <syslsocket.h>
int connect(int sockfd,const struct sockaddr *addr,socklen_t len);
//返回:成功返回0,出错返回-1
TCP的连接和关闭过程
服务器端并发性处理
- 多进程模型
- 多线程模型
- I/O多路转换(select)
UDP客户端服务器编程模型
发送数据
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flag);
/* 返回:成功返回发送字节数,出错返回-1 */
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flag,
const struct sockaddr *destaddr, socklen_t destlen);
/* 返回:成功返回发送的字节数,出错返回-1 */
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flag);
/* 返回:成功返回发送字节数,出错返回-1 */
struct msghdr {
void *msg_name;
/* 可选地址 */
socklen_t msg_namelen;
/* 地址大小,以字节为单位 */
struct iovec *msg_iov;
/* I/O缓冲区数组 */
int msg_iovlen;
/* 数组中元素的个数 */
void *msg_control;
/* 辅助数据 */
socklen_t msg_controllen;
/* 辅助数据的字节数 */
int msg_flags;
/* 接收到的消息的标志 */
};
接收数据
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flag);
ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flag,
struct sockaddr *restrict addr, socklen_t *restrict addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flag);
/* 返回:返回消息的字节数,无消息返回0,出错返回-1 */
域名解析
域名解析函数
struct hostent{
char *h_name; /* 正式主机名 */
char **h_aliases; /* 别名,字符串数组 */
int h_addrtype; /* 协议类型 */
int h_length; /* 网络地址大小*/
char **h_addr_list; /* 指向网络地址的指针 */
}
#include <netdb.h>
struct hostent *gethostent(void);
struct hostent* gethostbyname(const char *hostname);
void sethostent(int stayopen);
void endhostent(void);