本意论述了TCP/IP和网络编程
TCP/IP协议
TCP/IP (Comer 1988, 2001; RFC1180 1991)是互联网的基础。TCP代表传输控制协议。 IP代表互联网协议。目前有两个 版本的IP, 即 IPv4和IPv6。IPv4使用32位地址,1Pv6则使用128位地址。 本节围绕IPv4进行讨论, 它仍然是目前使用最多的IP版本。TCP/IP的组织结构分为几个层级, 通常称为TCP/IP堆栈。
网络编程
网络编程平台,用户可选择使用服务器上的用户账号进行编程或者直接使用PC设备
服务器-客户机计算模型
大多数网络编程任务都基于服务器-客户机计算模型。在服务器-客户机计算模型中,我们首先在服务器主机上运行服务器进程。然后,我们从客户机主机运行客户机。在 UDP 中,服务器等待来自客户机的数据报,处理数据报并生成对客户机的响应。在TCP 中、服务器等待客户机连接。客户机首先连接到服务器,在客户机和服务器之间建立一个虚拟电路。建立连接后,服务器和客户机可以交换连续的数据流。
套接字编程
在网络编程中,TCP/IP 的用户界面是通过一系列C语言库函数和系统调用来实现的,这些函数和系统调用统称为套接字 API。为了使用套接字API,我们需要套接字地址结构,它用于标识服务器和客户机。netdb.h和 sys/socketh中有套接字地址结构的定义。
struct sockaddr_in{ sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; }; struct in_addr{ uint32_t s_addr; };
●TCP/IP 网络的 sin_family 始终设置为AF_INET。
● sin_port 包含按网络字节顺序排列的端口号。
● sin addr是按网络字节顺序排列的主机 IP地址。
服务器必须创建一个套接字,并将其与包含服务器IP地址和端口号的套接字地址绑定。它可以使用一个固定端口号,或者让操作系统内核选择一个端口号(如果 sin port为0)。为了与服务器通信,客户机必须创建一个套接字。对于UPD 套接字,可以将套接字绑定到服务器地址。如果套接字没有绑定到任何特定的服务器,那么它必须在后续的 sendto()/recvfrom()调用中提供一个包含服务器IP和端口号的套接字地址。
UDP套接字
UDP 套接字使用 sendto()/recvfrom()来发送/接收数据报。
ssize_t sendto(int soCkfd,const void *buf,size_t len,int flags, const struct sockaddr *dest_addr,socklen_t addrlen); ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags, struct sockaddr *src_addr,socklen_t *addrlen);
UDP套接字
在创建套接字并将其绑定到服务器地址之后,TCP服务器使用listen()和 accept()来接收来自客户机的连接
int listen(int sockfd, int backlog);
listen()将sockfd引用的套接字标记为将用于接收连人连接的套接字。backlog 参数定义了等待连接的最大队列长度。
int accept(int sockfd, struct sockaddr *addr, socklen t *addrlen)
accept()系统调用与基于连接的套接字一起使用。它提取等待连接队列上的第一个连接请求用于监听套接字sockfd,创建一个新的连接套接字,并返回一个引用该套接字的新文件描述符,与客户机主机连接。在执行accept()系统调用时,TCP服务器阻塞,直到客户机通过 connect()建立连接。
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
connect()系统调用将文件描述符sockfd引用的套接字连接到addr指定的地址,addrlen 参数指定 addr的大小。addr中的地址格式由套接字sockfd的地址空间决定。
如果套接字 sockfd是SOCK_DGRAM类型,即UDP套接字,addr是发送数据报的默认地址,也是接收数据报的唯一地址。这会限制UDP套接字与特定UDP主机的通信,但实际上很少使用。所以对于UDP套接字来说,连接是可选的或不必要的。如果套接字是 SOCK_STREAM类型,即TCP套接字,connect()调用尝试连接到绑定到addr指定地址的套接字。
send()/read()以及recv/write()
建立连接后,两个TCP主机都可以使用send()/write()发送数据,并使用recv()/read()接收数据。它们唯一的区别是send()和recv()中的flag参数不同,通常情况下可以将其设置为0。
asize_t send(int sockfd, const void *buf, size_t len, int flags), asize_t write(sockfd, void *buf, size_t, len) ssize_t recv(int sockfd, void *buf, bize_t len, int flags); asize_t read(sockfd, void *buf, size_t len);
通用套接字地址结构
sockaddr struct sockaddr { uint8_t sa_len; sa_family_t sa_family; char sa_data[14]; };
IPv6套接字地址结构
IPv6套接字地址结构在<netinet/in.h>头文件中定义
struct in6_addr { unit8_t s6_add[16]; }; #define SIN6_LEN struct sockaddr_in6 { uint8_t sin6_len; sa_family_t sin6_family; in_port_t sin6_port; uint32_t sin6_flowinfo; struct in6_addr sin6_addr; uint32_t sin6_scope_id; };
新的struct sockaddr_storage足以容纳系统所支持的任何套接字地址结构。sockaddr_storage结构在<netinet/in.h>头文件中定义
struct sockaddr_storage { uint8_t ss_len; sa_family_t ss_family; };