1 网络通信基础
1.1 TCP/IP协议族
2 网络框架
3 TCP
4 Socket
4.1 Socket概述
socket是在应用层和传输层之间的一个抽象层,他把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用,以实现进程在网络中通信。
4.2 API详解
4.2.1 socket():创建socket文件描述符
为一个socket数据结构分配存储空间,成功时,返回一个用于监听的socket描述符。
所需头文件 | #include <sys/types.h> #include <sys/socket.h> |
|
函数原型 |
int socket(int domain, int type, int protocol); |
|
输入值 | domain 协议族 |
AF_UNIX/AF_LOCAL:Unix域协议 |
AF_INET:IPv4 | ||
AF_INET6:IPv6 | ||
type | SOCKET_STREAM:TCP | |
SOCKET_DGRAM:UDP | ||
SOCKET_RAW:提供传输层以下的协议,可以访问内部网络接口,例如接收和发送ICMP报文 | ||
protocol | 0,系统会根据type自动选择默认协议类型 | |
当type=SOCKET_RAW时需要设置此值说明协议类型 | ||
返回值 | -1,失败,失败原因存在于error中 | |
!=-1, 成功, 监听的socket描述符 |
4.2.2 bind():将一个本地协议地址与监听socket文件描述符关联
所需头文件 | #include <sys/types.h> #include <sys/socket.h> |
|
函数原型 |
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
|
输入值 | sockfd | 要关联的socket文件描述符 |
addr | 指向sockaddr,包含IP地址和端口信息的结构体 | |
addrlen | sockaddr结构的大小,可设置sizeof(struct sockaddr) | |
返回值 | -1,失败,失败原因存在于error中 | |
0 , 成功 |
4.4.3 listen() 实施监听服务
所需头文件 | #include <sys/types.h> #include <sys/socket.h> |
|
函数原型 |
int listen(int sockfd, int backlog); |
|
输入值 | sockfd | 要监听的socket文件描述符 |
backlog | 套接字排队的最大连接个数 | |
返回值 | -1,失败,失败原因存在于error中 | |
0 , 成功, |
4.4.4 accept()阻塞等待客户端接入
accept()函数仅被TCP类型的服务器程序调用,将从连接请求队列中获得连接信息,创建新的套接字,并返回该套接字的文件描述符,客户端可以通过这个描述符与服务器通信。
通常我们把accept()第一个参数成为监听套接字(listening socket),把accept()功能返回值成为已连接套接字(connected socket)。
所需头文件 | #include <sys/types.h> #include <sys/socket.h> |
|
函数原型 |
int accept(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
|
输入值 | sockfd | 要关联的监听socket文件描述符 |
addr | 指向sockaddr,包含IP地址和端口信息的结构体 | |
addrlen | sockaddr结构的大小,可设置sizeof(struct sockaddr) | |
返回值 | -1,失败,失败原因存在于error中 | |
成功,已连接的socket描述符 |
4.4.5 connect()客户端调用来与服务器建立连接
所需头文件 | #include <sys/types.h> #include <sys/socket.h> |
|
函数原型 |
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
|
输入值 | sockfd | 要关联的监听socket文件描述符 |
addr | 指向sockaddr,包含IP地址和端口信息的结构体 | |
addrlen | sockaddr结构的大小,可设置sizeof(struct sockaddr) | |
返回值 | -1,失败,失败原因存在于error中 | |
0 , 成功 |
4.4.6 close()关闭连接的socket文件描述符
所需头文件 | #include <unistd.h> | |
函数原型 |
int close(int sockfd) |
|
输入值 | sockfd | 要关联的socket文件描述符 |
返回值 | -1,失败,失败原因存在于error中 | |
0 , 成功 |
4.4.7 shutdown()终止socket通信
所需头文件 | #include <sys/socket.h> |
|
函数原型 |
int shutdown(int s, int how) |
|
输入值 | sockfd | 要关联的socket文件描述符 |
how | 0(SHUT_RD):关闭socket连接的读这一半,不再接收套接字中的数据且现留在收缓冲区的数据作废 | |
1(SHUT_WR):关闭socket连接的写这一半(半关闭),但留在套接字发送缓冲区中的数据都会被发送,后跟TCP连接终止序列,不管访问计数是否大于0,此后将不能再执行对套接字的任何写操作 | ||
2(SHUT_RDWR):socket连接的读、写都关闭 | ||
返回值 | -1,失败,失败原因存在于error中 | |
0 , 成功 |
4.4.8 send()TCP类型数据发送
所需头文件 | #include <sys/types.h> #include <sys/socket.h> |
|
函数原型 |
ssize_t send(int sockfd, const void *buf, size_t len, int flags); |
|
输入值 | sockfd | 要关联的socket文件描述符 |
addr | 指向sockaddr,包含IP地址和端口信息的结构体 | |
addrlen | sockaddr结构的大小,可设置sizeof(struct sockaddr) | |
返回值 | -1,失败,失败原因存在于error中 | |
0 , 成功 |
4.4.9 recv()TCP类型数据接收
所需头文件 | #include <sys/types.h> #include <sys/socket.h> |
|
函数原型 |
ssize_t recv(int sockfd, void *buf, size_t len, int flags); |
|
输入值 | sockfd | 要关联的socket文件描述符 |
*buf | 接收缓冲区地址 | |
len | 接收缓冲区数据长度(按字节计算) | |
flags | 0 | |
返回值 | -1,失败,失败原因存在于error中 | |
!=-1 , 返回接收到的数据字节数 |
4.4.10 sendto()UDP类型数据发送
4.4.11 recvfrom()UDP类型数据接收
4.2.12 htonl()、htons()、ntohl()、ntohs()
网络字节序与本地字节序互相转换,规定网络字节序一律是大端模式,h——本机字节序(大端或者小端),n——网络字节序(大端)
#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); //网络字节序转换成本机字节序
示例:
uint32_t addr=0x12345678, result=0; printf("%#x, %#x ", addr, htonl(addr));
结果:0x78563412
4.2.11 inet_addr()
in_addr_t inet_addr(const char *cp); //将一个字符串形式的点分十进制IP地址转换为32位二进制网络字节序
示例:
addr = inet_addr(“192.168.0.11”); //网络字节序:规定使用大端模式 printf("addr=%#X ", addr);
结果:addr=0XB00A8C0
4.2.12 inet_ntoa()、inet_aton()
int inet_aton(const char *cp, struct in_addr *inp); char *inet_ntoa(struct in_addr in);
4.2.13 inet_ntop()、inet_pton()
对于IPv4地址和IPv6地址都适用。
#include <arpa/inet.h> /*将数值格式转化为点分十进制的ip地址格式*/ const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); //返回值:1,成功; 0,输入无效; -1,出错 /*点分十进制IP地址转换为用于网络传输的数值格式*/ int inet_pton(int af, const char *src, void *dst); //返回值:NULL,出错
示例:
#include <stdio.h> #include <arpa/inet.h> int main(void) { /*1. inet_pton转换:点分十进制 转 网络数值格式*/ struct in_addr addr; int ret=0; ret = inet_pton(AF_INET, "192.168.0.11", &addr); if(ret != 1){ printf("inet_pton error "); return -1; } printf("addr=%#X. ", addr.s_addr); /*2. inet_ntop转换:网络数值格式 转 点分十进制*/ char buf[50]={0}; const char *p_ret=NULL; addr.s_addr = 0xB00A8C0; p_ret = inet_ntop(AF_INET, &addr, buf, sizeof(buf)); if(p_ret == NULL){ printf("inet_pton error "); return -1; } printf("addr=%s. ", buf); }
结果:addr=0XB00A8C0.
addr=192.168.0.11.
4.2.14 sockaddr和sockaddr_in
sinport和sin_addr都必须是网络字节序(NBO)。
sockaddr缺陷是把sin_port和sin_addr混在一起放在了sa_data[14]里了;
两者长度都是16个字节,因此可以互相转换;
sockaddr常用于bind, connect, recvfrom, sendto等函数的参数