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); /* 删除本任务 */
}