### LwIP Socket 的使用方法及示例代码
LwIP 是一种轻量级 TCP/IP 协议栈,广泛应用于嵌入式系统中。它的 Socket 接口设计类似于 BSD Socket,但在功能上有所简化[^1]。以下是关于 LwIP Socket 的基本用法及其典型应用场景。
#### 1. 创建套接字 (Socket)
创建一个套接字用于通信,可以通过 `lwip_socket` 函数完成:
```c
int sockfd = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0) {
// 错误处理逻辑
}
```
上述代码片段表示创建了一个面向流的 TCP 套接字。参数解释如下:
- `AF_INET`: 表明使用的地址族为 IPv4。
- `SOCK_STREAM`: 指定套接字类型为流式传输(TCP)。
- `IPPROTO_TCP`: 明确指定协议为 TCP。
此部分对应于基础的 Socket 初始化过程[^3]。
---
#### 2. 绑定本地地址 (Bind)
绑定本地 IP 地址和端口号到已创建的套接字上:
```c
struct sockaddr_in addr;
addr.sin_family = AF_INET; // 设置地址家族
addr.sin_port = htons(PORT); // 主机字节序转换为目标字节序
addr.sin_addr.s_addr = INADDR_ANY; // 自动获取本机 IP 地址
if (lwip_bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
// 处理错误情况
}
```
这里需要注意的是,`INADDR_ANY` 可以让操作系统自动选择合适的网络接口进行绑定[^2]。
---
#### 3. 进行监听 (Listen)
对于服务端程序来说,需要调用 `listen` 来等待客户端连接请求:
```c
if (lwip_listen(sockfd, SOMAXCONN) != 0) {
// 监听失败后的处理逻辑
}
```
其中 `SOMAXCONN` 定义了最大排队长度,具体数值由平台决定。
---
#### 4. 接受新连接 (Accept)
当有新的客户端尝试连接时,服务器会通过 `accept` 获取一个新的文件描述符来管理此次对话:
```c
struct sockaddr_in clientAddr;
socklen_t addrlen = sizeof(clientAddr);
int new_sockfd = lwip_accept(sockfd, (struct sockaddr*)&clientAddr, &addrlen);
if (new_sockfd < 0) {
// 新连接接受失败
} else {
printf("New connection from %s:%d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
}
```
这段代码展示了如何捕获并打印客户端的信息[^5]。
---
#### 5. 数据交互 (Read/Write)
无论是作为客户端还是服务端,都可以利用读写函数来进行数据交换:
```c
// 发送数据给对方
char *msg = "Hello LWIP!";
ssize_t sent_bytes = lwip_send(new_sockfd, msg, strlen(msg), 0);
if (sent_bytes <= 0) {
// 发送失败
}
// 接收来自对方的数据
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
ssize_t recv_bytes = lwip_recv(new_sockfd, buffer, sizeof(buffer)-1, 0);
if (recv_bytes > 0) {
printf("Received message: %s\n", buffer);
}
```
这些操作分别代表向远端发送消息以及从远端接收消息的功能实现[^4]。
---
#### 6. 关闭连接 (Close)
最后一步是关闭不再需要的套接字资源:
```c
lwip_close(new_sockfd);
```
释放掉所有的内存和其他关联资源是非常重要的实践之一。
---
### 示例代码:完整的 UDP Server 实现
下面给出一段基于 STM32 平台上的简单 UDP 服务器实例代码:
```c
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#define PORT 8080
void udp_server_task(void) {
int sockfd;
struct sockaddr_in addr;
// 创建 UDP 套接字
sockfd = lwip_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) {
printf("Failed to create socket.\n");
return;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
// 将套接字与特定端口绑定
if (lwip_bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
printf("Binding failed.\n");
lwip_close(sockfd);
return;
}
char buffer[1024];
while (1) {
struct sockaddr_in cli_addr;
socklen_t len = sizeof(cli_addr);
ssize_t n = lwip_recvfrom(sockfd, buffer, sizeof(buffer)-1, 0,
(struct sockaddr*)&cli_addr, &len);
if (n > 0) {
buffer[n] = '\0';
printf("Message received: %s\n", buffer);
// 向客户端回传相同的消息
lwip_sendto(sockfd, buffer, n, 0, (struct sockaddr*)&cli_addr, len);
} else {
printf("Error receiving data.\n");
}
}
lwip_close(sockfd);
}
```
---
### 总结
通过对 LwIP 中 Socket API 的学习可以看出,尽管它相较于标准 BSD Socket 功能有限制,但对于大多数嵌入式项目而言已经足够满足需求。同时掌握了以上提到的关键步骤之后就可以轻松构建自己的网络应用程序了。