有關于TCP協(xié)議的知識在上一章已經(jīng)有過描述,這里我們直接使用API來實現(xiàn)TCP服務器模式。
27.1 實驗例程
27.1.1 tcp_server.c代碼編寫
#include "tcp_server.h"
#include "delay.h"
#include "usart1.h"
#include "lcd.h"
#include "malloc.h"
#include "string.h"
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/memp.h"
#include "lwip/mem.h"
#include "comm.h"
//TCP Server 測試全局狀態(tài)標記變量
//bit7:0,沒有數(shù)據(jù)要發(fā)送;1,有數(shù)據(jù)要發(fā)送
//bit6:0,沒有收到數(shù)據(jù);1,收到數(shù)據(jù)了.
//bit5:0,沒有客戶端連接上;1,有客戶端連接上了.
//bit4~0:保留
u8 tcp_server_flag;
//關閉tcp連接
void tcp_server_connection_close( struct tcp_pcb *tpcb, struct tcp_server_struct *es )
{
tcp_close( tpcb ) ;
tcp_arg( tpcb, NULL ) ;
tcp_sent( tpcb, NULL ) ;
tcp_recv( tpcb, NULL ) ;
tcp_err( tpcb, NULL ) ;
tcp_poll( tpcb, NULL, 0 ) ;
if( es )
mem_free( es ) ;
tcp_server_flag &= ~( 1<<5 ) ; //標記連接斷開了
}
// tcp_recv函數(shù)的回調(diào)函數(shù)
u8 tcp_server_recvbuf[ TCP_SERVER_RX_BUFSIZE ] ; //TCP Server接收數(shù)據(jù)緩沖區(qū)
err_t tcp_server_recv( void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err )
{
err_t ret_err ;
u32 data_len = 0 ;
struct pbuf *q ;
struct tcp_server_struct *es ;
LWIP_ASSERT( "arg != NULL", arg != NULL ) ;
es = ( struct tcp_server_struct* )arg ;
//從客戶端接收到空數(shù)據(jù)
if( p==NULL )
{
es->state = ES_TCPSERVER_CLOSING ; //需要關閉TCP連接了
es->p = p ;
ret_err = ERR_OK ;
}
//從客戶端接收到一個非空數(shù)據(jù),但是由于某種原因err!=ERR_OK
else if( err!=ERR_OK )
{
if( p )
pbuf_free( p ) ; //釋放接收pbuf
ret_err = err ;
}
//處于連接狀態(tài)
else if( es->state==ES_TCPSERVER_ACCEPTED )
{
//當處于連接狀態(tài)并且接收到的數(shù)據(jù)不為空時將其打印出來
if( p!=NULL )
{
memset( tcp_server_recvbuf, 0, TCP_SERVER_RX_BUFSIZE ) ; //數(shù)據(jù)接收緩沖區(qū)清零
//遍歷完整個pbuf鏈表
for( q=p; q!=NULL; q=q->next )
{
if( q->len>( TCP_SERVER_RX_BUFSIZE-data_len ) )
memcpy( tcp_server_recvbuf+data_len, q->payload, TCP_SERVER_RX_BUFSIZE-data_len ) ;
else
memcpy(tcp_server_recvbuf+data_len, q->payload, q->len ) ;
data_len += q->len ;
//超出TCP客戶端接收數(shù)組,跳出
if( data_len>TCP_SERVER_RX_BUFSIZE )
break ;
}
tcp_server_flag |= 1<<6 ; //標記接收到數(shù)據(jù)了
lwipdev.remoteip[ 0 ] = tpcb->remote_ip.addr&0xFF ; //IADDR4
lwipdev.remoteip[ 1 ] = ( tpcb->remote_ip.addr>>8 )&0xFF ; //IADDR3
lwipdev.remoteip[ 2 ] = ( tpcb->remote_ip.addr>>16 )&0xFF ; //IADDR2
lwipdev.remoteip[ 3 ] = ( tpcb->remote_ip.addr>>24 )&0xFF ; //IADDR1
tcp_recved( tpcb, p->tot_len ) ; //用于獲取接收數(shù)據(jù)
pbuf_free( p ) ; //釋放內(nèi)存
ret_err = ERR_OK ;
}
}
//服務器關閉了
else
{
tcp_recved( tpcb, p->tot_len ); //用于獲取接收數(shù)據(jù)
es->p= NULL ;
pbuf_free( p ) ; //釋放內(nèi)存
ret_err = ERR_OK ;
}
return ret_err ;
}
// tcp_err函數(shù)的回調(diào)函數(shù)
void tcp_server_error( void *arg, err_t err )
{
LWIP_UNUSED_ARG( err ) ;
printf( "tcp error:%x\\r\\n", ( u32 )arg ) ;
//釋放內(nèi)存
if( arg!=NULL )
mem_free( arg ) ;
}
//發(fā)送數(shù)據(jù)
void tcp_server_senddata( struct tcp_pcb *tpcb, struct tcp_server_struct *es )
{
struct pbuf *ptr ;
u16 plen ;
err_t wr_err = ERR_OK ;
while( ( wr_err==ERR_OK )&&( es->p )&&( es->p->len<=tcp_sndbuf( tpcb ) ) )
{
ptr = es->p ;
wr_err = tcp_write( tpcb, ptr->payload, ptr->len, 1 ) ;
if( wr_err==ERR_OK )
{
plen = ptr->len ;
es->p = ptr->next ; //指向下一個pbuf
//pbuf的ref加一
if( es->p )
pbuf_ref( es->p ) ;
pbuf_free( ptr ) ;
tcp_recved( tpcb, plen ) ; //更新tcp窗口大小
}
else if( wr_err==ERR_MEM )
es->p = ptr ;
}
}
// tcp_poll的回調(diào)函數(shù)
const u8 *tcp_server_sendbuf = "STM32F103 TCP Server send data\\r\\n" ; //TCP服務器發(fā)送數(shù)據(jù)內(nèi)容
err_t tcp_server_poll( void *arg, struct tcp_pcb *tpcb )
{
err_t ret_err;
struct tcp_server_struct *es ;
es = ( struct tcp_server_struct* )arg ;
if( es!=NULL )
{
//判斷是否有數(shù)據(jù)要發(fā)送
if( tcp_server_flag&( 1<<7 ) )
{
es->p = pbuf_alloc( PBUF_TRANSPORT, strlen( ( char* )tcp_server_sendbuf ), PBUF_POOL ) ;
pbuf_take( es->p, ( char* )tcp_server_sendbuf, strlen( ( char* )tcp_server_sendbuf ) ) ;
tcp_server_senddata( tpcb, es ) ; //輪詢的時候發(fā)送要發(fā)送的數(shù)據(jù)
tcp_server_flag &= ~( 1<<7 ) ; //清除數(shù)據(jù)發(fā)送標志位
if( es->p!=NULL )
pbuf_free( es->p ) ; //釋放內(nèi)存
}
//關閉操作
else if( es->state==ES_TCPSERVER_CLOSING )
tcp_server_connection_close( tpcb, es ) ; //關閉連接
ret_err = ERR_OK ;
}
else
{
tcp_abort( tpcb ) ; //終止連接,刪除pcb控制塊
ret_err = ERR_ABRT ;
}
return ret_err ;
}
// tcp_sent的回調(diào)函數(shù)(遠端收到ACK信號后發(fā)送數(shù)據(jù))
err_t tcp_server_sent( void *arg, struct tcp_pcb *tpcb, u16_t len )
{
struct tcp_server_struct *es ;
LWIP_UNUSED_ARG( len ) ;
es = ( struct tcp_server_struct * )arg ;
if( es->p )
tcp_server_senddata( tpcb, es ) ; //發(fā)送數(shù)據(jù)
return ERR_OK ;
}
//強制刪除主動斷開時的time wait
extern void tcp_pcb_purge( struct tcp_pcb *pcb ) ; //在 tcp.c里面
extern struct tcp_pcb *tcp_active_pcbs ; //在 tcp.c里面
extern struct tcp_pcb *tcp_tw_pcbs ; //在 tcp.c里面
void tcp_server_remove_timewait()
{
struct tcp_pcb *pcb, *pcb2 ;
while( tcp_active_pcbs!=NULL )
{
lwip_periodic_handle() ; //繼續(xù)輪詢
lwip_pkt_handle() ;
delay_ms( 10 ) ; //等待tcp_active_pcbs為空
}
pcb = tcp_tw_pcbs ;
//如果有等待狀態(tài)的pcbs
while( pcb!=NULL )
{
tcp_pcb_purge( pcb ) ;
tcp_tw_pcbs = pcb->next ;
pcb2 = pcb ;
pcb = pcb->next ;
memp_free( MEMP_TCP_PCB, pcb2 ) ;
}
}
// tcp_accept的回調(diào)函數(shù)
err_t tcp_server_accept( void *arg, struct tcp_pcb *newpcb, err_t err )
{
err_t ret_err ;
struct tcp_server_struct *es ;
LWIP_UNUSED_ARG( arg ) ;
LWIP_UNUSED_ARG( err ) ;
tcp_setprio( newpcb, TCP_PRIO_MIN ) ; //設置新創(chuàng)建的pcb優(yōu)先級
es = ( struct tcp_server_struct* )mem_malloc( sizeof( struct tcp_server_struct ) ) ; //分配內(nèi)存
//內(nèi)存分配成功
if( es!=NULL )
{
es->state = ES_TCPSERVER_ACCEPTED ; //接收連接
es->pcb = newpcb ;
es->p = NULL ;
tcp_arg( newpcb, es ) ;
tcp_recv( newpcb, tcp_server_recv ) ; //初始化tcp_recv的回調(diào)函數(shù)
tcp_err( newpcb, tcp_server_error ) ; //初始化tcp_err回調(diào)函數(shù)
tcp_poll( newpcb, tcp_server_poll, 1 ) ; //初始化tcp_poll回調(diào)函數(shù)
tcp_sent( newpcb, tcp_server_sent ) ; //初始化發(fā)送回調(diào)函數(shù)
tcp_server_flag |= 1<<5 ; //標記有客戶端連上了
lwipdev.remoteip[ 0 ] = newpcb->remote_ip.addr&0xFF ; //IADDR4
lwipdev.remoteip[ 1 ] = ( newpcb->remote_ip.addr>>8 )&0xFF ; //IADDR3
lwipdev.remoteip[ 2 ] = ( newpcb->remote_ip.addr>>16 )&0xFF ; //IADDR2
lwipdev.remoteip[ 3 ] = ( newpcb->remote_ip.addr>>24 )&0xFF ; //IADDR1
ret_err = ERR_OK ;
}
else
ret_err = ERR_MEM ;
return ret_err ;
}
// TCP Server 測試
void tcp_server_test()
{
err_t err ;
struct tcp_pcb *tcppcbnew ; //定義一個TCP服務器控制塊
struct tcp_pcb *tcppcbconn ; //定義一個TCP服務器控制塊
u8 *tbuf ;
u8 res=0 ;
u8 connflag=0 ; //連接標記
tbuf = mymalloc( SRAMIN, 200 ) ; //申請內(nèi)存
//內(nèi)存申請失敗了,直接退出
if( tbuf==NULL )
return ;
sprintf( ( char* )tbuf, "Server IP:%d.%d.%d.%d", lwipdev.ip[0], lwipdev.ip[1], lwipdev.ip[2], lwipdev.ip[3] ) ;
LCD_ShowString( 30, 130, tbuf ) ; //服務器IP
sprintf( ( char* )tbuf, "Server Port:%d", TCP_SERVER_PORT ) ;
LCD_ShowString( 30, 150, tbuf ) ; //服務器端口號
tcppcbnew = tcp_new() ; //創(chuàng)建一個新的pcb
//創(chuàng)建成功
if( tcppcbnew )
{
err = tcp_bind( tcppcbnew, IP_ADDR_ANY, TCP_SERVER_PORT ) ; //將本地IP與指定端口號綁定
//綁定完成
if( err==ERR_OK )
{
tcppcbconn = tcp_listen( tcppcbnew ) ; //設置tcppcb進入監(jiān)聽狀態(tài)
tcp_accept( tcppcbconn, tcp_server_accept ) ; //初始化tcp_accept的回調(diào)函數(shù)
}
else
res = 1 ;
}
else
res = 1 ;
while( res==0 )
{
//收到數(shù)據(jù)
if( tcp_server_flag&1<<6 )
{
tcp_server_flag |= 1<<7 ; //標記要發(fā)送數(shù)據(jù)
LCD_ShowString( 30, 210, tcp_server_recvbuf ) ; //顯示接收到的數(shù)據(jù)
tcp_server_flag &= ~( 1<<6 ) ; //標記數(shù)據(jù)已經(jīng)被處理了
}
//是否連接上
if( tcp_server_flag&1<<5 )
{
if( connflag==0 )
{
sprintf( ( char* )tbuf, "Client IP:%d.%d.%d.%d", lwipdev.remoteip[0], lwipdev.remoteip[1], lwipdev.remoteip[2], lwipdev.remoteip[3] ) ;
LCD_ShowString( 30, 170, tbuf ) ; //客戶端IP
LCD_ShowString( 30, 190, "Receive Data:" ) ; //提示消息
connflag = 1 ; //標記連接了
}
}
else if( connflag )
connflag = 0 ; //標記連接斷開了
lwip_periodic_handle() ;
lwip_pkt_handle() ;
delay_ms( 2 ) ;
}
tcp_server_connection_close( tcppcbnew, 0 ) ; //關閉TCP Server連接
tcp_server_connection_close( tcppcbconn, 0 ) ; //關閉TCP Server連接
tcp_server_remove_timewait() ;
memset( tcppcbnew, 0, sizeof( struct tcp_pcb ) ) ;
memset( tcppcbconn, 0, sizeof( struct tcp_pcb ) ) ;
myfree( SRAMIN, tbuf ) ;
}
27.1.2 tcp_server.h代碼編寫
#ifndef _TCP_SERVER_DEMO_H_
#define _TCP_SERVER_DEMO_H_
#include "sys.h"
#include "lwip/tcp.h"
#include "lwip/pbuf.h"
#define TCP_SERVER_RX_BUFSIZE 2000 //定義tcp server最大接收數(shù)據(jù)長度
#define TCP_SERVER_PORT 8088 //定義tcp server的端口
//tcp服務器連接狀態(tài)
enum tcp_server_states
{
ES_TCPSERVER_NONE = 0, //沒有連接
ES_TCPSERVER_ACCEPTED, //有客戶端連接上了
ES_TCPSERVER_CLOSING, //即將關閉連接
};
//LWIP回調(diào)函數(shù)使用的結(jié)構體
struct tcp_server_struct
{
u8 state; //當前連接狀
struct tcp_pcb *pcb; //指向當前的pcb
struct pbuf *p; //指向接收/或傳輸?shù)膒buf
};
void tcp_server_test( void ) ; //TCP Server測試函數(shù)
#endif
27.1.3 主函數(shù)代碼編寫
#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "tim.h"
#include "lcd.h"
#include "malloc.h"
#include "dm9000.h"
#include "lwip/netif.h"
#include "comm.h"
#include "lwipopts.h"
#include "tcp_server.h"
int main()
{
u8 buf[ 30 ];
STM32_Clock_Init( 9 ) ; //系統(tǒng)時鐘設置
SysTick_Init( 72 ) ; //延時初始化
USART1_Init( 72, 115200 ) ; //串口初始化為115200
LCD_Init() ; //初始化LCD
TIM3_Init( 1000, 719 ) ; //定時器3頻率為100hz
my_mem_init( SRAMIN ) ; //初始化內(nèi)部內(nèi)存池
while( lwip_comm_init() ) ; //lwip初始化
//等待DHCP獲取成功/超時溢出
while( ( lwipdev.dhcpstatus!=2 )&&( lwipdev.dhcpstatus!=0xFF ) )
{
lwip_periodic_handle() ; //LWIP內(nèi)核需要定時處理的函數(shù)
lwip_pkt_handle() ;
}
POINT_COLOR=RED;
LCD_ShowString( 30, 110, "LWIP Init Successed" ) ;
//打印動態(tài)IP地址
if( lwipdev.dhcpstatus==2 )
sprintf( ( char* )buf, "DHCP IP:%d.%d.%d.%d", lwipdev.ip[0], lwipdev.ip[1], lwipdev.ip[2], lwipdev.ip[3] ) ;
//打印靜態(tài)IP地址
else
sprintf( ( char* )buf, "Static IP:%d.%d.%d.%d", lwipdev.ip[0], lwipdev.ip[1], lwipdev.ip[2], lwipdev.ip[3] ) ;
LCD_ShowString( 30, 130, buf ) ;
//得到網(wǎng)速
if( ( DM9000_Get_SpeedAndDuplex()&0x02 )==0x02 )
LCD_ShowString( 30, 150, "Ethernet Speed:10M" ) ;
else
LCD_ShowString( 30, 150, "Ethernet Speed:100M" ) ;
while( 1 )
{
tcp_server_test() ; //TCP服務器測試
lwip_periodic_handle() ;
lwip_pkt_handle() ;
delay_ms( 2 ) ;
}
}
27.2 實驗結(jié)果
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。
舉報投訴
-
服務器
+關注
關注
12文章
9304瀏覽量
86068 -
API
+關注
關注
2文章
1511瀏覽量
62397 -
TCP
+關注
關注
8文章
1378瀏覽量
79303
發(fā)布評論請先 登錄
相關推薦
串口服務器——TCP Server
。在實際應用中,我們經(jīng)常會遇到需要將串口數(shù)據(jù)轉(zhuǎn)發(fā)到TCP服務器或?qū)?b class='flag-5'>TCP服務器數(shù)據(jù)轉(zhuǎn)發(fā)到串口的情況,所以本文將介紹如何實現(xiàn)串口接入TCP
LABVIEW實現(xiàn)網(wǎng)絡通信的方法
︱Variable選項,然后根據(jù)提示一步一步即可完成設置,但需要注意的是要實現(xiàn)網(wǎng)絡通信變量類型必須選擇為“Network-Published”。這里在服務器中創(chuàng)建一個名為Server_Variable的共享
發(fā)表于 12-12 18:02
四種在LabVIEW中實現(xiàn)網(wǎng)絡通信的方法
方便的實現(xiàn)數(shù)據(jù)的共享。用戶無需了解任何的底層復雜的網(wǎng)絡通信,就能輕松地實現(xiàn)數(shù)據(jù)交換。用戶建立和使用共享變量就如同操作全局變量一樣方便。 4.2 利用共享變量通信實例 以下通過C/S(客戶端/服務器
發(fā)表于 02-04 15:32
基于LABVIEW實現(xiàn)網(wǎng)絡通信的方法
任何的底層復雜的網(wǎng)絡通信,就能輕松地實現(xiàn)數(shù)據(jù)交換。用戶建立和使用共享變量就如同操作全局變量一樣方便。4.2 利用共享變量通信實例 以下通過C/S(客戶端/服務器)通信模式實現(xiàn)數(shù)據(jù)的傳
發(fā)表于 04-28 10:04
為什么網(wǎng)絡通信實驗用TCP客戶端模式時連接不上?
戰(zhàn)艦V3上的實驗50 網(wǎng)絡通信實驗,當用TCP服務器模式時,通信正常,用TCP客戶端模式時連接不
發(fā)表于 08-16 04:35
如何才能讓網(wǎng)絡通信實驗支持10個UDP鏈接?
大家好,我在使用探索者STM32F4開發(fā)板,寄存器例程,實驗55 網(wǎng)絡通信實驗。我修改了例程,想創(chuàng)建10個UDP端口監(jiān)聽,udppcb=udp_new()這里最多只能創(chuàng)建5個,5個之后
發(fā)表于 08-27 22:48
為什么網(wǎng)絡通信實驗在網(wǎng)絡助手上接受到的先是456個字節(jié)?
在網(wǎng)絡通信實驗里將ARMF407配置成sever,通過電腦上網(wǎng)絡助手接受ARM發(fā)送到數(shù)據(jù),將tcp_sever_sendbuf[1000]改成一個1000的大數(shù)組并賦值,但是在網(wǎng)絡助手
發(fā)表于 09-02 02:54
STM32F103上網(wǎng)絡通信實驗中服務器與客戶端連接但不能進行數(shù)據(jù)交換該怎么辦?
我在應用原子哥的103板子的網(wǎng)絡通信實驗時,發(fā)現(xiàn)了一個小問題,就是在服務器模式下,板子可以與很多的客戶端建立連接,但是在進行數(shù)據(jù)交換時,卻只能和第一個連上的客戶端進行數(shù)據(jù)交換,只有在于這個客戶端斷開
發(fā)表于 10-15 04:37
為什么f4+ucosiii+lwip1.4.1連接服務器會返回錯誤碼-10?
f4+ucosiii+lwip1.4.1+lan8720采用原子哥的開發(fā)板帶操作系統(tǒng)的移植代碼,網(wǎng)絡部分的執(zhí)行代碼也是以開發(fā)板例子“實驗55 網(wǎng)絡通信實驗”為基礎移植的然后開發(fā)板作為c
發(fā)表于 10-16 01:47
請問探索者網(wǎng)絡通信實驗的控制網(wǎng)頁該怎么寫呢?
原子哥雖然有比較詳細的講解了網(wǎng)絡通信實驗,但是對于那個控制網(wǎng)頁怎么寫的卻沒有過多的涉及,請問有誰會寫這種控制網(wǎng)頁的,誠心求教!
發(fā)表于 11-06 04:35
基于原子STM32F4的攝像頭與網(wǎng)絡通信實驗
應單片機課設要求,做了一個攝像頭拍照網(wǎng)絡通信C/S實時LCD顯示。該工程基于原子STM32F4的攝像頭與網(wǎng)絡通信實驗,在此基礎上,將其整合。1.預期功能:攝像頭拍取的內(nèi)容實時傳輸至LCD進行顯示通過
發(fā)表于 08-03 06:04
基于TCP/IP的網(wǎng)絡通信應用程序分享
-IP網(wǎng)絡通信應用程序(TCP-Client)本章節(jié),將以服務端的角色進行講解,如何開發(fā)一個TCP服務端(
發(fā)表于 12-23 08:12
有線網(wǎng)絡通信實驗3之TCP客戶端
,TCP先把數(shù)據(jù)流分割成適當長度的報文段,最大傳輸段的長度MSS通常受計算機連接的網(wǎng)絡的數(shù)據(jù)鏈路層的最大傳輸單元MTU控制,之后TCP才把數(shù)據(jù)包傳給IP層,通過它來將數(shù)據(jù)傳送給接收端的TCP
評論