目录
1. Socket 概述
Socket 英文原意是“孔”或者“插座”的意思,在网络编程中,通常将其称之为“套接字”,当前网络中的主流程序设计都是使用Socket 进行编程的,因为它简单易用,更是一个标准,能在不同平台很方便移植。
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。
总之,套接字Socket=(IP地址:端口号),套接字的表示方法是点分十进制的IP地址后面写上端口号,中间用冒号或逗号隔开。每一个传输层连接唯一地被通信两端的两个端点(即两个套接字)所确定。
Socket最初是加利福尼亚大学Berkeley分校为Unix系统开发的网络通信接口。后来随着TCP/IP网络的发展,Socket成为最为通用的应用程序接口,也是在Internet上进行应用开发最为通用的API。
为了能让更多开发者直接上手LwIP 的编程,专门设计了LwIP 的第三种编程接口——Socket API,它兼容BSD Socket。
Socket 虽然是能在多平台移植,但是LwIP 中的Socket 并不完善,因为LwIP 设计之初就是为了在嵌入式平台中使用,它只实现了完整Socket 的部分功能,不过,在嵌入式平台中,这些功能早已足够。
2. LwIP 中的socket
在LwIP 中,Socket API 是基于NETCONN API 之上来实现的,系统最多提供MEMP_NUM_NETCONN 个netconn 连接结构,因此决定Socket 套接字的个数也是那么多个。
为了更好对netconn 进行封装,LwIP 还定义了一个套接字结构体——lwip_sock(称之为Socket 连接结构),每个lwip_sock 内部都有一个netconn 的指针,实现了对netconn 的再次封装。
LwIP 定义了一个lwip_sock 类型的sockets数组,通过套接字就可以直接索引并且访问这个结构体了,这也是为什么套接字是一个整数的原因,lwip_sock 结构体是比较简单的,因为基本上全是依赖netconn 实现。
#define NUM_SOCKETS MEMP_NUM_NETCONN // 默认是4
/** 全局可用套接字数组 **/
static struct lwip_sock sockets[NUM_SOCKETS];
union lwip_sock_lastdata {
struct netbuf *netbuf;
struct pbuf *pbuf;
};
/** 包含用于套接字的所有内部指针和状态*/
struct lwip_sock {
/** 套接字当前是在netconn 上构建的,每个套接字都有一个netconn*/
struct netconn *conn;
/** 从上一次读取中留下的数据 */
union lwip_sock_lastdata lastdata;
#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
/** number of times data was received, set by event_callback(),
tested by the receive and select functions */
s16_t rcvevent;
/** number of times data was ACKed (free send buffer), set by event_callback(),
tested by select */
u16_t sendevent;
/** error happened for this socket, set by event_callback(), tested by select */
u16_t errevent;
/** 使用select 等待此套接字的线程数 */
SELWAIT_T select_waiting;
#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
#if LWIP_NETCONN_FULLDUPLEX
/* counter of how many threads are using a struct lwip_sock (not the 'int') */
u8_t fd_used;
/* status of pending close/delete actions */
u8_t fd_free_pending;
#define LWIP_SOCK_FD_FREE_TCP 1
#define LWIP_SOCK_FD_FREE_FREE 2
#endif
};
3. Socket API
3.1 socket()
向内核申请一个套接字,在本质上该函数其实就是对netconn_new()函数进行了封装,虽然说不是直接调用它,但是主体完成的工作就做了 netconn_new()函数的事情,而且该函数本质是一个宏定义.
/** @ingroup socket */
#define socket(domain,type,protocol) lwip_socket(domain,type,protocol)
int
lwip_socket(int domain, int type, int protocol);
#define AF_INET 2
/* Socket protocol types (TCP/UDP/RAW) */
#define SOCK_STREAM 1
#define SOCK_DGRAM 2
#define SOCK_RAW 3
参数domain :表示该套接字使用的协议簇,对于TCP/IP 协议来说,该值始终为AF_INET。
参数type: 指定了套接字使用的服务类型,可能的类型有3 种:
1. SOCK_STREAM:提供可靠的(即能保证数据正确传送到对方)面向连接的Socket 服务,多用于资料(如文件)传输,如TCP 协议。
2. SOCK_DGRAM:是提供无保障的面向消息的Socket 服务,主要用于在网络上