先来一个三次握手和四次挥手
嵌入式连接过程:
1,af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。
2,SOCK_STREAM 是一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送。
3,protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议,IPPROTO_IP = 0.使用 IPv4 地址,参数 af 的值为 PF_INET。如果使用 SOCK_STREAM 传输数据,那么满足这两个条件的协议只有 TCP, protocol 的值设为 0,系统会自动推演出应该使用什么协议。
SOCKET socket(int af, int type, int protocol);
int listen_sock;
listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
struct sockaddr_in destAddr;
destAddr.sin_addr.s_addr = htonl(INADDR_ANY); //32位IP地址
destAddr.sin_family = AF_INET; //地址族,ip地址类型,AF_INET为IPv4类型
destAddr.sin_port = htons(WIFI_AP_PORT); //16位端口号
inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1); //将IPv4 Internet地址转换为字符串
/* 绑定地址和端口号 */
bind(listen_sock, (struct sockaddr *)&destAddr, sizeof(destAddr));
sockaddr 和 sockaddr_in 的长度相同,都是16字节,只是将IP地址和端口号合并到一起,用一个成员 sa_data 表示。要想给 sa_data 赋值,必须同时指明IP地址和端口号,例如”127.0.0.1:80“,遗憾的是,没有相关函数将这个字符串转换成需要的形式,也就很难给 sockaddr 类型的变量赋值,所以使用 sockaddr_in 来代替。这两个结构体的长度相同,强制转换类型时不会丢失字节,也没有多余的字节。
可以认为,sockaddr 是一种通用的结构体,可以用来保存多种类型的IP地址和端口号,而 sockaddr_in 是专门用来保存 IPv4 地址的结构体。
int listen(SOCKET sock, int backlog);
sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。
当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列(Request Queue)。
注意:listen() 只是让套接字处于监听状态,并没有接收请求。接收请求需要使用 accept() 函数。
/* 检测是否还在连接 */
setsockopt(listen_sock, SOL_SOCKET, SO_KEEPALIVE,(void *)&keepalive,sizeof(keepalive));
SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);
它的参数与 listen() 和 connect() 是相同的:sock 为服务器端套接字,addr 为 sockaddr_in 结构体变量,addrlen 为参数 addr 的长度,可由 sizeof() 求得。
accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而 sock 是服务器端的套接字,大家注意区分。后面和客户端通信时,要使用这个新生成的套接字,而不是原来服务器端的套接字。
int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen);
和 bind() 相同
int send(SOCKET sock, const char *buf, int len, int flags);
sock 为要发送数据的套接字,buf 为要发送的数据的缓冲区地址,len 为要发送的数据的字节数,flags 为发送数据时的选项。
返回值和前三个参数不再赘述,最后的 flags 参数一般设置为 0 或 NULL
int recv(SOCKET sock, char *buf, int len, int flags);
与上面一样