基于NicheStack协议栈的网络例程分析及客户端程序设计 【转】

标签:
niosii |
分类: niosII |
一、摘要
分析基于NicheStack协议栈的网络例程,重点分析了simple_socket_server.c文件,阐述网络通信的过程,最后,完成了基于C#的上位机网络通信应用程序。
二、实验平台
软件平台:Quartus
硬件平台:DIY_DE2
三、基于NicheStack协议栈的网络例程分析
首先,明确两个概念:
服务器端:FPGA端,
客户端:PC端。
1、工程文件解读
本例程需要的工程文件有以下几种,下面对其意义及作用做了说明:
(1)alt_error_handler.h、alt_error_handler.c:错误类型句柄文件;
(2)dm9000a_regs.h、dm9000a.h、dm9000a.c:DM9000A的驱动;
(3)network_utilities.h、network_utilities.c:设置IP,设置MAC;
(4)simple_socket_server.h、simple_socket_server.c:工程的主体程序,包括任务调度优先级、缺省IP设置、套接字、各种任务调度等等工作;
(5)led.c:LED、七段数码管显示程序;
(6)iniche_init.c:程序主函数。
例程的主程序理解起来就较为简单,其过程为:初始化uC/OS
2、simple_socket_server.c文件解读
首先看几个函数:
void
套接字任务的创建。创建2个任务:LED和七段数码管显示任务。
void
连接初始化。
void
发送菜单。连接建立后,回传给客户端菜单信息。
void
套接字连接函数。侧重网络连接。
void
命令函数。显示LED、七段数码管。
void
套接字接收数据函数。侧重接收数据。
void
套接字任务主函数。
其中,套接字任务函数里面,给出了相关函数的调用顺序:
http://pic002.cnblogs.com/images/2012/314385/2012042215380824.jpg【转】" />
下面着重分析一下接收数据函数:
- void
sss_handle_receive(SSSConn* conn) - {
-
data_used = 0, rx_code = 0; -
INT8U *lf_addr; -
-
conn->rx_rd_pos = conn->rx_buffer; -
conn->rx_wr_pos = conn->rx_buffer; -
-
printf( processing );RX data\n" -
-
!= CLOSE) -
{ -
-
lf_addr = strchr(conn->rx_buffer, -
-
-
{ -
-
printf( -
-
sss_exec_command(conn); -
} -
-
-
{ -
rx_code = recv(conn->fd, conn->rx_wr_pos, -
SSS_RX_BUF_SIZE - (conn->rx_wr_pos - conn->rx_buffer) -1, 0); -
-
> 0) -
{ -
conn->rx_wr_pos += rx_code; -
-
-
*(conn->rx_wr_pos+1) = 0; -
} -
} -
-
conn->state = conn->close ? CLOSE : READY; -
-
data_used = conn->rx_rd_pos - conn->rx_buffer; -
memmove(conn->rx_buffer, conn->rx_rd_pos, -
conn->rx_wr_pos - conn->rx_rd_pos); -
conn->rx_rd_pos = conn->rx_buffer; -
conn->rx_wr_pos -= data_used; -
memset(conn->rx_wr_pos, 0, data_used); -
} -
printf( closing );connection\n" -
close(conn->fd); -
sss_reset_connection(conn); -
-
- }
程序主要采用指针操作的形式,首先看指针的定义及初始化:
simple_socket_server.h中:
INT8U
INT8U
simple_socket_server.c中:
conn->rx_wr_pos
显然,conn->rx_buffer是指向数组rx_buffer的首地址,初始化后,读指针rx_rd_pos和写指针rx_wr_pos也指向了数组rx_buffer的首地址,如下图所示。
http://pic002.cnblogs.com/images/2012/314385/2012042215470527.jpg【转】" />
第14行
lf_addr
这里是搜索得到的字符串中是否有转义符'\n',即查询客户端输入数据后,是否有回车。如果有'\n',则返回一个非零值;否则,返回0。因此,除了在客户端发送数据外,还要跟一个'\n'。
第24~36行
else
}
这里用到recv函数,函数原型为:
int
第一个参数
第二个参数指明
第三个参数指明
第四个参数一般置0。
根据上面的定义,可以得知:
(1)conn->fd为接收端套接字描述符,
(2)conn->rx_wr_pos为一个缓冲区,即使用rx_buffer数组,且从首地址开始存储数据,
(3)SSS_RX_BUF_SIZE
recv函数的返回值是,一次传输完成后,rx_buffer接收到的字节数。由于网卡是16bit模式,所以,接收到的数据是以2个字节为单位,即rx_code为2的整数倍。
下面的if语句就比较简单了,如果有新的数据收到,则写指针rx_wr_pos向数组rx_buffer移动rx_code个字节,并把下一个字节里面的内容清零,以方便存储转义符'\n'。
第45~50行
memset(conn->rx_wr_pos,
存储区管理:
data_used
把已经读取的数据的字节数赋值给data_used
memmove(conn->rx_buffer,
这里用到了拷贝字符串函数memmove(memmove与memcpy的区别可参考相关文档),memmove的函数原型为:
*dest
*src
n
由一个图示来解释程序中的memmove语句,如下图所示。
http://pic002.cnblogs.com/images/2012/314385/2012042412055385.jpg【转】" />
左右侧分别为运行内存管理语句前后存储区状态。
其中,
memmove(conn->rx_buffer,
为拷贝存储区内容,
conn->rx_rd_pos
和
conn->rx_wr_pos
为重新调整读指针和写指针指向。
最后,
memset(conn->rx_wr_pos,
memset函数给一段内存赋初值,从rx_wr_pos所指的地址开始的data_used个字节。
总结一下void
四、基于C#的PC端网络应用程序
前面博文介绍了PC端CMD下输入telent命令的方式,这里应用Visual
http://pic002.cnblogs.com/images/2012/314385/2012042215545873.jpg【转】" />
注:本例程来自网络,后经调试使用。
五、总结
该篇博文分析了服务器端(FPGA端)网络任务处理过程,并完成服务器端与客户端(PC端)的网络通信。最后,可以根据自己的需要修改客户端和服务器端程序