1、基本流程
wifi连接到sta,新建socket,连接到tcp server。
2、API函数
1、创建socket
代码语言:javascript复制int socket(int domain,int type,int protocol)
domain
:为地址族,也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6;
type
:数据传输方式/套接字类型,常用的有 SOCK_STREAM
(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM
;
protocol
:为协议类型,常用的有 IPPROTO_TCP
和 IPPTOTO_UDP
,分别表示 TCP
传输协议和 UDP
传输协议;
返回值为套接字。
2、连接
代码语言:javascript复制int connect(int s,const struct sockaddr *name,socklen_t namelen)
s
:套接字;
sockaddr
:套接字s
想要连接的主机地址和端口号;
namelen
:name缓冲区的长度。
3、发送
代码语言:javascript复制ssize_t send(int s,const void *dataptr,size_t size,int flags)
s
:发送端套接字描述符;
dataptr
:要发送数据的缓冲区;
size
:要发送的数据的字节数;
flags
:一般置0。
4、接收
代码语言:javascript复制ssize_t recv(int s,void *mem,size_t len,int flags)
s
:接收端套接字描述符;
mem
:接收数据缓冲区;
size
:要接收的最大长度;
flags
:一般置0。
5、关闭连接
代码语言:javascript复制int shutdown(int s,int how)
s
:套接字描述符;
how
:标志,用于描述禁止哪些操作。
6、关闭socket
代码语言:javascript复制close(int s)
s
:套接字描述符。
7、控制套接口的模式
代码语言:javascript复制int ioctlsocket(int s,long cmd,void *argp)
s:套接字描述符;
cmd
:对套接口s的操作命令;
argp
:指向cmd命令所带参数的指针;
当cmd
为FIONBIO
时表示非阻塞,对应的argp
为1
时是非阻塞,为0
时是阻塞。
8、设置套接口的选项
代码语言:javascript复制int setsockopt(int s,int level,int optname,const void *opval,socklen_t optlen)
s
:套接口的描述字;
level
:选项定义的层次;支持SOL_SOCKET
、IPPROTO_TCP
、IPPROTO_IP
和IPPROTO_IPV6
;
optname
:需设置的选项;
optval
:指针,指向存放选项待设置的新值的缓冲区;
optlen
:optval缓冲区长度;
对应的optname
为SO_RCVTIMEO
时表示接收超时,optval
为超时时间,optlen
为长度。
3、核心代码
代码语言:javascript复制static void tcp_client(void)
{
char rx_buffer[128];
char host_ip[] = HOST_IP_ADDR;
int addr_family = 0;
int ip_protocol = 0;
struct timeval timeout={
.tv_sec = 0,
.tv_usec = 20,
};
u_long non_blocking=1;
int sendcnt=0;
while (1) {
struct sockaddr_in dest_addr;
dest_addr.sin_addr.s_addr = inet_addr(host_ip);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
int sock = socket(addr_family, SOCK_STREAM, ip_protocol);
//创建socket
if (sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
//建立连接
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Successfully connected");
ioctlsocket(sock,FIONBIO,&non_blocking);
//设置为非阻塞
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
//超时接收时间
while (1) {
int err = send(sock, payload, strlen(payload), 0);
//发送
if (err < 0) {
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
break;
}
sendcnt ;
if((sendcnt%5)==0)
{
non_blocking=0;
ioctlsocket(sock,FIONBIO,&non_blocking);
}
else
{
non_blocking=1;
ioctlsocket(sock,FIONBIO,&non_blocking);
}
int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
//接收
#if 1
if (len >= 0) {
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
ESP_LOGI(TAG, "%s", rx_buffer);
}
#else
// Error occurred during receiving
if (len < 0) {
ESP_LOGE(TAG, "recv failed: errno %d", errno);
break;
}
// Data received
else {
rx_buffer[len] = 0;
ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
ESP_LOGI(TAG, "%s", rx_buffer);
}
#endif
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
if (sock != -1) {
ESP_LOGI(TAG, "Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
}
}
vTaskDelete(NULL);
}
在代码中设置为循环发送次数为5的倍数后,设置为阻塞,直到接收到数据后,再设置为非阻塞。
4、实验
可以看到,esp32连接到ap后,ap分配了192.168.2.3
的地址,创建socket后连接到了192.168.2.5
的服务器。
————————END————————