先分析出LWIP大致结构:
lwip_init_task
netif_add(lpc17xx_netif, &ipaddr, &netmask, &gw, NULL, ethernetif_init, tcpip_input)
init(netif) //就是ethernetif_init
low_level_init(netif);
ethernetif_input
CoPendSem(semEthRx, 0) //等待semEthRx,由ENET_IRQHandler的isr_PostSem(semEthRx)发送,说明缓存区接收完成
p = low_level_input(netif); //从MAC缓存区取数据
len = StartReadFrame();
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
CopyFromFrame_EMAC(q->payload, q->len);
EndReadFrame();
netif->input(p, netif) //就是tcpip_input,通过netif_add设置的
sys_mbox_trypost(mbox, msg) //发送msg到——>tcpip_thread线程
----------------------------
----------------------------
sys_mbox_fetch(mbox, (void *)&msg); //tcpip_thread线程等待msg
ip_input(struct pbuf *p, struct netif *inp)
tcp_input(p, inp);或udp_input(p, inp);协议层处理
Lwip的socket编程用到的底层函数:
底层链接初始化:low_level_init
底层数据发送: low_level_output //发送数据时最后会调用此netif->output,也就是etharp_output,而etharp_output会调用netif->linkoutput,也就是low_level_output
底层数据接收: low_level_input //在ethernetif_input里面等待底层有数据就调用此函数接收数据
下面介绍基于LwIP socket的UDP服务器的编写步骤,并且比较客户端和服务器之间的区别,UDP服务器编写步骤如下所示:
1、创建一个基于数据包的socket
2、设置本地服务器地址及端口号
3、将本地服务器地址与创建好的socket进行绑定
4、接收绑定好的socket的消息
经过了以上四个步骤以后,一个简单的UDP服务器就搭建好了,其他客户端发来的数据就会被服务器接收下来,其源代码如下所示
/*
* receive UDP packet from PC
* local server IP:192.168.0.80 or INADDR_ANY
*/
#include
#include
#include
#define PORT 50000
static RAW_U32 udp_msg[100];
static void udp_server_thread(void *p_arg)
{
struct sockaddr_in server_addr;
int sock_fd; /* server socked */
int err;
int count = 0;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd == -1) {
Uart_Printf("failed to create sock_fd!\n");
RAW_ASSERT(0);
}
raw_memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
//server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_addr.s_addr = inet_addr("192.168.0.80");
server_addr.sin_port = htons(PORT);
err = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err == -1) {
RAW_ASSERT(0);
}
while (1) {
raw_memset(udp_msg, 0, sizeof(udp_msg));
err = recv(sock_fd, (RAW_U8 *)udp_msg, sizeof(udp_msg), 0);
Uart_Printf("receive msg: %s", udp_msg);
count++;
}
}
void udp_server_init(void)
{
sys_thread_new("udp_server_thread", udp_server_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO - 1);
}
UDP客户端编程的基本步骤及测试代码。
1、创建一个数据包类型socket
2、绑定socket IP地址及端口号
3、往绑定好的socket发送数据
通过以上步骤,即可以搭建好一个基于socket 的客户端,代码如下所示
/*
* send UDP packet to PC
* remote IP: 192.168.0.100 or INADDR_BROADCAST
*/
#include
#include
#include
#define PORT 50000
char udp_msg[] = "this is a UDP test package";
static void udp_client_thread(void *p_arg)
{
struct sockaddr_in client_addr;
int sock_fd; /* client socked */
int err;
int count = 0;
err = err;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd == -1) {
Uart_Printf("failed to create sock_fd!\n");
RAW_ASSERT(0);
}
raw_memset(&client_addr, 0, sizeof(client_addr));
client_addr.sin_family = AF_INET;
//client_addr.sin_addr.s_addr = INADDR_BROADCAST;
client_addr.sin_addr.s_addr = inet_addr("192.168.0.100");
client_addr.sin_port = htons(PORT);
while (1) {
err = sendto(sock_fd, (char *)udp_msg, sizeof(udp_msg), 0, \
(struct sockaddr *)&client_addr, sizeof(client_addr));
count++;
Uart_Printf("-------------------send count %d-------------------\n", count);
raw_sleep(100);
}
}
void udp_client_init(void)
{
sys_thread_new("udp_client_thread", udp_client_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO - 1);
}
下面介绍一下基于连接的TCP的编写方法,首先介绍TCP客户端编写流程,其步骤如下所示
1、创建一个基于流的socket
2、设置服务器IP地址和端口号
3、连接设置好以后的socket和服务器地址
4、连接好以后就发送/接收数据
从以上几个步骤可以发现TCP和UDP的最主要区别就是TCP有一个建立连接的过程,而UDP是没有的,其源代码如下所示
#include
#include
#include
#define PORT 50000
char msg[] = "hello, you are connected!\n";
static void tcp_client_thread(void * arg)
{
struct sockaddr_in server_addr;
int sock_fd;
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd == -1) {
Uart_Printf("failed to create sock_fd!\n");
RAW_ASSERT(0);
}
raw_memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("192.168.0.100");
server_addr.sin_port = htons(PORT);
connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
while (1) {
send(sock_fd, (char *)msg, sizeof(msg), 0);
raw_sleep(100);
}
// close(sock_fd);
}
void tcp_client_init(void)
{
sys_thread_new("tcp_client", tcp_client_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
}
下面主要介绍TCP服务器的编写步骤方法,其流程如下所示
1、创建一个基于流的socket
2、设置本地服务器IP地址及端口号
3、绑定创建的socket和本地IP地址及端口信息
4、监听该socket(listen)
5、接受该socket(accept)
6、发送、接收数据
经过了以上几个步骤以后,一个简单的服务器就创建起来了,TCP服务器主要是多了监听和接受两个步骤,少了连接步骤,其源代码如下所示:
#include
#include
#include
#define PORT 50000
RAW_U8 data_buffer[100];
static void tcp_server_thread(void *p_arg)
{
struct sockaddr_in server_addr;
struct sockaddr_in conn_addr;
int sock_fd; /* server socked */
int sock_conn; /* request socked */
socklen_t addr_len;
int err;
int length;
int count = 0;
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd == -1) {
Uart_Printf("failed to create sock_fd!\n");
RAW_ASSERT(0);
}
raw_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(PORT);
err = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err < 0) {
RAW_ASSERT(0);
}
err = listen(sock_fd, 1);
if (err < 0) {
RAW_ASSERT(0);
}
addr_len = sizeof(struct sockaddr_in);
Uart_Printf("before accept!\n");
sock_conn = accept(sock_fd, (struct sockaddr *)&conn_addr, &addr_len);
Uart_Printf("after accept!\n");
while (1) {
raw_memset(data_buffer, 0, sizeof(data_buffer));
length = recv(sock_conn, (unsigned int *)data_buffer, 20, 0);
Uart_Printf("length received %d\n", length);
Uart_Printf("received string: %s\n", data_buffer);
Uart_Printf("received count: %d\n", count);
send(sock_conn, "good", 5, 0);
}
}
void tcp_server_init(void)
{
sys_thread_new("tcp_server_thread", tcp_server_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO - 1);
}