2009-05-11LWIP之SOCKET的实现

Lwip协议栈的实现目的,无非是要上层用来实现app的socket编程。好,我们就从socket开始。为了兼容性,lwip的socket应该也是提供标准的socket接口函数,恩,没错,在src\include\lwip\socket.h文件中可以看到下面的宏定义:

#if LWIP_COMPAT_SOCKETS

#define accept(a,b,c)lwip_accept(a,b,c)

#define bind(a,b,c) lwip_bind(a,b,c)

#define shutdown(a,b)lwip_shutdown(a,b)

#define closesocket(s)lwip_close(s)

#define connect(a,b,c)lwip_connect(a,b,c)

#define getsockname(a,b,c)lwip_getsockname(a,b,c)

#define getpeername(a,b,c)lwip_getpeername(a,b,c)

#define setsockopt(a,b,c,d,e) lwip_setsockopt(a,b,c,d,e)

#define getsockopt(a,b,c,d,e) lwip_getsockopt(a,b,c,d,e)

#define listen(a,b)lwip_listen(a,b)

#define recv(a,b,c,d)lwip_recv(a,b,c,d)

#define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f)

#define send(a,b,c,d)lwip_send(a,b,c,d)

#define sendto(a,b,c,d,e,f)lwip_sendto(a,b,c,d,e,f)

#define socket(a,b,c)lwip_socket(a,b,c)

#define select(a,b,c,d,e)lwip_select(a,b,c,d,e)

#define ioctlsocket(a,b,c)lwip_ioctl(a,b,c)

#if LWIP_POSIX_SOCKETS_IO_NAMES

#define read(a,b,c)lwip_read(a,b,c)

#define write(a,b,c)lwip_write(a,b,c)

#define close(s)lwip_close(s)

先不说实际的实现函数,光看这些定义的宏,就是标准socket所必须有的接口。

接着看这些实际的函数实现。这些函数实现在src\api\socket.c中。先看下接受连接的函数,这个是tcp的

原型:int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)

可以看到这里的socket类型参数s,实际上是个int型

在这个函数中的第一个函数调用是sock = get_socket(s);

这里的sock变量类型是lwip_socket,定义如下:

/** Contains all internal pointers and states used for a socket */

struct lwip_socket {

/** sockets currently are built on netconns, each socket has one netconn */

struct netconn *conn;

/** data that was left from the previous read */

struct netbuf *lastdata;

/** offset in the data that was left from the previous read */

u16_t lastoffset;

/** number of times data was received, set by event_callback(),

tested by the receive and select functions */

u16_t rcvevent;

/** number of times data was received, set by event_callback(),

tested by select */

u16_t sendevent;

/** socket flags (currently, only used for O_NONBLOCK) */

u16_t flags;

/** last error that occurred on this socket */

int err;

};

好,这个结构先不管它,接着看下get_socket函数的实现【也是在src\api\socket.c文件中】,在这里我们看到这样一条语句sock = &sockets[s];很明显,返回值也是这个sock,它是根据传进来的序列号在sockets数组中找到对应的元素并返回该元素的地址。好了,那么这个sockets数组是在哪里被赋值了这些元素的呢?

进行到这里似乎应该从标准的socket编程的开始,也就是socket函数讲起,那我们就顺便看一下。它对应的实际实现是下面这个函数

Int lwip_socket(int domain, int type, int protocol)【src\api\socket.c】

这个函数根据不同的协议类型,也就是函数中的type参数,创建了一个netconn结构体的指针,接着就是用这个指针作为参数调用了alloc_socket函数,下面具体看下这个函数的实现

static int alloc_socket(struct netconn *newconn)

{

int i;

/* Protect socket array */

sys_sem_wait(socksem);

/* allocate a new socket identifier */

for (i = 0; i < NUM_SOCKETS; ++i) {

if (!sockets[i].conn) {

sockets[i].conn= newconn;

sockets[i].lastdata= NULL;

sockets[i].lastoffset = 0;

sockets[i].rcvevent= 0;

sockets[i].sendevent= 1; /* TCP send buf is empty */

sockets[i].flags= 0;

sockets[i].err= 0;

sys_sem_signal(socksem);

return i;

}

}

sys_sem_signal(socksem);

return -1;

}

对了,就是这个时候对全局变量sockets数组的元素赋值的。

既然都来到这里了,那就顺便看下netconn结构的情况吧。它的学名叫netconn descriptor

/** A netconn descriptor */

struct netconn

{

/** type of the netconn (TCP, UDP or RAW) */

enum netconn_type type;

/** current state of the netconn */

enum netconn_state state;

/** the lwIP internal protocol control block */

union {

struct ip_pcb*ip;

struct tcp_pcb *tcp;

struct udp_pcb *udp;

struct raw_pcb *raw;

} pcb;

/** the last error this netconn had */

err_t err;

/** sem that is used to synchroneously execute functions in the core context */

sys_sem_t op_completed;

/** mbox where received packets are stored until they are fetched

by the netconn application thread (can grow quite big) */

sys_mbox_t recvmbox;

/** mbox where new connections are stored until processed

by the application thread */

sys_mbox_t acceptmbox;

/** only used for socket layer */

int socket;

#if LWIP_SO_RCVTIMEO

/** timeout to wait for new data to be received

(or connections to arrive for listening netconns) */

int recv_timeout;

#endif /* LWIP_SO_RCVTIMEO */

#if LWIP_SO_RCVBUF

/** maximum amount of bytes queued in recvmbox */

int recv_bufsize;

#endif /* LWIP_SO_RCVBUF */

u16_t recv_avail;

/** TCP: when data passed to netconn_write doesn't fit into the send buffer,

this temporarily stores the message. */

struct api_msg_msg *write_msg;

/** TCP: when data passed to netconn_write doesn't fit into the send buffer,

this temporarily stores how much is already sent. */

int write_offset;

#if LWIP_TCPIP_CORE_LOCKING

/** TCP: when data passed to netconn_write doesn't fit into the send buffer,

this temporarily stores whether to wake up the original application task

if data couldn't be sent in the first try. */

u8_t write_delayed;

#endif /* LWIP_TCPIP_CORE_LOCKING */

/** A callback function that is informed about events for this netconn */

netconn_callback callback;

};【src\include\lwip\api.h】

到此,对这个结构都有些什么,做了一个大概的了解。

下面以SOCK_STREAM类型为例,看下netconn的new过程:

在lwip_socket函数中有

case SOCK_DGRAM:

conn = netconn_new_with_callback( (protocol == IPPROTO_UDPLITE) ?

NETCONN_UDPLITE : NETCONN_UDP, event_callback);

#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)

简略实现如下:

struct netconn*

netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)

{

struct netconn *conn;

struct api_msg msg;

conn = netconn_alloc(t, callback);

if (conn != NULL )

{

msg.function = do_newconn;

msg.msg.msg.n.proto = proto;

msg.msg.conn = conn;

TCPIP_APIMSG(&msg);

}

return conn;

}

主要就看TCPIP_APIMSG了,这个宏有两个定义,一个是LWIP_TCPIP_CORE_LOCKING的,一个非locking的。分别分析这两个不同类型的函数

* Call the lower part of a netconn_* function

* This function has exclusive access to lwIP core code by locking it

* before the function is called.

err_t tcpip_apimsg_lock(struct api_msg *apimsg)【这个是可以locking的】

{

LOCK_TCPIP_CORE();

apimsg->function(&(apimsg->msg));

UNLOCK_TCPIP_CORE();

return ERR_OK;

}

* Call the lower part of a netconn_* function

* This function is then running in the thread context

* of tcpip_thread and has exclusive access to lwIP core code.

err_t tcpip_apimsg(struct api_msg *apimsg)【此为非locking的】

{

struct tcpip_msg msg;

if (mbox != SYS_MBOX_NULL) {

msg.type = TCPIP_MSG_API;

msg.msg.apimsg = apimsg;

sys_mbox_post(mbox, &msg);

sys_arch_sem_wait(apimsg->msg.conn->op_completed, 0);

return ERR_OK;

}

return ERR_VAL;

}

其实,功能都是一样的,都是要对apimsg->function函数的调用。只是途径不一样而已。看看它们的功能说明就知道了。这么来说apimsg->function的调用很重要了。从netconn_new_with_proto_and_callback函数的实现,可以知道这个function就是do_newconn

Void do_newconn(struct api_msg_msg *msg)

{

if(msg->conn->pcb.tcp == NULL) {

pcb_new(msg);

}

/* Else? This "new" connection already has a PCB allocated. */

/* Is this an error condition? Should it be deleted? */

/* We currently just are happy and return. */

TCPIP_APIMSG_ACK(msg);

}

还是看TCP的,在pcb_new函数中有如下代码:

case NETCONN_TCP:

msg->conn->pcb.tcp = tcp_new();

if(msg->conn->pcb.tcp == NULL) {

msg->conn->err = ERR_MEM;

break;

}

setup_tcp(msg->conn);

break;

我们知道在这里建立了这个tcp的连接。至于这个超级牛的函数,以后再做介绍。

嗯,还是回过头来接着看accept函数吧。

Sock获得了,接着就是newconn = netconn_accept(sock->conn);通过mbox取得新的连接。粗略的估计了一下,这个新的连接应该和listen有关系。那就再次打断一下,看看那个listen操作。

lwip_listen --ànetconn_listen_with_backlog--àdo_listen--à

tcp_arg(msg->conn->pcb.tcp, msg->conn);

tcp_accept(msg->conn->pcb.tcp, accept_function);//注册了一个接受函数

* Accept callback function for TCP netconns.

* Allocates a new netconn and posts that to conn->acceptmbox.

static err_t accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)

{

struct netconn *newconn;

struct netconn *conn;

conn = (struct netconn *)arg;

/* We have to set the callback here even though

* the new socket is unknown. conn->socket is marked as -1. */

newconn = netconn_alloc(conn->type, conn->callback);

if (newconn == NULL) {

return ERR_MEM;

}

newconn->pcb.tcp = newpcb;

setup_tcp(newconn);

newconn->err = err;

/* Register event with callback */

API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);

if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK)

{

/* When returning != ERR_OK, the connection is aborted in tcp_process(),

so do nothing here! */

newconn->pcb.tcp = NULL;

netconn_free(newconn);

return ERR_MEM;

}

return ERR_OK;

}

对了,accept函数中从mbox中获取的连接就是这里放进去的。

再回到accept中来,取得了新的连接,接下来就是分配sock了,再然后,再然后?再然后就等用户来使用接收、发送数据了。

到此整个APP层,也就是传输层以上对socket的封装讲完了。在最后再总结一些整个路径的调用情况吧

lwip网络通信socket_LWIP之SOCKET的实现相关推荐

  1. lwip网络通信socket_lwIP在Socket模式下接口:BSD Socket API

    http://home.eeworld.com.cn/my/space-uid-162102-blogid-52270.html 2011 原文: 关于BSD Socket API 在网上找到的两个网 ...

  2. lwip网络通信socket_LWIP之SOCKET编程

    2009-05-15 LWIP之SOCKET编程 前几天看了关于LWIP协议栈的实现和FREERTOS的基本原理.今天开始调试LWIP的socket通信,是基于freertos系统的ARM9_STR9 ...

  3. java c语言 socket_网络通信 C语言 Socket TCP Select Server

    前言 工作中遇到各种各样的网络通信.有MQTT和CoAP这样的物联网应用层协议,也有各种自定义的TCP或UDP协议.使用各种不同的计算机语言和框架开发网络通信,例如Java的Netty框架,C语言原始 ...

  4. Python C/S架构,网络通信相关名词,socket编程

    主要内容: 一.  C/S架构 二. 网络通信的相关名词 三. socket编程 一. C/S架构和B\S架构概述 1. C/S架构: Client/Server(客户端/服务端)架构 描述: C/S ...

  5. Windows网络通信(二):socket异步编程

    简述 这里使用的API和同步编程的API是差不多的,只多了一个ioctlsocket和select函数.这里面涉及一个很重要的结构体fd_set.这里用到的API大部分都是windows和linux通 ...

  6. 树莓派python网络通信_Python3使用Socket实现树莓派与计算机半双工通信,实现聊天功能...

    项目中需要实现PC与树莓派通信完成控制,寻找一遍后,发现现例子大多比较简单,可以实现一次收发过程,第二次数据发送就会出现问题.观察发现少一个循环,无法保持联通状态,代码修改后可以简单的实现半双工通信, ...

  7. Android中基于TCP协议的网络通信之使用Socket进行通信

    TCP协议被称为一种端到端的协议.这是因为它为两台计算机的连接起到了重要作用:当一台计算机需要与另一台计算机进行接连时,TCP协议会他们之间建立一个连接:用于发送和接收数据的虚拟链路. TCP协议负责 ...

  8. Java 网络通信必备之socket通信基本原理介绍

    Java socket通信基本原理介绍 Java socket通信在不断的进行相关代码的开发,下面我们就看看如何才能更好的使用有关技术为我们的编程工作带来一定的帮助. 作者:佚名来源:互联网|2010 ...

  9. linux网络通信移植,基于socket API的C/S通信:将Qt程序从Linux移植到windows

    Qt是一个跨平台的GUI开发语言,它是对C++在图形设计方面上的一种扩充.Qt本身包含一系列用来设计图形界面的类,并且对C++原有的类都进行了再次封装.如果你的程序采用Qt的类库,那么源程序在不同平台 ...

最新文章

  1. 【IOC 控制反转】IOC 简介 ( 依赖注入的两种实现方式 | 编译期注入 | 运行期注入 )
  2. Sklearn(v3)——SVM理论(1)
  3. 简单总结过去的2021年
  4. 遥感空间尺度转换技术(升尺度和降尺度)
  5. 计算机二级web考点,2018年计算机二级考试WEB考点:web应用程序状态管理方式
  6. 共阳数码管段码表_简单共阴极数码管电路图大全
  7. bzoj 1579: [Usaco2009 Feb]Revamping Trails 道路升级【分层图+spfa】
  8. map java 初始化赋值_Java 中 HashMap 初始化时赋值(示例代码)
  9. 烂大街的常用Linux命令、工具
  10. springmvc 页面跳转样式访问路径总是多一层地址_Net Core实战之基于角色的访问控制的设计...
  11. Windows Phone 程序发布过程
  12. [2019上海网络赛J题]Stone game
  13. linux qt 屏幕亮度,调整14.04.1 屏幕亮度
  14. python_基础部分(1)
  15. hive经典面试题1--根据主播上播、下播时间戳求播出时长
  16. win7 64位搭建scrapy
  17. Ubuntu系统安装webrtc
  18. python判断阶乘和数_python如何计算数的阶乘
  19. 基于 USB PD DRP的充电宝原理解析
  20. python循环语句和跳出语句详解

热门文章

  1. eclipse 设置豆沙绿
  2. 【SSH网上商城项目实战22】获取银行图标以及支付页面的显示
  3. 【unity】粒子系统
  4. 微信小程序之人脸识别(2)
  5. excel表格怎么拆分
  6. 位图的简单实现(bitMap)
  7. Tensorflow2.* 加载和预处理数据之用 tf.data 加载磁盘图片数据(4)
  8. java程序如何获取北京时间_计算当前的北京时间java(currentTimeMillis)
  9. vivado如何添加xci文件
  10. Arduino-IRremote-红外遥控器,发射与接收