LWIP的Socket接口
Socket接口简介
类似于文件操作的一种网络连接接口,通常将其称之为“套接字”。lwIP的Socket接口兼容BSD Socket接口,但只实现完整Socket的部分功能
netconn是对RAW的封装
Socket是对netconn的封装
SOCKET结构体
struct sockaddr {
u8_t sa_len; /* 长度 */
sa_family_t sa_family; /* 协议簇 */
char sa_data[14]; /* 连续的 14 字节信息 */
};
struct sockaddr_in {
u8_t sin_len; /* 长度 */
u8_t sin_family; /* 协议簇 */
u16_t sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP 地址 */
char sin_zero[8];
}
为啥用两个结构体
sockaddr的缺陷是:sa_zero把目标地址和端口信息混在了一起
sockaddr_in该结构体解决了sockaddr的缺陷,把port和addr分开存储在两个变量中
NETCONN相关函数
SOCKET API | 描述 |
scoket | 创建一个scoket连接 |
bind | 服务器端绑定套接字与网卡信息 |
connect | 将 Socket 与远程 IP 地址和端口号绑定 |
listen | 监听连接 |
accept | 断开连接 |
read/recv/recvfrom | 监听连接(只在TCP服务器) |
sendto/send/write | 获取一个TCP连接(只在TCP服务器) |
close | 关闭连接 |
Socket 编程 UDP 连接流程
实现 UDP 协议之前, 用户必须先配置结构体 sockaddr_in 的成员变量才能实现 UDP 连接,该配置步骤如下所示:
① sin_family 设置为 AF_INET 表示 IPv4 网络协议。
② sin_port 为设置端口号, 笔者设置为 8080。
③ sin_addr.s_addr 设置本地 IP 地址。
④ 调用函数 Socket 创建 Socket 连接,注意: 该函数的第二个参数 SOCK_STREAM 表
示 TCP 连接, SOCK_DGRAM 表示 UDP 连接。
⑤ 调用函数 bind 将本地服务器地址与 Socket 进行绑定。
⑥ 调用收发函数接收或者发送。
实现连接的主要函数
/* 需要自己设置远程IP地址 */
#define IP_ADDR "192.168.1.111"#define LWIP_DEMO_RX_BUFSIZE 200 /* 最大接收数据长度 */
#define LWIP_DEMO_PORT 8080 /* 连接的本地端口号 */
#define LWIP_SEND_THREAD_PRIO ( tskIDLE_PRIORITY + 3 ) /* 发送数据线程优先级 *//* 接收数据缓冲区 */
uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE];
/* 发送数据内容 */
char g_lwip_demo_sendbuf[] = "ALIENTEK DATA \r\n";
/* 数据发送标志位 */
uint8_t g_lwip_send_flag;
struct sockaddr_in g_local_info; /* 定义Socket地址信息结构体 */
socklen_t g_sock_fd; /* 定义一个Socket接口 */
static void lwip_send_thread(void *arg);extern QueueHandle_t g_display_queue; /* 显示消息队列句柄 *//*** @brief 发送数据线程* @param 无* @retval 无*/
void lwip_data_send(void)
{sys_thread_new("lwip_send_thread", lwip_send_thread, NULL, 512, LWIP_SEND_THREAD_PRIO );
}/*** @brief lwip_demo实验入口* @param 无* @retval 无*/
void lwip_demo(void)
{BaseType_t lwip_err;char *tbuf;lwip_data_send(); /* 创建发送数据线程 */memset(&g_local_info, 0, sizeof(struct sockaddr_in)); /* 将服务器地址清空 */g_local_info.sin_len = sizeof(g_local_info);g_local_info.sin_family = AF_INET; /* IPv4地址 */g_local_info.sin_port = htons(LWIP_DEMO_PORT); /* 设置端口号 */g_local_info.sin_addr.s_addr = htons(INADDR_ANY); /* 设置本地IP地址 */g_sock_fd = socket(AF_INET, SOCK_DGRAM, 0); /* 建立一个新的socket连接 */tbuf = mymalloc(SRAMIN, 200); /* 申请内存 */sprintf((char *)tbuf, "Port:%d", LWIP_DEMO_PORT); /* 客户端端口号 */lcd_show_string(5, 150, 200, 16, 16, tbuf, BLUE);/* 建立绑定 */bind(g_sock_fd, (struct sockaddr *)&g_local_info, sizeof(struct sockaddr_in));while (1){memset(g_lwip_demo_recvbuf, 0, sizeof(g_lwip_demo_recvbuf));recv(g_sock_fd, (void *)g_lwip_demo_recvbuf, sizeof(g_lwip_demo_recvbuf), 0);lwip_err = xQueueSend(g_display_queue,&g_lwip_demo_recvbuf,0);if (lwip_err == errQUEUE_FULL){printf("队列Key_Queue已满,数据发送失败!\r\n");}}
}/*** @brief 发送数据线程函数* @param pvParameters : 传入参数(未用到)* @retval 无*/
void lwip_send_thread(void *pvParameters)
{pvParameters = pvParameters;g_local_info.sin_addr.s_addr = inet_addr(IP_ADDR); /* 需要发送的远程IP地址 */while (1){if ((g_lwip_send_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA) /* 有数据要发送 */{sendto(g_sock_fd, /* scoket */(char *)g_lwip_demo_sendbuf, /* 发送的数据 */sizeof(g_lwip_demo_sendbuf), 0, /* 发送的数据大小 */(struct sockaddr *)&g_local_info, /* 接收端地址信息 */ sizeof(g_local_info)); /* 接收端地址信息大小 */g_lwip_send_flag &= ~LWIP_SEND_DATA;}vTaskDelay(100);}
}
Socket 编程 UDP 组播实验
必须对以下保证设置
\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_eth.c
static void ETH_MACDMAConfig(ETH_HandleTypeDef *heth, uint32_t err)
macinit.ReceiveAll = ETH_RECEIVEALL_ENABLE; //使能 全部接收
macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_NONE;//不过滤 组播的帧
\Middlewares\lwip\arch\lwipopts.h
保证设置1
\Middlewares\LWIP\arch\ethernetif.c
static void low_level_init(struct netif *netif)
必须保证添加如下所有标志位
netif->flags = NETIF_FLAG_BROADCAST|NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP|NETIF_FLAG_IGMP; /*广播 ARP协议 链接检测*/
组播的主要实现函数
/* socket信息 */
struct link_socjet_info
{struct sockaddr_in client_addr; /* 网络地址信息 */socklen_t client_addr_len; /* 网络地址信息长度 */int optval; /* 为存放选项值 */int sfd; /* socket控制块 */ip_mreq multicast_mreq; /* 组播控制块 */struct{uint8_t *buf; /* 缓冲空间 */uint32_t size; /* 缓冲空间大小 */} send; /* 发送缓冲 */struct{uint8_t *buf; /* 缓冲空间 */uint32_t size; /* 缓冲空间大小 */} recv; /* 接收缓冲 */
};/* 多播信息 */
struct ip_mreq_t
{struct ip_mreq mreq; /* 多播信息控制块 */socklen_t mreq_len; /* 多播信息长度 */
};#define LWIP_SEND_THREAD_PRIO (tskIDLE_PRIORITY + 3) /* 发送数据线程优先级 */
void lwip_send_thread(void *pvParameters);
/* 接收数据缓冲区 */
static uint8_t g_lwip_demo_recvbuf[1024];
static uint8_t g_lwip_demo_sendbuf[] = {"ALIENTEK DATA\r\n"}; /* 多播 IP 地址 */
#define GROUP_IP "224.0.1.0"/*** @brief 测试代码* @param 无* @retval 无*/
void lwip_demo(void)
{struct link_socjet_info *socket_info;struct ip_mreq_t *mreq_info;socket_info = mem_malloc(sizeof(struct link_socjet_info));mreq_info = mem_malloc(sizeof(struct ip_mreq_t));socket_info->sfd = socket(AF_INET, SOCK_DGRAM, 0);if (socket_info->sfd < 0){printf("socket failed!\n");}socket_info->client_addr.sin_family = AF_INET;socket_info->client_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* 待与 socket 绑定的本地网络接口 IP */ socket_info->client_addr.sin_port = htons(9999); /* 待与 socket 绑定的本地端口号 */socket_info->client_addr_len = sizeof(socket_info->client_addr);/* 设置接收和发送缓冲区 */socket_info->recv.buf = g_lwip_demo_recvbuf;socket_info->recv.size = sizeof(g_lwip_demo_recvbuf);socket_info->send.buf = g_lwip_demo_sendbuf;socket_info->send.size = sizeof(g_lwip_demo_sendbuf);/* 将 Socket 与本地某网络接口绑定 */int ret = bind(socket_info->sfd, (struct sockaddr*)&socket_info->client_addr, socket_info->client_addr_len);if (ret < 0){printf(" bind error!\n ");}mreq_info->mreq.imr_multiaddr.s_addr = inet_addr(GROUP_IP); /* 多播组 IP 地址设置 */mreq_info->mreq.imr_interface.s_addr = htonl(INADDR_ANY); /* 待加入多播组的 IP 地址 */mreq_info->mreq_len = sizeof(struct ip_mreq);/* 添加多播组成员(该语句之前,socket 只与 某单播IP地址相关联 执行该语句后 将与多播地址相关联) */ret = setsockopt(socket_info->sfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq_info->mreq,mreq_info->mreq_len);if (ret < 0){printf("setsockopt failed !");}else{printf("setsockopt success\n");}int length = 0;struct sockaddr_in sender;int sender_len = sizeof(sender);sys_thread_new("lwip_send_thread", lwip_send_thread, (void *)socket_info, 512, LWIP_SEND_THREAD_PRIO );while(1){length = recvfrom(socket_info->sfd,socket_info->recv.buf,socket_info->recv.size,0,(struct sockaddr*)&sender,(socklen_t *)&sender_len);socket_info->recv.buf[length]='\0';printf("%s %d : %s\n", inet_ntoa(sender.sin_addr), ntohs(sender.sin_port), socket_info->recv.buf);vTaskDelay(10);}setsockopt(socket_info->sfd, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq_info->mreq, mreq_info->mreq_len);closesocket(socket_info->sfd);
}/*** @brief 发送数据线程函数* @param pvParameters : 传入struct link_socjet_info结构体* @retval 无*/
void lwip_send_thread(void *pvParameters)
{struct link_socjet_info *socket_info = pvParameters;socket_info->client_addr.sin_addr.s_addr = inet_addr(GROUP_IP); /* 组播ip */while (1){/* 数据广播 */sendto(socket_info->sfd, socket_info->send.buf, socket_info->send.size + 1, 0, (struct sockaddr*)&socket_info->client_addr,socket_info->client_addr_len);vTaskDelay(1000);}
}
Socket 编程 TCP 客户端流程
实现 TCP 客户端之前,用户必须先配置结构体 sockaddr_in 的成员变量才能实现TCPClient 连接,该配置步骤如下所示:
① sin_family 设置为 AF_INET 表示 IPv4 网络协议。
② sin_port 为设置端口号。
③ sin_addr.s_addr 设置远程 IP 地址。
④ 调用函数 Socket 创建 Socket 连接, 注意: 该函数的第二个参数 SOCK_STREAM 表
示 TCP 连接, SOCK_DGRAM 表示 UDP 连接。
⑤ 调用函数 connect 连接远程 IP 地址。
⑥ 调用收发函数实现远程通讯。
Socket 接口的 TCPClient 实验
实现的主要代码
/* 设置远程IP地址 */
//#define DEST_IP_ADDR0 192
//#define DEST_IP_ADDR1 168
//#define DEST_IP_ADDR2 1
//#define DEST_IP_ADDR3 167/* 需要自己设置远程IP地址 */
#define IP_ADDR "192.168.1.37"#define LWIP_DEMO_RX_BUFSIZE 100 /* 最大接收数据长度 */
#define LWIP_DEMO_PORT 8080 /* 连接的本地端口号 */
#define LWIP_SEND_THREAD_PRIO ( tskIDLE_PRIORITY + 3 ) /* 发送数据线程优先级 */
/* 接收数据缓冲区 */
uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE]; /* 发送数据内容 */
uint8_t g_lwip_demo_sendbuf[] = "ALIENTEK DATA \r\n";
/* 数据发送标志位 */
uint8_t g_lwip_send_flag;
int g_sock = -1;
int g_lwip_connect_state = 0;
static void lwip_send_thread(void *arg);extern QueueHandle_t g_display_queue; /* 显示消息队列句柄 *//*** @brief 发送数据线程* @param 无* @retval 无*/
void lwip_data_send(void)
{sys_thread_new("lwip_send_thread", lwip_send_thread, NULL, 512, LWIP_SEND_THREAD_PRIO );
}/*** @brief lwip_demo实验入口* @param 无* @retval 无*/
void lwip_demo(void)
{struct sockaddr_in atk_client_addr;err_t err;int recv_data_len;BaseType_t lwip_err;char *tbuf;lwip_data_send(); /* 创建发送数据线程 */while (1){
sock_start:g_lwip_connect_state = 0;atk_client_addr.sin_family = AF_INET; /* 表示IPv4网络协议 */atk_client_addr.sin_port = htons(LWIP_DEMO_PORT); /* 端口号 */atk_client_addr.sin_addr.s_addr = inet_addr(IP_ADDR); /* 远程IP地址 */g_sock = socket(AF_INET, SOCK_STREAM, 0); /* 可靠数据流交付服务既是TCP协议 */memset(&(atk_client_addr.sin_zero), 0, sizeof(atk_client_addr.sin_zero));tbuf = mymalloc(SRAMIN, 200); /* 申请内存 */sprintf((char *)tbuf, "Port:%d", LWIP_DEMO_PORT); /* 客户端端口号 */lcd_show_string(5, 150, 200, 16, 16, tbuf, BLUE);/* 连接远程IP地址 */err = connect(g_sock, (struct sockaddr *)&atk_client_addr, sizeof(struct sockaddr));if (err == -1){printf("连接失败\r\n");g_sock = -1;closesocket(g_sock);myfree(SRAMIN, tbuf);vTaskDelay(10);goto sock_start;}printf("连接成功\r\n");lcd_show_string(5, 90, 200, 16, 16, "State:Connection Successful", BLUE);g_lwip_connect_state = 1;while (1){recv_data_len = recv(g_sock,g_lwip_demo_recvbuf,LWIP_DEMO_RX_BUFSIZE,0);if (recv_data_len <= 0 ){closesocket(g_sock);g_sock = -1;lcd_fill(5, 89, lcddev.width,110, WHITE);lcd_show_string(5, 90, 200, 16, 16, "State:Disconnect", BLUE);myfree(SRAMIN, tbuf);goto sock_start;}/* 接收的数据 */ lwip_err = xQueueSend(g_display_queue,&g_lwip_demo_recvbuf,0);if (lwip_err == errQUEUE_FULL){printf("队列Key_Queue已满,数据发送失败!\r\n");}vTaskDelay(10);}}
}/*** @brief 发送数据线程函数* @param pvParameters : 传入参数(未用到)* @retval 无*/
void lwip_send_thread(void *pvParameters)
{pvParameters = pvParameters;err_t err;while (1){while (1){if(((g_lwip_send_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA) && (g_lwip_connect_state == 1)) /* 有数据要发送 */{err = write(g_sock, g_lwip_demo_sendbuf, sizeof(g_lwip_demo_sendbuf));if (err < 0){break;}g_lwip_send_flag &= ~LWIP_SEND_DATA;}vTaskDelay(10);}closesocket(g_sock);}
}
Socket 编程 TCP 服务器流程
实现 TCP 服务器之前,用户必须先配置结构体 sockaddr_in 的成员变量才能实现TCPServer 连接,该配置步骤如下所示:
① sin_family 设置为 AF_INET 表示 IPv4 网络协议。
② sin_port 为设置端口号。
③ sin_addr.s_addr 设置本地 IP 地址。
④ 调用函数 Socket 创建 Socket 连接,注意:该函数的第二个参数 SOCK_STREAM 表
示 TCP 连接, SOCK_DGRAM 表示 UDP 连接。
⑤ 调用函数 bind 绑定本地 IP 地址和端口号。
⑥ 调用函数 listen 监听连接请求。
⑦ 调用函数 accept 监听连接。
⑧ 调用收发函数进行通讯。
上述的步骤就是 Socket 编程接口配置 TCPServer 的流程。
Socket 接口的 TCPServer 实验
之后应用不要什么goto代码
/* 设置远程IP地址 */
//#define DEST_IP_ADDR0 192
//#define DEST_IP_ADDR1 168
//#define DEST_IP_ADDR2 1
//#define DEST_IP_ADDR3 167/* 需要自己设置远程IP地址 */
//#define IP_ADDR "192.168.1.167"#define LWIP_DEMO_RX_BUFSIZE 200 /* 最大接收数据长度 */
#define LWIP_DEMO_PORT 8080 /* 连接的本地端口号 */
#define LWIP_SEND_THREAD_PRIO ( tskIDLE_PRIORITY + 3 ) /* 发送数据线程优先级 *//* 接收数据缓冲区 */
uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE];
/* 发送数据内容 */
uint8_t g_lwip_demo_sendbuf[] = "ALIENTEK DATA \r\n";/* 数据发送标志位 */
uint8_t g_lwip_send_flag;
int g_sock_conn; /* 请求的 socked */
int g_lwip_connect_state = 0;
static void lwip_send_thread(void *arg);
extern QueueHandle_t g_display_queue; /* 显示消息队列句柄 *//*** @brief 发送数据线程* @param 无* @retval 无*/
void lwip_data_send(void)
{sys_thread_new("lwip_send_thread", lwip_send_thread, NULL, 512, LWIP_SEND_THREAD_PRIO );
}/*** @brief lwip_demo实验入口* @param 无* @retval 无*/
void lwip_demo(void)
{struct sockaddr_in server_addr; /* 服务器地址 */struct sockaddr_in conn_addr; /* 连接地址 */socklen_t addr_len; /* 地址长度 */int err;int length;int sock_fd;char *tbuf;BaseType_t lwip_err;lwip_data_send(); /* 创建一个发送线程 */sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* 建立一个新的socket连接 */memset(&server_addr, 0, sizeof(server_addr)); /* 将服务器地址清空 */server_addr.sin_family = AF_INET; /* 地址家族 */server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* 注意转化为网络字节序 */server_addr.sin_port = htons(LWIP_DEMO_PORT); /* 使用SERVER_PORT指定为程序头设定的端口号 */tbuf = mymalloc(SRAMIN, 200); /* 申请内存 */sprintf((char *)tbuf, "Port:%d", LWIP_DEMO_PORT); /* 客户端端口号 */lcd_show_string(5, 150, 200, 16, 16, tbuf, BLUE);err = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); /* 建立绑定 */if (err < 0) /* 如果绑定失败则关闭套接字 */{closesocket(sock_fd); /* 关闭套接字 */myfree(SRAMIN, tbuf);}err = listen(sock_fd, 4); /* 监听连接请求 */if (err < 0) /* 如果监听失败则关闭套接字 */{closesocket(sock_fd); /* 关闭套接字 */}while(1){g_lwip_connect_state = 0;addr_len = sizeof(struct sockaddr_in); /* 将链接地址赋值给addr_len */g_sock_conn = accept(sock_fd, (struct sockaddr *)&conn_addr, &addr_len); /* 对监听到的请求进行连接,状态赋值给sock_conn */if (g_sock_conn < 0) /* 状态小于0代表连接故障,此时关闭套接字 */{closesocket(sock_fd);}else {lcd_show_string(5, 90, 200, 16, 16, "State:Connection Successful", BLUE);g_lwip_connect_state = 1;}while (1){memset(g_lwip_demo_recvbuf,0,LWIP_DEMO_RX_BUFSIZE);length = recv(g_sock_conn, (unsigned int *)g_lwip_demo_recvbuf, sizeof(g_lwip_demo_recvbuf), 0); /* 将收到的数据放到接收Buff */if (length <= 0){goto atk_exit;}// printf("%s",g_lwip_demo_recvbuf);lwip_err = xQueueSend(g_display_queue,&g_lwip_demo_recvbuf,0);if (lwip_err == errQUEUE_FULL){printf("队列Key_Queue已满,数据发送失败!\r\n");}}
atk_exit:if (g_sock_conn >= 0){ closesocket(g_sock_conn);g_sock_conn = -1;lcd_fill(5, 89, lcddev.width,110, WHITE);lcd_show_string(5, 90, 200, 16, 16, "State:Disconnect", BLUE);myfree(SRAMIN, tbuf);}}
}/*** @brief 发送数据线程函数* @param pvParameters : 传入参数(未用到)* @retval 无*/
static void lwip_send_thread(void *pvParameters)
{pvParameters = pvParameters;while (1){if(((g_lwip_send_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA) && (g_lwip_connect_state == 1)) /* 有数据要发送 */{send(g_sock_conn, g_lwip_demo_sendbuf, sizeof(g_lwip_demo_sendbuf), 0); /* 发送数据 */g_lwip_send_flag &= ~LWIP_SEND_DATA;}vTaskDelay(1);}
}
Socket 编程接口 TCP 服务器多连接实验
开发板作为服务器,让多个客户端连接
客户端信息的结构体
/* 客户端的信息 */
struct client_info
{int socket_num; /* socket号的数量 */struct sockaddr_in ip_addr; /* socket客户端的IP地址 */int sockaddr_len; /* socketaddr的长度 */
};
客户端的任务信息结构体
/* 客户端的任务信息 */
struct client_task_info
{UBaseType_t client_task_pro; /* 客户端任务优先级 */uint16_t client_task_stk; /* 客户端任务优先级 */TaskHandle_t * client_handler; /* 客户端任务控制块 */char *client_name; /* 客户端任务名称 */char *client_num; /* 客户端任务数量 */
};
socket信息结构体
/* socket信息 */
struct link_socjet_info
{int sock_listen; /* 监听 */int sock_connect; /* 连接 */struct sockaddr_in listen_addr; /* 监听地址 */struct sockaddr_in connect_addr;/* 连接地址 */
};
实现多链接的主要函数
/*** @brief 客户端的任务函数* @param pvParameters : 传入链接客户端的信息* @retval 无*/
void lwip_client_thread_entry(void *param)
{struct client_info* client = param;/* 某个客户端连接 */printf("Client[%d]%s:%d is connect server\r\n", client->socket_num, inet_ntoa(client->ip_addr.sin_addr),ntohs(client->ip_addr.sin_port));/* 向客户端发送连接成功信息 */send(client->socket_num, (const void* )send_data, strlen(send_data), 0);while (1){char str[2048];memset(str, 0, sizeof(str));int bytes = recv(client->socket_num, str, sizeof(str), 0);//客户端发来的数据/* 获取关闭连接的请求 */if (bytes <= 0){mem_free(client);closesocket(client->socket_num);break;//退出这个whiel 删除任务}printf("[%d]%s:%d=>%s...\r\n", client->socket_num, inet_ntoa(client->ip_addr.sin_addr),ntohs(client->ip_addr.sin_port), str);send((int )client->socket_num, (const void * )str, (size_t )strlen(str), 0);}printf("[%d]%s:%d is disconnect...\r\n", client->socket_num, inet_ntoa(client->ip_addr.sin_addr),ntohs(client->ip_addr.sin_port));vTaskDelete(NULL); /* 删除该任务 */
}/*** @brief lwip_demo实验入口* @param 无* @retval 无*/
void lwip_demo(void)
{struct client_info *client_fo;struct client_task_info *client_task_fo;struct link_socjet_info *socket_link_info;int sin_size = sizeof(struct sockaddr_in);char client_name[10] = "cli";char client_num[10];/* socket连接结构体申请内存 */socket_link_info = mem_malloc(sizeof(struct link_socjet_info));/* 设置客户端任务信息 */client_task_fo = mem_malloc(sizeof(struct client_task_info));client_task_fo->client_handler = NULL;client_task_fo->client_task_pro = 5;client_task_fo->client_task_stk = 512;/* 创建socket连接 */if ((socket_link_info->sock_listen = socket(AF_INET, SOCK_STREAM, 0)) == -1){printf("Socket error\r\n");return;}/* 初始化连接的服务端地址 *///记录每个客户端的ip地址socket_link_info->listen_addr.sin_family = AF_INET;socket_link_info->listen_addr.sin_port = htons(8088);socket_link_info->listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);memset(&(socket_link_info->listen_addr.sin_zero), 0, sizeof(socket_link_info->listen_addr.sin_zero));/* 绑定socket和连接的服务端地址信息 */if (bind(socket_link_info->sock_listen, (struct sockaddr * )&socket_link_info->listen_addr, sizeof(struct sockaddr)) < 0){printf("Bind fail!\r\n");goto __exit;}/* 监听客户端的数量 */listen(socket_link_info->sock_listen, 4);printf("begin listing...\r\n");while (1){/* 请求客户端连接 */socket_link_info->sock_connect = accept(socket_link_info->sock_listen, (struct sockaddr* )&socket_link_info->connect_addr, (socklen_t* )&sin_size);if (socket_link_info->sock_connect == -1){printf("no socket,waitting others socket disconnect.\r\n");continue;}lwip_itoa((char *)socket_link_info->sock_connect, (size_t)client_num, 10);strcat(client_name, client_num);client_task_fo->client_name = client_name;client_task_fo->client_num = client_num;/* 初始化连接客户端信息 */client_fo = mem_malloc(sizeof(struct client_info));client_fo->socket_num = socket_link_info->sock_connect;memcpy(&client_fo->ip_addr, &socket_link_info->connect_addr, sizeof(struct sockaddr_in));client_fo->sockaddr_len = sin_size;/* 创建连接的客户端任务 */xTaskCreate((TaskFunction_t )lwip_client_thread_entry,(const char * )client_task_fo->client_name,(uint16_t )client_task_fo->client_task_stk,(void * )(void*) client_fo,(UBaseType_t )client_task_fo->client_task_pro ++ ,(TaskHandle_t * )&client_task_fo->client_handler);if (client_task_fo->client_handler == NULL){printf("no memery for thread %s startup failed!\r\n",client_task_fo->client_name);mem_free(client_fo);continue;}else{printf("thread %s success!\r\n", client_task_fo->client_name);}}__exit: printf("listener failed\r\n");/* 关闭这个socket */closesocket(socket_link_info->sock_listen);vTaskDelete(NULL); /* 删除本任务 */
}
相关文章:
LWIP的Socket接口
Socket接口简介 类似于文件操作的一种网络连接接口,通常将其称之为“套接字”。lwIP的Socket接口兼容BSD Socket接口,但只实现完整Socket的部分功能 netconn是对RAW的封装 Socket是对netconn的封装 SOCKET结构体 struct sockaddr { u8_t sa_len; /* 长…...
【Linux笔记】——线程同步条件变量与生产者消费者模型的实现
🔥个人主页🔥:孤寂大仙V 🌈收录专栏🌈:Linux 🌹往期回顾🌹:【Linux笔记】——线程互斥与互斥锁的封装 🔖流水不争,争的是滔滔不息 一、线程同步的…...
Popeye
概览与定位 Popeye 是由 derailed 团队开源的 Kubernetes 集群资源 “Sanitizer”,它以只读方式扫描集群内的各种资源(如 Pod、Service、Ingress、PVC、RBAC 等),并基于社区最佳实践给出问题等级及修复建议,覆盖配置误…...
ES(ES2023/ES14)最新更新内容,及如何减少内耗
截至2023年10月,JavaScript(ECMAScript)的最新版本是 ES2023(ES14)。 ES2023 引入了许多新特性,如findLast、toSorted等,同时优化了性能。通过减少全局变量、避免内存泄漏、优化循环、减少DOM操作、使用Web Workers、懒加载、缓存、高效数据结构和代码压缩,可以显著降低…...
电子数据取证(数字取证)技术全面指南:从基础到实践
为了后续查阅方便,推荐工具先放到前面 推荐工具 数字取证基础工具 综合取证平台 工具名称类型主要功能适用场景EnCase Forensic商业全面的证据获取和分析、强大的搜索能力法律诉讼、企业调查FTK (Forensic Toolkit)商业高性能处理和索引、集成内存分析大规模数据处…...
【通用智能体】Serper API 详解:搜索引擎数据获取的核心工具
Serper API 详解:搜索引擎数据获取的核心工具 一、Serper API 的定义与核心功能二、技术架构与核心优势2.1 技术实现原理2.2 对比传统方案的突破性优势 三、典型应用场景与代码示例3.1 SEO 监控系统3.2 竞品广告分析 四、使用成本与配额策略五、开发者注意事项六、替…...
基于 STM32 的手持式安检金属探测器设计与实现
一、硬件设计:芯片与功能模块选型及接线 1. 主控芯片选型 芯片型号:STM32F103C8T6 核心优势: 32 位 Cortex-M3 内核,主频 72MHz,满足实时数据处理需求64KB Flash+20KB SRAM,支持程序存储与数据缓存丰富外设:2 路 USART、2 路 SPI、1 路 I2C、12 位 ADC,适配多模块通信…...
虚幻引擎5-Unreal Engine笔记之Default Pawn与GamMode、Camera的关系
虚幻引擎5-Unreal Engine笔记之Default Pawn与GamMode、Camera的关系 code review! 文章目录 虚幻引擎5-Unreal Engine笔记之Default Pawn与GamMode、Camera的关系1.Default Pawn与Camera的关系1.1. Default Pawn 是什么?1.2. Default Pawn 的主要组件1.3. Default…...
Spring源码主线全链路拆解:从启动到关闭的完整生命周期
Spring源码主线全链路拆解:从启动到关闭的完整生命周期 一文看懂 Spring 框架从启动到销毁的主线流程,结合原理、源码路径与伪代码三位一体,系统学习 Spring 底层机制。 1. 启动入口与环境准备 原理说明 Spring Boot 应用入口是标准 Java 应…...
飞帆控件:可配置post/get接口
先上链接: post_get_ithttps://fvi.cn/796看一下这个控件的配置: 当 url 有某个 get 参数时,例如某些接口回传的参数。使用这个接口会发生这些: 如果检测到 url 中有该 url 参数则继续执行选择是否从 url 中删除该参数将这个参数…...
Android 自定义悬浮拖动吸附按钮
一个悬浮的拨打电话按钮,使用CardViewImageView可能会出现适配问题,也就是图片显示不全,出现这种问题,就直接替换控件了,因为上述的组合控件没有FloatingActionButton使用方便,还可以有拖动和吸附效果不是更…...
Spring AI Alibaba集成阿里云百炼大模型应用
文章目录 1.准备工作2.引入maven依赖3.application.yml4.调用4.1.非流式调用4.2.流式调用 阿里云百炼推出的智能体应用、工作流应用和智能体编排应用,有效解决了大模型在处理私有领域问题、获取最新信息、遵循固定流程以及自动规划复杂项目等方面的局限,…...
UI-TARS本地部署
UI-TARS本地部署 UI-TARS本地部署 UI-TARS 论文(arXiv) UI-TARS 官方仓库:包含部署指南、模型下载链接及示例代码。 UI-TARS-Desktop 客户端:支持本地桌面应用的交互控制。 模型部署框架:vLLM本地部署 1.下载项目…...
如何利用内网穿透实现Cursor对私有化部署大模型的跨网络访问实践
文章目录 前言1.安装Ollama2.QwQ-32B模型安装与运行3.Cursor安装与配置4. 简单使用测试5. 调用本地大模型6. 安装内网穿透7. 配置固定公网地址总结 前言 本文主要介绍如何在Windows环境下,使用Cursor接入Ollama启动本地部署的千问qwq-32b大模型实现辅助编程&#x…...
Linux的进程概念
目录 1、冯诺依曼体系结构 2、操作系统(Operating System) 2.1 基本概念 编辑 2.2 目的 3、Linux的进程 3.1 基本概念 3.1.1 PCB 3.1.2 struct task_struct 3.1.3 进程的定义 3.2 基本操作 3.2.1 查看进程 3.2.2 初识fork 3.3 进程状态 3.3.1 操作系统的进程状…...
(10)python开发经验
文章目录 1 cp35 cp36什么意思2 找不到pip3 subprocess编码错误4 导出依赖文件包含路径5 使用自己编译的python并且pyinstall打包程序 更多精彩内容👉内容导航 👈👉Qt开发 👈👉python开发 👈 1 cp35 cp36什…...
什么是时间戳?怎么获取?有什么用
时间戳的定义 时间戳(Timestamp)是指记录某个事件发生的具体时间点,通常以特定的格式表示。它可以精确到秒、毫秒甚至更小的单位,用于标识某个时刻在时间轴上的位置。 获取时间戳的方法 在不同的编程语言中,获取时间…...
Zookeeper 入门(二)
4. Zookeeper 的 ACL 权限控制( Access Control List ) Zookeeper 的ACL 权限控制,可以控制节点的读写操作,保证数据的安全性,Zookeeper ACL 权 限设置分为 3 部分组成,分别是:权限模式(Scheme)、授权对象(…...
[创业之路-361]:企业战略管理案例分析-2-战略制定-使命、愿景、价值观的失败案例
一、失败案例 1、使命方面的失败案例 真功夫创业者内乱:真功夫在创业过程中,由于股权结构不合理,共同创始人及公司大股东潘宇海与实际控制人、董事长蔡达标产生管理权矛盾。双方在公司发展方向、管理改革等方面无法达成一致,导致…...
dijkstra算法加训上 之 分层图最短路
来几个分层图的题练习下哈 P4568 [JLOI2011] 飞行路线 P4568 [JLOI2011] 飞行路线 - 洛谷https://www.luogu.com.cn/problem/P4568 题目描述 Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 n 个城市设有业务,设这…...
赋予AI更强的“思考”能力
刚刚!北大校友、OpenAI前安全副总裁Lilian Weng最新博客来了:Why We Think 原文链接:Why We Think by Lilian Weng 这篇文章关注:如何让AI不仅仅是“知道”答案,更能“理解”问题并推导出答案。通过赋予AI更强的“思…...
微服务项目->在线oj系统(Java版 - 1)
相信自己,终会成功 目录 C/S架构与B/S架构 C/S架构(Client/Server,客户端/服务器架构) 特点: 优点: 缺点: 典型应用: B/S架构(Browser/Server,浏览器/服务器架构&a…...
【深度学习】使用块的网络(VGG)
虽然 AlexNet 证明深层神经网络卓有成效,但它没有提供一个通用的模板来指导后续的研究人员设计新的网络。 也就是说尽管我知道了更深更大的网络更有效,但是不清楚怎么让它更深更大,从而起到一个更好的效果。 于是,研究人员开始从单…...
Python数据可视化 - Pyecharts绘图示例
文章目录 一、Pyecharts简介及安装1. Pyecharts简介2. 安装Pyecharts 二、准备数据三、饼图示例1. 初始化选项配置2. 饼图相关设置3. 全局配置项3.1 标题配置项3.2 图例配置项3.3 提示框配置项3.4 工具箱配置项3.5 视觉映射配置项 4. 系列配置项4.1 标签选项配置4.2 图元样式配…...
Day29
复习日 知识点回顾 类的装饰器装饰器思想的进一步理解:外部修改、动态类方法的定义:内部定义和外部定义 作业:复习类和函数的知识点,写下自己过去29天的学习心得,如对函数和类的理解,对python这门工具的理…...
Python列表全面解析:从入门到精通
文章目录 Python列表全面解析:从入门到精通一、列表基础1. 什么是列表?2. 列表特性总结表 二、列表的基本操作(基础)1. 访问元素2. 修改列表 三、列表的常用方法(基础)1. 添加元素的方法2. 删除元素的方法3. 查找和统计方法4. 排序和反转 四、列表的高级…...
Nacos数据写入流程
在 3 节点的 Nacos 集群中,数据写入流程和主节点(Leader)的角色基于 Nacos 的分布式一致性协议(通常使用 Raft 协议)来实现。以下以 Markdown 格式详细说明 3 节点 Nacos 集群的数据写入流程以及主节点的角色和确定方式…...
《P4551 最长异或路径》
题目描述 给定一棵 n 个点的带权树,结点下标从 1 开始到 n。寻找树中找两个结点,求最长的异或路径。 异或路径指的是指两个结点之间唯一路径上的所有边权的异或。 输入格式 第一行一个整数 n,表示点数。 接下来 n−1 行,给出…...
Ansible模块——文件属性查看,文件或目录创建和属性修改
ansible.builtin.stat 可以查看文件信息。 选项 类型 默认值 描述 pathstrnull 要检查的文件或目录的完整路径(必需)。 followboolfalse 如果是符号链接,是否跟随到目标路径上获取其状态。 get_attributesbooltrue 是否返回扩展属性&#…...
【图像生成大模型】Wan2.1:下一代开源大规模视频生成模型
Wan2.1:下一代开源大规模视频生成模型 引言Wan2.1 项目概述核心技术1. 3D 变分自编码器(Wan-VAE)2. 视频扩散 Transformer(Video Diffusion DiT)3. 数据处理与清洗 项目运行方式与执行步骤1. 环境准备2. 安装依赖3. 模…...
AGI大模型(25):LangChain提示词模版
我们也可以创建prompt template, 并引入一些变量到prompt template中,这样在应用的时候更加灵活。 1 代码实现 # 我们也可以创建prompt template, 并引入一些变量到prompt template中,这样在应用的时候更加灵活 from langchain_core.prompts import ChatPromptTemplate from…...
mybatis中的resultMap的association及collectio的使用
目录 1.reusltmap的说明 2.association的使用 3.collection的使用 4.总结 1.reusltmap的说明 resultmap定义了数据库的结果映射到java对象的规则,resultmap包含4个属性: id: ResultMap 的唯一标识 type: 映射到的 Java 类型(全限定类名或…...
静态网站部署:如何通过GitHub免费部署一个静态网站
GitHub提供的免费静态网站托管服务可以无需担心昂贵的服务器费用和复杂的设置步骤,本篇文章中将一步步解如何通过GitHub免费部署一个静态网站,帮助大家将创意和作品快速展现给世界。 目录 了解基础情况 创建基础站点 在线调试站点 前端项目部署 部署…...
Android 手写签名功能详解:从原理到实践
Android 手写签名功能详解 1. 引言2. 手写签名核心实现:SignatureView 类3. 交互层实现:MainActivity 类4. 布局与配置5. 性能优化与扩展方向 1. 引言 在电子政务、金融服务等移动应用场景中,手写签名功能已成为提升用户体验与业务合规性的关…...
【iOS(swift)笔记-9】WKWebView无法访问网络
对于iOS 在info中添加App Transport Security Settings,然后在App Transport Security Settings里添加Allow Arbitrary Loadstrue 对于macOS 除了上面的操作,还需在项目信息的App Sandbox里有个Network打钩选项...
Adapter适配器模式
Adapter适配器模式是一种结构设计模式,用于解决接口不兼容的问题,通过适配器类,可以将一个类的接口转换为客户渴望的另一个接口,从而使原来无法协作的对象能够一起工作。 角色和职责: 目标接口(Target&…...
七、xlib窗口渲染
文章目录 1.渲染图片2.双缓冲3.混合图片4.渐变窗口 1.渲染图片 在上篇文章中的最后,我们使用libpng加载了一个png图片,并显示到窗口上,但是我们可以看到显示到窗口的图片周边有黑色的背景。原因是在我测试的操作系统下使用xlib创建的窗口默认…...
python中http.cookiejar和http.cookie的区别
在Python中,http.cookiejar和http.cookie(通常指http.cookies模块)是两个不同的模块,它们的主要区别如下: 1. 功能定位 http.cookiejar 用于管理HTTP客户端的Cookie,提供自动化的Cookie存储、发送和接收功…...
架构设计模式:构建健壮、可扩展的 Serverless 应用
架构设计模式:构建健壮、可扩展的 Serverless 应用 到目前为止,我们已经掌握了 Serverless 的基本概念,了解了 FaaS 和 BaaS 如何协同工作,学会了使用框架进行开发部署,并知道了如何监控和排查问题。现在,是时候从“能用”向“好用”迈进了。 仅仅将代码部署到 Lambda 函…...
2- PyTorch
文章目录 1. Overview2. 线性模型 1. Overview 在人的智能中,最经常做的事情是推理和预测,在机器学习中也是如此。我们在以往的算法课中,所接触的穷举、贪心、分治和动规等算法都是由人设计的,而在机器学习中,算法是由…...
MinIO:从入门到精通,解锁云原生存储的奥秘
一、引言:为什么 MinIO 正在重塑存储世界? 在云计算和大数据时代,传统存储系统面临扩展性差、成本高、兼容性不足等挑战。MinIO 凭借其 S3 兼容性、分布式架构、高性能存储 等特性,成为企业构建现代化存储基础设施的首选。 本文…...
【LeetCode 热题100】739:每日温度(详细解析)(Go语言版)
🌡️ LeetCode 739:每日温度(详解 单调栈 多种思路对比) 📌 题目描述 给定一个整数数组 temperatures,表示每天的温度,返回一个数组 answer,其中 answer[i] 是指在第 i 天之后&am…...
Linux学习笔记|GCC编译指令基础|静动态库|makefile
一、GCC 编译指令基础 基本编译命令 gcc -o code code.c和gcc code.c -o code:这两条命令功能相同,都是使用 GCC 编译器将code.c源文件编译成名为code的可执行文件。-o选项用于指定输出文件名,选项位置在源文件前后不影响最终结果。 编译过程…...
【LeetCode 热题100】17:电话号码的字母组合(详细解析)(Go语言版)
☎️ LeetCode 17. 电话号码的字母组合(回溯 DFS 详解) 📌 题目描述 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按任意顺序返回。 数字到字母的映射如下(与电话按键相同)…...
C++学习:六个月从基础到就业——C++17:std::optional/variant/any
C学习:六个月从基础到就业——C17:std::optional/variant/any 本文是我C学习之旅系列的第四十七篇技术文章,也是第三阶段"现代C特性"的第九篇,主要介绍C17引入的三个重要工具类型:std::optional、std::varia…...
Go语言中函数 vs 方法
函数(Function):不属于任何类型,是全局可调用的。 方法(Method):绑定在某个类型上的函数,调用时依赖于这个类型的值或指针。 一、函数(Function) func 函数…...
代码随想录算法训练营第六十五天| 图论10—卡码网94. 城市间货物运输 I,95. 城市间货物运输 II
被学校课程轰炸了一周,回过头发现训练营已经要结束了,抓紧时间补完。不过算法这边也很难,感觉每天都是勉强理解在干什么的状态。 94. 城市间货物运输 I 94. 城市间货物运输 I SPFA算法,也是Bellman_ford 队列优化算法 优化原理…...
TDengine 在新能源领域的价值
能源数据的定义 能源数据是指记录和描述能源产业各个方面的信息,包括能源生产、供应、消费、储备、价格、排放以及相关政策和技术的数据。这些数据可以通过各种途径收集和整理,如能源企业的统计报表、政府部门的调查和监测、国际组织的发布数据等。 能…...
浅谈Frida 检测与绕过
目录 ptrace 占位与进程名检测端口检测与 D-Bus 协议通信扫描 /proc 目录(maps、task、fd)定位 so 中的 SVC syscall内存动态释放代码 1. ptrace 占位与进程名检测 检测方式 遍历运行进程列表,检查是否存在 frida-server 或相关进程名&…...
WaterStamp —— 一个实用的网页水印生成器开发记
我正在参加CodeBuddy「首席试玩官」内容创作大赛,本文所使用的 CodeBuddy 免费下载链接:腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 最近,我和 CodeBuddy 一起完成了一个名为 WaterStamp 的网页水印生成器项目。这个小工具主要用于给…...