深度学习uip中的psock.c和psock.h,由于"psock.c和psock.h"在源文件中,结构定义很分散,这里我做了修改,另外把pt协程进行替换。
#define My_REPLACEMENT 0 //PT协程不用替换
#define My_REPLACEMENT 1 //PT协程替换
替换后的代码,可以仿真,跟踪,便于理解pt协程的工作原理。本文对uIP协议栈中的psock.c和psock.h文件进行了深度解析和重构。通过条件编译保留原始实现和修改后的实现,便于对比分析。新增了更详细的代码注释,解释了TCP/UDP缓冲区位置等关键细节。该重构提高了代码可读性和可调试性,为理解uIP协议栈的protothread实现机制提供了清晰参考。
1、uip中的psock.h
#ifndef PSOCK_H
#define PSOCK_H
#include "uipopt.h"
#include "pt.h"
//pt协程的状态"struct psock结构"中的state的值定义
#define STATE_NONE 0 //pt协程的初始状态
#define STATE_ACKED 1 //收到TCP应答
#define STATE_READ 2 //"读完新数据标志"
#define STATE_BLOCKED_NEWDATA 3
//如果发现"读完新数据标志"建立,则进入阻塞读新数据
#define STATE_BLOCKED_CLOSE 4 //程序没有使用
#define STATE_BLOCKED_SEND 5 //程序没有使用
#define STATE_DATA_SENT 6 //有新数据等待发送
struct psock_buf {
u8_t *ptr; //输入缓冲区的剩余空间的首地址
unsigned short left; //输入缓冲区的剩余字节数
};
struct psock {
struct pt pt; //协程状态变量pt.lc
struct pt psockpt; //协程状态变量psockpt.lc
/* Protothreads - one that's using the psock functions, and one that runs inside the psock functions. */
const u8_t *sendptr;
/*待发送数据的下一组数据的指针,Pointer to the next data to be sent. */
u8_t *readptr;
/*待读取的下一组数据的指针,Pointer to the next data to be read. */
char *bufptr;
/*传入数据的缓冲区的指针,Pointer to the buffer used for buffering incoming data. */
u16_t sendlen;
/*待发送数据的剩余字节数,The number of bytes left to be sent. */
u16_t readlen;
/*待读取的剩余字节数,The number of bytes left to be read. */
struct psock_buf buf;
/*输入缓冲区的状态,The structure holding the state of the input buffer. */
unsigned int bufsize;
/*输入缓冲区的大小,The size of the input buffer. */
unsigned char state;
/*pt协程的状态,The state of the protosocket. */
};
struct httpd_fs_file {
char *data; //指向发送"网页数据"的首地址
int len; //发送"网页数据"的长度
};
struct httpd_state {
unsigned char timer;
//若"轮询标志"建立,则"中止连接标志"计时器开始工作
struct psock sin; //用来管理http输入数据
struct psock sout; //用来管理http输出数据
struct pt outputpt; //协程状态变量outputpt.lc
struct pt scriptpt; //协程状态变量scriptpt.lc,用来处理script
char inputbuf[50];
//用来保存接收到的"WEB指令GET","/r"或"/ "
char filename[20];
//用来存放文件名,如"/index.html","/404.html","/favicon.ico"
char state;
//指示HTTP数据的输出状态:0为STATE_WAITING,1为STATE_OUTPUT
struct httpd_fs_file file; //网页数据的首地址和数据长度
int len;
/*若待发送数据长度大于最大段,则按照最大段的长度发送,
否则按照实际长度发送;
*/
char *scriptptr; //脚本指针script
int scriptlen; //脚本指针script指向的数据长度
unsigned short count;
/*为TCP连接表uipConnectTable[]中的下标值,
uipConnectTable[count]表示当前连接到哪个IP地址和端口*/
};
void psock_init(struct psock *psock, char *buffer, unsigned int buffersize);
//初始化一个协程,Initialize a protosocket.
#define PSOCK_INIT(psock, buffer, buffersize) psock_init(psock, buffer, buffersize)
#define PSOCK_BEGIN(psock) PT_BEGIN(&((psock)->pt))
//在一个函数中启动原套接字原线程。
//Start the protosocket protothread in a function.
PT_THREAD(psock_send(struct psock *psock, const char *buf, unsigned int len));
//Send data.
//将buf[]中前len个字节拷贝到首地址为uip_sappdata的发送缓冲区,待发送数据的长度为uip_slen=len
#define PSOCK_SEND(psock, data, datalen) PT_WAIT_THREAD(&((psock)->pt), psock_send(psock, data, datalen))
#define PSOCK_SEND_STR(psock, str) PT_WAIT_THREAD(&((psock)->pt), psock_send(psock, str, strlen(str)))
//str指向待发送字符串的首地址,strlen(str)为str指向待发送字符串的长度
//将str[]中中的字符串拷贝到首地址为uip_sappdata的发送缓冲区
//待发送数据的长度为uip_slen,即字符串长度
PT_THREAD(psock_generator_send(struct psock *s,unsigned short (*f)(void *), void *arg));
//将s->file.data为首地址的前s->len个字节拷贝到uip_sappdata为首地址的"发送缓存"
//使用函数生成数据并发送数据,Generate data with a function and send it
#define PSOCK_GENERATOR_SEND(psock, generator, arg) PT_WAIT_THREAD(&((psock)->pt),psock_generator_send(psock, generator, arg))
#define PSOCK_CLOSE(psock) uip_close()
//关闭一个协程,Close a protosocket.
PT_THREAD(psock_readbuf(struct psock *s));
//读取数据,直到缓冲区已满。Read data until the buffer is full.
#define PSOCK_READBUF(s) PT_WAIT_THREAD(&((s)->pt), psock_readbuf(s))
PT_THREAD(psock_readto(struct psock *s, unsigned char c));
//读取数据到指定的字符,即c的值。Read data up to a specified character.
#define PSOCK_READTO(s, c) PT_WAIT_THREAD( &((s)->pt), psock_readto(s, c) )
u16_t psock_datalen(struct psock *psock);
#define PSOCK_DATALEN(psock) psock_datalen(psock)
//之前读取的数据的长度。
//The length of the data that was previously read.
#define PSOCK_EXIT(psock) PT_EXIT(&((psock)->pt))
//退出一个协程的协线程。 Exit the protosocket's protothread.
//关闭一个协程,并退出该协程的协线程。
//Close a protosocket and exit the protosocket's protothread.
#define PSOCK_CLOSE_EXIT(psock) \
do { \
PSOCK_CLOSE(psock); \
PSOCK_EXIT(psock); \
} while(0)
//声明原套接字的原线程的结束。
//Declare the end of a protosocket's protothread.
#define PSOCK_END(psock) PT_END(&((psock)->pt))
char psock_newdata(struct psock *s);
//检查新数据是否已到达原套接字上。
//Check if new data has arrived on a protosocket.
#define PSOCK_NEWDATA(psock) psock_newdata(psock)
//返回1,在uip_appdata为首地址的缓冲区中有新数据等待读取
//返回0,在uip_appdata为首地址的缓冲区中的所有数据已经读取
#define PSOCK_WAIT_UNTIL(psock, condition) PT_WAIT_UNTIL(&((psock)->pt), (condition));
//Wait until a condition is true.
#define PSOCK_WAIT_THREAD(psock, condition) PT_WAIT_THREAD(&((psock)->pt), (condition))
#endif /* PSOCK_H */
2、uip中的psock.c
#include "psock.h"
#include "string.h" //使能strcpy(),strlen(),memset(),NULL
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "uip.h"
#include "uip-conf.h"
#define BUF_NOT_FOUND 0
#define BUF_NOT_FULL 0
#define BUF_FULL 1
#define BUF_FOUND 2
//函数功能:
//设置buf->ptr = bufptr;它是"输入缓冲区"的剩余空间的首地址
//设置buf->left.left = bufsize;它是"输入缓冲区"的剩余字节数
static void buf_setup(struct psock_buf *buf, u8_t *bufptr, u16_t bufsize)
{
buf->ptr = bufptr;
//buf->ptr为"输入缓冲区"的剩余空间的首地址
buf->left = bufsize;
//buf->left为"输入缓冲区"的剩余字节数
//令buf->ptr = bufptr;buf->left.left = bufsize;
}
//函数功能:
//(*sourceDataPointer)指向uip_buf[]中的某个存储单元;
//(*sourceDataLength)为uip_buf[]中的剩余字节数;
//返回BUF_NOT_FULL,表示目的buf[]未装满,但源数据被读完了;
//返回BUF_FULL,表示目的buf[]装满,源数据可能没有被读完
static u8_t buf_bufdata(struct psock_buf *buf, u16_t len, u8_t **sourceDataPointer, u16_t *sourceDataLength)
{
if(*sourceDataLength < buf->left)
//buf[]的剩余空间可以装下(*sourceDataLength)个字节,还有剩余
{
memcpy(buf->ptr, *sourceDataPointer, *sourceDataLength);
buf->ptr += *sourceDataLength; // 修改buf[]的剩余空间的首地址
buf->left -= *sourceDataLength; // 修改buf[]的剩余空间的字节数
*sourceDataPointer += *sourceDataLength;//修改源数据指针
*sourceDataLength = 0;
//buf[]可以装下(*sourceDataLength)个字节,所以设置(*sourceDataLength)=0
return BUF_NOT_FULL;//返回0,表示buf[]未满
}
else if(*sourceDataLength == buf->left)
//buf[]的剩余空间刚好可以装下(*sourceDataLength)个字节,没有剩余
{
memcpy(buf->ptr, *sourceDataPointer, *sourceDataLength);
buf->ptr += *sourceDataLength;//装完数据后,buf[]的剩余空间的首地址
buf->left = 0;
//buf[]的剩余空间刚好可以装下(*sourceDataLength)个字节,所以没有剩余
*sourceDataPointer += *sourceDataLength;//装完数据后,修改源数据指针
*sourceDataLength = 0;
//buf[]可以装下(*sourceDataLength)个字节,所以设置(*sourceDataLength)=0
return BUF_FULL;//返回1,表示buf[]满
}
else//buf[]的剩余空间一次性装不下(*sourceDataLength)个字节
{
memcpy(buf->ptr, *sourceDataPointer, buf->left);
buf->ptr += buf->left;//装完数据后,buf[]的剩余空间的首地址
*sourceDataLength -= buf->left;//装完数据后,计算源数据的剩余字节数
*sourceDataPointer += buf->left;//装完数据后,修改源数据指针
buf->left = 0;
//buf[]装不下(*sourceDataLength)个字节,所以buf[]的剩余空间的字节数为0
return BUF_FULL;//返回1,表示buf[]满
}
}
//函数功能:若读到"endmarker的值",则返回BUF_FOUND或(BUF_FOUND | BUF_FULL)
//*buf是输入缓冲区的剩余空间的首地址
//endmarker的值是要读的目标数据
//*sourceDataPointer为"待读取的下一组数据的指针",指向uip_buf[]中的某个存储单元;
//*sourceDataLength为待读取的剩余字节数,为uip_buf[]中的剩余字节数;
//返回BUF_NOT_FOUND,表示没有找到"结束符号endmarker",buf[]可能未满
//返回BUF_FULL,表示没有找到"结束符号endmarker",但是目的缓存buf[]满了
//返回BUF_FOUND,表示找到"结束符号endmarker",buf[]可能未满
//返回BUF_FOUND | BUF_FULL,表示找到"结束符号endmarker",但是目的缓存buf[]满了;
static u8_t buf_bufto(register struct psock_buf *buf, u8_t endmarker,register u8_t **sourceDataPointer, register u16_t *sourceDataLength)
{
u8_t c;
while(buf->left > 0 && *sourceDataLength > 0)
{
c = **sourceDataPointer;
*buf->ptr = c;//buf->ptr为"输入缓冲区的剩余空间的首地址"
++*sourceDataPointer;//源数据的指针加1
++buf->ptr; //buf[]的剩余空间首地址加1
--*sourceDataLength; //源数据的剩余长度减1
--buf->left; //buf[]的剩余空间的字节数减1
if(c == endmarker)//找到"结束符号endmarker"
{
return BUF_FOUND;//返回2,表示找到"结束符号",buf[]可能未满
}
}
if(*sourceDataLength == 0)//继续找"结束符号endmarker",发现源数据被找完了
{
return BUF_NOT_FOUND;//返回0,表示没有找到"结束符号endmarker"
}
while(*sourceDataLength > 0)//继续找"结束符号endmarker",说明目的缓存buf[]满了
{
c = **sourceDataPointer;
--*sourceDataLength; //源数据的剩余长度减1
++*sourceDataPointer; //源数据的指针加1
if(c == endmarker)//找到"结束符号endmarker"
{
return BUF_FOUND | BUF_FULL;
//返回3,表示找到"结束符号endmarker",但是目的缓存buf[]满了
}
}
//执行到此处,说明目的缓存buf[]满了
return BUF_FULL;//返回1,表示没有找到"结束符号endmarker",但是目的缓存buf[]满了
}
//将s->sendptr[]中前uip_slen个字节拷贝到首地址为uip_sappdata的发送缓冲区,待发送数据的长度为uip_slen,返回值为1
//返回值为0,无发送数据
//在TCP中uip_sappdata = &uip_buf[54];
//在UDP中uip_sappdata = &uip_buf[42];
static char send_data(register struct psock *s)
{
u8_t tmpFlags;
u16_t sizeMAX;
tmpFlags=uip_rexmit();
//令tmpFlags=uip_flags & UIP_REXMIT,查询是否要"重新传输上次发送的数据"
if(s->state != STATE_DATA_SENT || tmpFlags)
{
sizeMAX=uip_mss();//读"最大段"的大小
if(s->sendlen > sizeMAX)
{
uip_send(s->sendptr, sizeMAX);
/*将s->sendptr[]中前sizeMAX个字节拷贝到首地址为uip_sappdata的发送缓冲区,待发送数据的长度为uip_slen*/
//在TCP中uip_sappdata = &uip_buf[54];在UDP中uip_sappdata = &uip_buf[42];
}
else
{
uip_send(s->sendptr, s->sendlen);
/*将s->sendptr[]中前s->sendlen个字节拷贝到首地址为uip_sappdata的发送缓冲区,待发送数据的长度为uip_slen*/
//在TCP中uip_sappdata = &uip_buf[54];在UDP中uip_sappdata = &uip_buf[42];
}
s->state = STATE_DATA_SENT; //修改"pt协程的状态为"发送状态"STATE_DATA_SENT
return 1;
}
return 0;
}
//接收到的TCP应答标志后,需要"修改剩余长度s->sendlen,剩余空间的首地址s->sendptr",返回值为1
static char data_acked(register struct psock *s)
{
u16_t sizeMAX;
u8_t tmpFlags;
tmpFlags=uip_acked();
//令tmpFlags=(uip_flags & UIP_ACKDATA),读接收到的TCP应答标志
if(s->state == STATE_DATA_SENT && tmpFlags)
{
sizeMAX=uip_mss();//读"最大段"的大小
if(s->sendlen > sizeMAX)//一次性发送不完
{
s->sendlen -= sizeMAX;// 收到的TCP应答标志后,修改剩余字节数
s->sendptr += sizeMAX;// 收到的TCP应答标志后,修改发送缓存的首地址
}
else//一次性发完
{
s->sendptr += s->sendlen;// 收到的TCP应答标志后,修改发送缓存的首地址
s->sendlen = 0;// 收到的TCP应答标志后,修改剩余字节数
}
s->state = STATE_ACKED; //修改"pt协程的状态为"收到的TCP应答标志"STATE_ACKED
return 1;
}
return 0;//没收到的TCP应答标志,或者没有进入发送状态
}
//将buf[]中前len个字节拷贝到首地址为uip_sappdata的发送缓冲区,待发送数据的长度为uip_slen=len
//在TCP中uip_sappdata = &uip_buf[54];
//在UDP中uip_sappdata = &uip_buf[42];
//buf指向待发送数据的首地址
//len为待发送数据的长度
PT_THREAD(psock_send(register struct psock *s, const char *buf,unsigned int len))
{
#if My_REPLACEMENT==0
PT_BEGIN(&s->psockpt);
if(len == 0)//待发送数据长度为0,退出
{
PT_EXIT(&s->psockpt);
}
//保存指向要发送的数据的长度和指针。
s->sendptr = (const u8_t*)buf;
s->sendlen = len;
s->state = STATE_NONE;
while(s->sendlen > 0)
{
PT_WAIT_UNTIL(&s->psockpt, data_acked(s) & send_data(s));
/*send_data(s)将s->sendptr[]中前uip_slen个字节拷贝到首地址为uip_sappdata的发送缓冲区,待发送数据的长度为uip_slen,返回值为1;data_acked(s)接收到的TCP应答标志后,需要"修改剩余长度s->sendlen,剩余空间的首地址s->sendptr",返回值为1*/
}
s->state = STATE_NONE;
PT_END(&s->psockpt);
#else
{PT_YIELD_FLAG = 1;switch(s->psockpt.lc){case 0:
if(len == 0)
{
s->psockpt.lc=0;return PT_EXITED;
}
s->sendptr = (const u8_t*)buf; //保存指向要发送的数据的指针。
s->sendlen = len; //保存指向要发送的数据的长度。
s->state = STATE_NONE;
while(s->sendlen > 0)
{//"__LINE__宏"在源文件中出现的行号
s->psockpt.lc=LINE;case LINE:if( !( data_acked(s) & send_data(s) ) ){return PT_WAITING;}
/*send_data(s)将s->sendptr[]中前uip_slen个字节拷贝到首地址为uip_sappdata的发送缓冲区,待发送数据的长度为uip_slen,返回值为1;data_acked(s)接收到的TCP应答标志后,需要"修改剩余长度s->sendlen,剩余空间的首地址s->sendptr",返回值为1*/
}
s->state = STATE_NONE;
}PT_YIELD_FLAG = 0;s->psockpt.lc=0;return PT_ENDED;}
#endif
}
//将s->file.data为首地址的前s->len个字节拷贝到uip_sappdata为首地址的"发送缓存"
//在TCP中uip_sappdata = &uip_buf[54];
//在UDP中uip_sappdata = &uip_buf[42];
//generate=generate_part_of_file()
PT_THREAD(psock_generator_send(register struct psock *s,unsigned short (*generate)(void *), void *arg))
{
#if My_REPLACEMENT==0
PT_BEGIN(&s->psockpt);
/* Ensure that there is a generator function to call. */
if(generate == NULL) {
PT_EXIT(&s->psockpt);
}
/* Call the generator function to generate the data in the uip_appdata buffer. */
s->sendlen = generate(arg);//装载发送数据的长度
s->sendptr = uip_appdata; //装载发送数据的首地址
s->state = STATE_NONE;
do {
/* Call the generator function again if we are called to perform a retransmission. */
if(uip_rexmit())
{
generate(arg);
}
/* Wait until all data is sent and acknowledged. */
PT_WAIT_UNTIL(&s->psockpt, data_acked(s) & send_data(s));
/*send_data(s)将s->sendptr[]中前uip_slen个字节拷贝到首地址为uip_sappdata的发送缓冲区,待发送数据的长度为uip_slen,返回值为1;data_acked(s)接收到的TCP应答标志后,需要"修改剩余长度s->sendlen,剩余空间的首地址s->sendptr",返回值为1*/
} while(s->sendlen > 0);
s->state = STATE_NONE;
PT_END(&s->psockpt);
#else
{PT_YIELD_FLAG = 1;switch(s->psockpt.lc){case 0:
if(generate == NULL)
{
s->psockpt.lc=0;return PT_EXITED;
}
s->sendlen = generate(arg);//装载发送数据的长度
s->sendptr = uip_appdata; //装载发送数据的首地址
s->state = STATE_NONE;
do
{
if(uip_rexmit()){generate(arg);}
s->psockpt.lc = LINE;case LINE:if( !(data_acked(s) & send_data(s)) )return PT_WAITING;
/*send_data(s)将s->sendptr[]中前uip_slen个字节拷贝到首地址为uip_sappdata的发送缓冲区,待发送数据的长度为uip_slen,返回值为1;data_acked(s)接收到的TCP应答标志后,需要"修改剩余长度s->sendlen,剩余空间的首地址s->sendptr",返回值为1*/
} while(s->sendlen > 0);
s->state = STATE_NONE;
}s->psockpt.lc=0;PT_YIELD_FLAG = 0;return PT_ENDED;}
#endif
}
//之前读取的数据的长度。The length of the data that was previously read.
u16_t psock_datalen(struct psock *psock)
{
return psock->bufsize - psock->buf.left;
//psock->bufsize为"输入缓冲区的大小"
//psock->buf.left是"输入缓冲区的剩余字节数"
//计算psock->bufsize[]读到的字节数量
}
//函数功能:
//返回1,有新数据等待读取;返回0,没有新数据等待读取;
char psock_newdata(struct psock *s)
{
if(s->readlen > 0)//"待读取的剩余字节数"大于0
{
return 1;
}
else if(s->state == STATE_READ)//发现"读完新数据标志"建立
{
s->state = STATE_BLOCKED_NEWDATA;
//修改"pt协程的状态"为新数据读完STATE_BLOCKED_NEWDATA
return 0;//没有新数据
}
else if(uip_newdata())//发现"建立收到新数据标志"
{
return 1;
}
else
{
/*没有新数据 There is no new data. */
return 0;
}
}
//读取数据到指定的字符c,即c的值。
PT_THREAD(psock_readto(register struct psock *s, unsigned char c))
{
u8_t ret;
#if My_REPLACEMENT==0
PT_BEGIN(&s->psockpt);
buf_setup(&s->buf, (u8_t*)s->bufptr, s->bufsize);
//令s->buf.ptr = s->bufptr;s->buf.left.left = s->bufsize;
/* XXX: Should add buf_checkmarker() before do{} loop, if
incoming data has been handled while waiting for a write. */
do
{
if(s->readlen == 0)//剩余字节数为0
{
PT_WAIT_UNTIL(&s->psockpt, psock_newdata(s));
//psock_newdata()返回1,有新数据等待读取;psock_newdata()返回0,没有新数据等待读取;
s->state = STATE_READ;//修改"pt协程的状态"为建立"读完新数据标志"
s->readptr = (u8_t *)uip_appdata;
//s->readptr为"待读取的下一组数据的指针"
//如果紧急指针为0x0000,则在TCP中uip_sappdata = &uip_buf[54];
//如果紧急指针为0x0000,则在UDP中uip_sappdata = &uip_buf[42];
s->readlen = uip_datalen();//令s->readlen=uip_len
}
ret=buf_bufto(&s->buf, c,&s->readptr, &s->readlen);
//s->buf是输入缓冲区的剩余空间的首地址
//c的值是要读的目标数据
//s->readptr为"待读取的下一组数据的指针"
//s->readlen为待读取的剩余字节数
//返回BUF_NOT_FOUND,表示没有找到c,s->buf[]可能未满
//返回BUF_FULL,表示没有找到c,但是目的缓存s->buf[]满了
//返回BUF_FOUND,表示找到c,s->buf[]可能未满
//返回BUF_FOUND | BUF_FULL,表示找到c,但是目的缓存s->buf[]满了;
ret=(u8_t)(ret & BUF_FOUND);
} while( ret == 0 );
if(psock_datalen(s) == 0)//之前读取的数据的长度。
{
s->state = STATE_NONE;//修改"pt协程的状态"为"pt协程的初始状态"
PT_RESTART(&s->psockpt);
}
PT_END(&s->psockpt);
#else
{PT_YIELD_FLAG = 1;switch(s->psockpt.lc){case 0:
buf_setup(&s->buf, (u8_t*)s->bufptr, s->bufsize);
//设置s->buf.ptr = s->bufptr;它是输入缓冲区的剩余空间的首地址
//s->bufptr为传入数据的缓冲区的指针
//设置s->buf.left = s->bufsize;它是输入缓冲区的剩余字节数
//s->bufsize为输入缓冲区的大小
do
{
if(s->readlen == 0)//待读取的剩余字节数为0
{
s->psockpt.lc = LINE; case LINE:if( !( psock_newdata(s) ) )return PT_WAITING;
//psock_newdata()返回1,有新数据等待读取;psock_newdata()返回0,没有新数据等待读取;
//如果"有新数据等待读取",则执行下面的语句
s->state = STATE_READ; //修改"pt协程的状态"为建立"读完新数据标志"
s->readptr = (u8_t *)uip_appdata;
//s->readptr为"待读取的下一组数据的指针"
//如果紧急指针为0x0000,则在TCP中uip_sappdata = &uip_buf[54];
//如果紧急指针为0x0000,则在UDP中uip_sappdata = &uip_buf[42];
s->readlen = uip_datalen();
//令s->readlen=uip_len,s->readlen为待读取的剩余字节数
}
ret=buf_bufto(&s->buf, c,&s->readptr, &s->readlen);
//s->buf是输入缓冲区的剩余空间的首地址
//c的值是要读的目标数据,若读到"c的值",则返回BUF_FOUND或(BUF_FOUND | BUF_FULL)
//s->readptr为"待读取的下一组数据的指针"
//s->readlen为待读取的剩余字节数
//ret=BUF_NOT_FOUND,表示没有找到c,s->buf[]可能未满
//ret=BUF_FULL,表示没有找到c,但是目的缓存s->buf[]满了
//ret=BUF_FOUND,表示找到c,s->buf[]可能未满
//ret=BUF_FOUND | BUF_FULL,表示找到c,但是目的缓存s->buf[]满了;
ret=(u8_t)(ret & BUF_FOUND);
} while( ret == 0 );
if(psock_datalen(s) == 0)//如果"之前读取的数据的长度"为0
{
s->state = STATE_NONE; //修改"pt协程的状态"为"pt协程的初始状态"
s->psockpt.lc=0;return PT_WAITING;
}
}PT_YIELD_FLAG = 0;s->psockpt.lc=0;return PT_ENDED;}
#endif
}
//读取数据,直到缓冲区已满。
PT_THREAD(psock_readbuf(register struct psock *s))
{
u8_t ret;
#if My_REPLACEMENT==0
PT_BEGIN(&s->psockpt);
buf_setup(&s->buf, (u8_t*)s->bufptr, s->bufsize);
//令s->buf.ptr = s->bufptr;s->buf.left.left = s->bufsize;
/* XXX: Should add buf_checkmarker() before do{} loop, if
incoming data has been handled while waiting for a write. */
do {
if(s->readlen == 0)
{
PT_WAIT_UNTIL(&s->psockpt, psock_newdata(s));
//psock_newdata()返回1,有新数据等待读取;psock_newdata()返回0,没有新数据等待读取;
s->state = STATE_READ; //修改"pt协程的状态"为建立"读完新数据标志"
s->readptr = (u8_t *)uip_appdata;
//s->readptr为"待读取的下一组数据的指针",指向uip_buf[]中的某个存储单元
s->readlen = uip_datalen();//令s->readlen=uip_len,为uip_buf[]中的剩余字节数
}
ret=buf_bufdata(&s->buf, s->bufsize,&s->readptr,&s->readlen);
//ret=BUF_NOT_FULL,表示s->buf[]未满,但源数据被装完了;
//ret=BUF_FULL,表示s->buf[]满,源数据可能没有被装完
} while( ret != BUF_FULL );
if(psock_datalen(s) == 0) {
s->state = STATE_NONE; //修改"pt协程的状态"为"pt协程的初始状态"
PT_RESTART(&s->psockpt);
}
PT_END(&s->psockpt);
#else
{PT_YIELD_FLAG = 1;switch(s->psockpt.lc) { case 0:
buf_setup(&s->buf, (u8_t*)s->bufptr, s->bufsize);
//令s->buf.ptr = s->bufptr;s->buf.left.left = s->bufsize;
do {
if(s->readlen == 0)
{
s->psockpt.lc = LINE; case LINE:if( !( psock_newdata(s) ) )return PT_WAITING;
//psock_newdata()返回1,有新数据等待读取;psock_newdata()返回0,没有新数据等待读取;
//如果"有新数据等待读取",则执行下面的语句
s->state = STATE_READ; //修改"pt协程的状态"为建立"读完新数据标志"
s->readptr = (u8_t *)uip_appdata;
//s->readptr为"待读取的下一组数据的指针",指向uip_buf[]中的某个存储单元
//如果紧急指针为0x0000,则在TCP中uip_sappdata = &uip_buf[54];
//如果紧急指针为0x0000,则在UDP中uip_sappdata = &uip_buf[42];
s->readlen = uip_datalen();//令s->readlen=uip_len,为uip_buf[]中的剩余字节数
}
ret=buf_bufdata(&s->buf, s->bufsize,&s->readptr,&s->readlen);
//ret=BUF_NOT_FULL,表示s->buf[]未满,但源数据被装完了;
//ret=BUF_FULL,表示s->buf[]满,源数据可能没有被装完
} while( ret != BUF_FULL );
//源数据被装完了
if(psock_datalen(s) == 0)//如果"之前读取的数据的长度"为0
{
s->state = STATE_NONE; //修改"pt协程的状态"为"pt协程的初始状态"
s->psockpt.lc=0;return PT_WAITING;
}
}PT_YIELD_FLAG = 0;s->psockpt.lc=0;return PT_ENDED;}
#endif
}
void psock_init(register struct psock *psock, char *buffer, unsigned int buffersize)
{
psock->state = STATE_NONE; //修改"pt协程的状态"为"pt协程的初始状态"
psock->readlen = 0; //待读取的剩余字节数
psock->bufptr = buffer; //传入数据的缓冲区的指针
psock->bufsize = buffersize;//psock->bufsize为"输入缓冲区的大小"
buf_setup(&psock->buf, (u8_t*)buffer, buffersize);
//设置buf->ptr = buffer;它是输入缓冲区的剩余空间的首地址
//设置buf->left.left = buffersize;它是输入缓冲区的剩余字节数
#if My_REPLACEMENT==0
PT_INIT(&psock->pt);
PT_INIT(&psock->psockpt);
#else
psock->pt.lc=0;
psock->psockpt.lc=0;
#endif
}
3、附上uip中psock.c的源文件:
#include "stdio.h"
#include "string.h"
#include "uipopt.h"
#include "psock.h"
#include "uip.h"
#define STATE_NONE 0
#define STATE_ACKED 1
#define STATE_READ 2
#define STATE_BLOCKED_NEWDATA 3
#define STATE_BLOCKED_CLOSE 4
#define STATE_BLOCKED_SEND 5
#define STATE_DATA_SENT 6
#define BUF_NOT_FULL 0
#define BUF_NOT_FOUND 0
#define BUF_FULL 1
#define BUF_FOUND 2
/*---------------------------------------------------------------------------*/
static void
buf_setup(struct psock_buf *buf,
u8_t *bufptr, u16_t bufsize)
{
buf->ptr = bufptr;
buf->left = bufsize;
}
/*---------------------------------------------------------------------------*/
static u8_t
buf_bufdata(struct psock_buf *buf, u16_t len,
u8_t **dataptr, u16_t *datalen)
{
if(*datalen < buf->left) {
memcpy(buf->ptr, *dataptr, *datalen);
buf->ptr += *datalen;
buf->left -= *datalen;
*dataptr += *datalen;
*datalen = 0;
return BUF_NOT_FULL;
} else if(*datalen == buf->left) {
memcpy(buf->ptr, *dataptr, *datalen);
buf->ptr += *datalen;
buf->left = 0;
*dataptr += *datalen;
*datalen = 0;
return BUF_FULL;
} else {
memcpy(buf->ptr, *dataptr, buf->left);
buf->ptr += buf->left;
*datalen -= buf->left;
*dataptr += buf->left;
buf->left = 0;
return BUF_FULL;
}
}
/*---------------------------------------------------------------------------*/
static u8_t
buf_bufto(register struct psock_buf *buf, u8_t endmarker,
register u8_t **dataptr, register u16_t *datalen)
{
u8_t c;
while(buf->left > 0 && *datalen > 0) {
c = *buf->ptr = **dataptr;
++*dataptr;
++buf->ptr;
--*datalen;
--buf->left;
if(c == endmarker) {
return BUF_FOUND;
}
}
if(*datalen == 0) {
return BUF_NOT_FOUND;
}
while(*datalen > 0) {
c = **dataptr;
--*datalen;
++*dataptr;
if(c == endmarker) {
return BUF_FOUND | BUF_FULL;
}
}
return BUF_FULL;
}
/*---------------------------------------------------------------------------*/
static char
send_data(register struct psock *s)
{
if(s->state != STATE_DATA_SENT || uip_rexmit()) {
if(s->sendlen > uip_mss()) {
uip_send(s->sendptr, uip_mss());
} else {
uip_send(s->sendptr, s->sendlen);
}
s->state = STATE_DATA_SENT;
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
static char
data_acked(register struct psock *s)
{
if(s->state == STATE_DATA_SENT && uip_acked()) {
if(s->sendlen > uip_mss()) {
s->sendlen -= uip_mss();
s->sendptr += uip_mss();
} else {
s->sendptr += s->sendlen;
s->sendlen = 0;
}
s->state = STATE_ACKED;
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
PT_THREAD(psock_send(register struct psock *s, const char *buf,unsigned int len))
{
PT_BEGIN(&s->psockpt);
/* If there is no data to send, we exit immediately. */
if(len == 0) {
PT_EXIT(&s->psockpt);
}
/* Save the length of and a pointer to the data that is to be
sent. */
s->sendptr = (const u8_t*)buf;
s->sendlen = len;
s->state = STATE_NONE;
/* We loop here until all data is sent. The s->sendlen variable is
updated by the data_sent() function. */
while(s->sendlen > 0) {
/*
* The condition for this PT_WAIT_UNTIL is a little tricky: the
* protothread will wait here until all data has been acknowledged
* (data_acked() returns true) and until all data has been sent
* (send_data() returns true). The two functions data_acked() and
* send_data() must be called in succession to ensure that all
* data is sent. Therefore the & operator is used instead of the
* && operator, which would cause only the data_acked() function
* to be called when it returns false.
*/
PT_WAIT_UNTIL(&s->psockpt, data_acked(s) & send_data(s));
}
s->state = STATE_NONE;
PT_END(&s->psockpt);
}
/*---------------------------------------------------------------------------*/
PT_THREAD(psock_generator_send(register struct psock *s,unsigned short (*generate)(void *), void *arg))
{
PT_BEGIN(&s->psockpt);
/* Ensure that there is a generator function to call. */
if(generate == NULL) {
PT_EXIT(&s->psockpt);
}
/* Call the generator function to generate the data in the
uip_appdata buffer. */
s->sendlen = generate(arg);
s->sendptr = uip_appdata;
s->state = STATE_NONE;
do {
/* Call the generator function again if we are called to perform a
retransmission. */
if(uip_rexmit()) {
generate(arg);
}
/* Wait until all data is sent and acknowledged. */
PT_WAIT_UNTIL(&s->psockpt, data_acked(s) & send_data(s));
} while(s->sendlen > 0);
s->state = STATE_NONE;
PT_END(&s->psockpt);
}
/*---------------------------------------------------------------------------*/
u16_t
psock_datalen(struct psock *psock)
{
return psock->bufsize - psock->buf.left;
}
/*---------------------------------------------------------------------------*/
char
psock_newdata(struct psock *s)
{
if(s->readlen > 0) {
/* There is data in the uip_appdata buffer that has not yet been
read with the PSOCK_READ functions. */
return 1;
} else if(s->state == STATE_READ) {
/* All data in uip_appdata buffer already consumed. */
s->state = STATE_BLOCKED_NEWDATA;
return 0;
} else if(uip_newdata()) {
/* There is new data that has not been consumed. */
return 1;
} else {
/* There is no new data. */
return 0;
}
}
/*---------------------------------------------------------------------------*/
PT_THREAD(psock_readto(register struct psock *psock, unsigned char c))
{
PT_BEGIN(&psock->psockpt);
buf_setup(&psock->buf, (u8_t*)psock->bufptr, psock->bufsize);
/* XXX: Should add buf_checkmarker() before do{} loop, if
incoming data has been handled while waiting for a write. */
do {
if(psock->readlen == 0) {
PT_WAIT_UNTIL(&psock->psockpt, psock_newdata(psock));
psock->state = STATE_READ;
psock->readptr = (u8_t *)uip_appdata;
psock->readlen = uip_datalen();
}
} while((buf_bufto(&psock->buf, c,
&psock->readptr,
&psock->readlen) & BUF_FOUND) == 0);
if(psock_datalen(psock) == 0) {
psock->state = STATE_NONE;
PT_RESTART(&psock->psockpt);
}
PT_END(&psock->psockpt);
}
/*---------------------------------------------------------------------------*/
PT_THREAD(psock_readbuf(register struct psock *psock))
{
PT_BEGIN(&psock->psockpt);
buf_setup(&psock->buf, (u8_t*)psock->bufptr, psock->bufsize);
/* XXX: Should add buf_checkmarker() before do{} loop, if
incoming data has been handled while waiting for a write. */
do {
if(psock->readlen == 0) {
PT_WAIT_UNTIL(&psock->psockpt, psock_newdata(psock));
printf("Waited for newdata\n");
psock->state = STATE_READ;
psock->readptr = (u8_t *)uip_appdata;
psock->readlen = uip_datalen();
}
} while(buf_bufdata(&psock->buf, psock->bufsize,
&psock->readptr,
&psock->readlen) != BUF_FULL);
if(psock_datalen(psock) == 0) {
psock->state = STATE_NONE;
PT_RESTART(&psock->psockpt);
}
PT_END(&psock->psockpt);
}
/*---------------------------------------------------------------------------*/
void
psock_init(register struct psock *psock, char *buffer, unsigned int buffersize)
{
psock->state = STATE_NONE;
psock->readlen = 0;
psock->bufptr = buffer;
psock->bufsize = buffersize;
buf_setup(&psock->buf, (u8_t*)buffer, buffersize);
PT_INIT(&psock->pt);
PT_INIT(&psock->psockpt);
}
/*---------------------------------------------------------------------------*/
4、附上uip中psock.h的源文件:
#ifndef __PSOCK_H__
#define __PSOCK_H__
#include "uipopt.h"
#include "pt.h"
/*
* The structure that holds the state of a buffer.
*
* This structure holds the state of a uIP buffer. The structure has
* no user-visible elements, but is used through the functions
* provided by the library.
*
*/
struct psock_buf {
u8_t *ptr;
unsigned short left;
};
/**
* The representation of a protosocket.
*
* The protosocket structrure is an opaque structure with no user-visible
* elements.
*/
struct psock {
struct pt pt, psockpt; /* Protothreads - one that's using the psock
functions, and one that runs inside the
psock functions. */
const u8_t *sendptr; /* Pointer to the next data to be sent. */
u8_t *readptr; /* Pointer to the next data to be read. */
char *bufptr; /* Pointer to the buffer used for buffering
incoming data. */
u16_t sendlen; /* The number of bytes left to be sent. */
u16_t readlen; /* The number of bytes left to be read. */
struct psock_buf buf; /* The structure holding the state of the
input buffer. */
unsigned int bufsize; /* The size of the input buffer. */
unsigned char state; /* The state of the protosocket. */
};
void psock_init(struct psock *psock, char *buffer, unsigned int buffersize);
/**
* Initialize a protosocket.
*
* This macro initializes a protosocket and must be called before the
* protosocket is used. The initialization also specifies the input buffer
* for the protosocket.
*
* \param psock (struct psock *) A pointer to the protosocket to be
* initialized
*
* \param buffer (char *) A pointer to the input buffer for the
* protosocket.
*
* \param buffersize (unsigned int) The size of the input buffer.
*
* \hideinitializer
*/
#define PSOCK_INIT(psock, buffer, buffersize) \
psock_init(psock, buffer, buffersize)
/**
* Start the protosocket protothread in a function.
*
* This macro starts the protothread associated with the protosocket and
* must come before other protosocket calls in the function it is used.
*
* \param psock (struct psock *) A pointer to the protosocket to be
* started.
*
* \hideinitializer
*/
#define PSOCK_BEGIN(psock) PT_BEGIN(&((psock)->pt))
PT_THREAD(psock_send(struct psock *psock, const char *buf, unsigned int len));
/**
* Send data.
*
* This macro sends data over a protosocket. The protosocket protothread blocks
* until all data has been sent and is known to have been received by
* the remote end of the TCP connection.
*
* \param psock (struct psock *) A pointer to the protosocket over which
* data is to be sent.
*
* \param data (char *) A pointer to the data that is to be sent.
*
* \param datalen (unsigned int) The length of the data that is to be
* sent.
*
* \hideinitializer
*/
#define PSOCK_SEND(psock, data, datalen) \
PT_WAIT_THREAD(&((psock)->pt), psock_send(psock, data, datalen))
/**
* \brief Send a null-terminated string.
* \param psock Pointer to the protosocket.
* \param str The string to be sent.
*
* This function sends a null-terminated string over the
* protosocket.
*
* \hideinitializer
*/
#define PSOCK_SEND_STR(psock, str) \
PT_WAIT_THREAD(&((psock)->pt), psock_send(psock, str, strlen(str)))
PT_THREAD(psock_generator_send(struct psock *psock,
unsigned short (*f)(void *), void *arg));
/**
* \brief Generate data with a function and send it
* \param psock Pointer to the protosocket.
* \param generator Pointer to the generator function
* \param arg Argument to the generator function
*
* This function generates data and sends it over the
* protosocket. This can be used to dynamically generate
* data for a transmission, instead of generating the data
* in a buffer beforehand. This function reduces the need for
* buffer memory. The generator function is implemented by
* the application, and a pointer to the function is given
* as an argument with the call to PSOCK_GENERATOR_SEND().
*
* The generator function should place the generated data
* directly in the uip_appdata buffer, and return the
* length of the generated data. The generator function is
* called by the protosocket layer when the data first is
* sent, and once for every retransmission that is needed.
*
* \hideinitializer
*/
#define PSOCK_GENERATOR_SEND(psock, generator, arg) \
PT_WAIT_THREAD(&((psock)->pt), \
psock_generator_send(psock, generator, arg))
/**
* Close a protosocket.
*
* This macro closes a protosocket and can only be called from within the
* protothread in which the protosocket lives.
*
* \param psock (struct psock *) A pointer to the protosocket that is to
* be closed.
*
* \hideinitializer
*/
#define PSOCK_CLOSE(psock) uip_close()
PT_THREAD(psock_readbuf(struct psock *psock));
/**
* Read data until the buffer is full.
*
* This macro will block waiting for data and read the data into the
* input buffer specified with the call to PSOCK_INIT(). Data is read
* until the buffer is full..
*
* \param psock (struct psock *) A pointer to the protosocket from which
* data should be read.
*
* \hideinitializer
*/
#define PSOCK_READBUF(psock) \
PT_WAIT_THREAD(&((psock)->pt), psock_readbuf(psock))
PT_THREAD(psock_readto(struct psock *psock, unsigned char c));
/**
* Read data up to a specified character.
*
* This macro will block waiting for data and read the data into the
* input buffer specified with the call to PSOCK_INIT(). Data is only
* read until the specifieed character appears in the data stream.
*
* \param psock (struct psock *) A pointer to the protosocket from which
* data should be read.
*
* \param c (char) The character at which to stop reading.
*
* \hideinitializer
*/
#define PSOCK_READTO(psock, c) \
PT_WAIT_THREAD(&((psock)->pt), psock_readto(psock, c))
/**
* The length of the data that was previously read.
*
* This macro returns the length of the data that was previously read
* using PSOCK_READTO() or PSOCK_READ().
*
* \param psock (struct psock *) A pointer to the protosocket holding the data.
*
* \hideinitializer
*/
#define PSOCK_DATALEN(psock) psock_datalen(psock)
u16_t psock_datalen(struct psock *psock);
/**
* Exit the protosocket's protothread.
*
* This macro terminates the protothread of the protosocket and should
* almost always be used in conjunction with PSOCK_CLOSE().
*
* \sa PSOCK_CLOSE_EXIT()
*
* \param psock (struct psock *) A pointer to the protosocket.
*
* \hideinitializer
*/
#define PSOCK_EXIT(psock) PT_EXIT(&((psock)->pt))
/**
* Close a protosocket and exit the protosocket's protothread.
*
* This macro closes a protosocket and exits the protosocket's protothread.
*
* \param psock (struct psock *) A pointer to the protosocket.
*
* \hideinitializer
*/
#define PSOCK_CLOSE_EXIT(psock) \
do { \
PSOCK_CLOSE(psock); \
PSOCK_EXIT(psock); \
} while(0)
/**
* Declare the end of a protosocket's protothread.
*
* This macro is used for declaring that the protosocket's protothread
* ends. It must always be used together with a matching PSOCK_BEGIN()
* macro.
*
* \param psock (struct psock *) A pointer to the protosocket.
*
* \hideinitializer
*/
#define PSOCK_END(psock) PT_END(&((psock)->pt))
char psock_newdata(struct psock *s);
/**
* Check if new data has arrived on a protosocket.
*
* This macro is used in conjunction with the PSOCK_WAIT_UNTIL()
* macro to check if data has arrived on a protosocket.
*
* \param psock (struct psock *) A pointer to the protosocket.
*
* \hideinitializer
*/
#define PSOCK_NEWDATA(psock) psock_newdata(psock)
/**
* Wait until a condition is true.
*
* This macro blocks the protothread until the specified condition is
* true. The macro PSOCK_NEWDATA() can be used to check if new data
* arrives when the protosocket is waiting.
*
* Typically, this macro is used as follows:
*
\code
PT_THREAD(thread(struct psock *s, struct timer *t))
{
PSOCK_BEGIN(s);
PSOCK_WAIT_UNTIL(s, PSOCK_NEWADATA(s) || timer_expired(t));
if(PSOCK_NEWDATA(s)) {
PSOCK_READTO(s, '\n');
} else {
handle_timed_out(s);
}
PSOCK_END(s);
}
\endcode
*
* \param psock (struct psock *) A pointer to the protosocket.
* \param condition The condition to wait for.
*
* \hideinitializer
*/
#define PSOCK_WAIT_UNTIL(psock, condition) \
PT_WAIT_UNTIL(&((psock)->pt), (condition));
#define PSOCK_WAIT_THREAD(psock, condition) \
PT_WAIT_THREAD(&((psock)->pt), (condition))
#endif /* __PSOCK_H__ */
/** @} */