文章概述
对于IWIP协议的介绍,属于一个笔记的一个功能 主要的一个功能是方便自己的理解和查阅,所作的介绍或者解释都是自己在网络上或者自己遇到的一些情况的一个笔记!
什么是IWIP?
简介
我们百度一下IWIP:百度给的解释如下wIP是Light Weight (轻型)IP协议,有无操作系统的支持都可以运行。LwIP实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用,它只需十几KB的RAM和40K左右的ROM就可以运行,这使LwIP协议栈适合在低端的嵌入式系统中使用。lwIP协议栈主要关注的是怎么样减少内存的使用和代码的大小,这样就可以让lwIP适用于资源有限的小型平台例如嵌入式系统。为了简化处理过程和内存要求,lwIP对API进行了裁减,可以不需要复制一些数据。
重点
适合嵌入式 需要的RAM/ROM都是比较小的,由于TCP/IP的特性 ,目前应用最广的是还是跑操作系统的,比如UCOS,FreeRTOS等系统,因为使用中需要用到信号量进行通信
模式
lwip提供三种API:(1):RAW API ,(2):lwip API,(3)BSD API
Socket应用
Socket:英文原意是“孔”或者“插座”的意思,在网络编程中,通常将其称之为:“套接字”,当前网络中的主流程序设计都是使用Socket 进行编程的,因为它简单易用,更是一个标准,能在不同平台很方便移植。
LWIP 中的socket
服务端(Service)
建立步骤
1:创建一个Socket(套接字)’socket()’
举例 :s_create = socket(AF_INET, 1, 0);
'Socket()'其实是调用的'int lwip_socket(int domain, int type, int protocol)'
参数’domain’:
表示该套接字使用的协议簇,对于'TCP/IP 协议'来说,该值始终为`AF_INET`.
参数’type’:
type: 指定了套接字使用的服务类型,可能的类型有3 种
‘1. SOCK_STREAM’:提供可靠的(即能保证数据正确传送到对方)面向连接的Socket 服务,多用于资料(如文件)传输,如TCP 协议。
‘2. SOCK_DGRAM’:是提供无保障的面向消息的Socket 服务,主要用于在网络上发广播信息,如UDP 协议,提供无连接不可靠的数据报交付服务。
‘3. SOCK_RAW’:表示原始套接字,它允许应用程序访问网络层的原始数据包,这个套接字用得比较少,暂时不用理会它。
参数’protocol’:
protocol:指定了套接字使用的协议,在'IPv4' 中,只有'TCP协议提供SOCK_STREAM这种可靠的服务',只有'UDP 协议供SOCK_DGRAM服务',对于这两种协议,'protocol 的值均为0'。当申请套接字成功的时候,该函数返回一个'int 类型的值',也是Socket 描述符,用户通过这个值可以索引到一个Socket 连接结构——'lwip_sock',当申请套接字'失败'时,'该函数返回-1'
2: bind() 将IP地址等一些网络信息绑定到创建的套子上
举例:socket_check = bind(s_create, (struct sockaddr *)&address, sizeof(address));
' bind() ':实际调用的是下面的这个函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
{
struct lwip_sock * sock;
ip_addr_t local_addr;
u16_t local_port;
err_t err;
const struct sockaddr_in *name_in;
sock = get_socket(s);
if (!sock) {
return -1;
}
/* check size, familiy and alignment of 'name' */
LWIP_ERROR(
"lwip_bind: invalid address",
((namelen == sizeof(struct sockaddr_in)) && ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)),
sock_set_errno(sock, err_to_errno(ERR_ARG));
return -1;);
name_in = (const struct sockaddr_in *)(void *)name;
......代码太多会影响阅读 有兴趣可以到这个函数中仔细观察
参数 s :
参数s: 实际就是我们之前创建的socket(套接字):' s_create '
参数 *name :
*name:是指向sockaddr结构体的一个指针,我们看一下这个结构体的类容如下:
1
2
3
4
5
struct sockaddr {
u8_t sa_len; //长度
u8_t sa_family; //协议簇:比如上面使用的AF_INET(TCP/IP)
char sa_data[14]; //连续的14个字节的信息 包括IP地址,端口信息等
};
其中' char sa_data[14]; '个人觉得是需要填写的数据格式和数量对我们不是很友好 所以可以看到上面的调用函数' lwip_bind() '中定义了一个结构体指针*name_in指向结构体:sockaddr_in
1
2
3
4
5
6
7
struct sockaddr_in {
u8_t sin_len;
u8_t sin_family;
u16_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
在这个结构体中 ,官方将刚才的14个字节帮分了一下类其中sin_port 字段是我们需要填写的端口号信息,sin_addr 字段是我们需要填写的IP 地址信息,剩下sin_zero区域的8 字节保留未用.
完整一些的使用例程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int sock = -1;
struct sockaddr_in server_addr;
sock = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(IP_ADDR);
memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
;
}
参数 namelen:
namelen:指定了之前name结构体的长度
3 listen():服务端使用, 让服务端进入监听状态 等待连接端的连接 LwIP 中可以接收多个客户端的连接,因此参数backlog 指定了请求队列的大小
举例:listen(s_create, 5);
对于' listen() ' 调用的是:
' lwip_listen(int s, int backlog) '函数
参数 s : 创建的socket套字
参数 backlog:
' backlog ' 在函数原型中的使用是:
' limit the "backlog" parameter to fit in an u8_t '
官方给的注释是: ' limit the "backlog" parameter to fit in an u8_t '//限制 backlog 以适合 u8
那么我们可以大概的计算一下 限制的请求队列最大个数是 ' 255 '
4 accept()函数 服务端使用, :主要作用是等待着远端主机的连接请求,并且建立一个新的TCP 连接,在调用这个函数之前需要通过调用listen()函数让服务器进入监听状态,如果是跑操作系统 ,在任务中调用这个函数 那么该任务就只能等待连接成功后才会往下执行 这个适合任务是处于一个阻塞状态的,失败返回 -1
举例:int new_socket_1=0 ; new_socket_1 = accept(s_create, (struct sockaddr *)&address, (socklen_t *)&addrlen);
accept()函数调用的是' lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) '
举例:new_socket_1 = accept(s_create, (struct sockaddr *)&address, (socklen_t *)&addrlen);
该函数如果连接成功后返回的值在参数 ‘ *addr ‘中,所以可以创建一个变量来得到这个值