瀏覽器更新HTTP伺服器圖片並顯示

WIZnet發表於2017-08-23
1         實驗目的

瀏覽器登入W5500伺服器顯示一張圖片,再通過檔案上傳一個圖片更新伺服器中的圖片;重新整理瀏覽器顯示更新後的圖片。

2         實驗總體設計

實驗主要分兩個部分:1:瀏覽器申請圖片資料以及上傳圖片資料:;

2:W5500伺服器接收資料以及處理HTTP請求和資料。

3         實驗原理

該實現的原理過程就是先利用取模軟體獲取一個圖片的十六進位制資料陣列,然後W5500伺服器將資料寫入FLASH裡面並記下寫入的位置,主函式迴圈檢測瀏覽器客戶端是否傳送TCP連線請求,如果檢測到請求報文伺服器就會與客戶端建立連線並傳輸第一包資料;第一包資料內包含一段有瀏覽器執行的HTML程式碼,瀏覽器獲取第一包資料後開始執行HTML程式碼,即瀏覽器向伺服器傳送第一張圖片資料請求,伺服器返回圖片的資料並由瀏覽器顯示在網頁上面,此時的圖片是我們第一次寫入FLASH的圖片資料,而伺服器返回資料的過程就是根據瀏覽器的第二個請求中的URL找到圖片並從FLASH讀取出來放在HTTP響應報文中傳送給瀏覽器,瀏覽器再顯示在網頁。

圖片更新的部分就是瀏覽器顯示第一張圖片後會根據我們編寫的HTML程式碼在網頁上顯示一個選擇按鈕以及一個文字框,該部分是HTML利用FORM表單來上傳檔案到我們的伺服器;我們在這裡使用的就是將我們的圖片以檔案的形式上傳到伺服器,伺服器根據表單的請求方式以及路徑來接收圖片資料並將資料寫入內部FLASH,由於我們使用的W5500開發板的內部FLASH是256K(經過測試300K的照片也能寫入,原因可能是內部FLASH實際上是512K的,這個可能是ST的問題在這裡不做解釋)的,所有我們把更新的圖片直接覆蓋掉原來的圖片資料;這樣也比較方便我們獲取資料;資料寫入後我們會在給瀏覽器的響應報文中加入一段HTML以及JAVASCRIPT的程式碼,以便於讓我們的瀏覽器重新整理頁面並從新傳送圖片請求來獲取更新後的圖片並顯示在頁面。

4         內部FLASH操作
4.1          STM32RCT6內部FLASH

STM32F103RCT6為大容量產品,其FLASH的大小是256K,所以一共可以分為128頁,每頁得大小是2K,地址範圍0X0800 0000-0X0803 FFFF;內部FLASH是存放我們下載的程式的,所以要把內部FLASH當做儲存器來使用的話就要考慮寫入地址與存放程式碼的地址是否重合,這裡我把前64頁放我的程式碼,從64頁開始用來存放我寫入的資料;即開始寫入或讀取的地址是0X0802 0000。(根據這裡的解釋我們每次更新的圖片的大小應該小於64K,但是我上面說過了實際測試的時候是可以上傳300K左右的資料的)

 

 

內部FLASH讀寫程式碼

 

uint16_t Flash_Write_Without_check(uint32_t iAddress, uint8_t *buf, uint16_t iNumByteToWrite)

{

uint16_t i=0;

while((i < iNumByteToWrite) && (FLASHStatus == FLASH_COMPLETE))

{

FLASHStatus = FLASH_ProgramHalfWord(iAddress, *(uint16_t*)buf);

i = i+2;

iAddress = iAddress + 2;

buf = buf + 2;

}

return iNumByteToWrite;

}

 

int Flash_Write(uint32_t iAddress, uint8_t *buf, uint32_t iNbrToWrite)

{

uint32_t secpos;

uint32_t iNumByteToWrite = iNbrToWrite;

uint16_t secoff;

uint16_t secremain;

uint16_t i = 0;

static uint8_t tmp[FLASH_PAGE_SIZE];

FLASH_UnlockBank1();

secpos=iAddress & (~(FLASH_PAGE_SIZE -1 )) ;//扇區地址

secoff=iAddress & (FLASH_PAGE_SIZE -1);     //在扇區內的偏移

secremain=FLASH_PAGE_SIZE-secoff;           //扇區剩餘空間大小

 

if(iNumByteToWrite<=secremain) secremain = iNumByteToWrite;//不大於4096個位元組

 

while(1)

{

ReadFlashNBtye(secpos, tmp, FLASH_PAGE_SIZE);   //讀出整個扇區

for(i=0;i

{

if(tmp[secoff+i]!=0XFF)break;       //需要擦除

}

if(i

{

FLASHStatus = FLASH_ErasePage(secpos); //擦除這個扇區

if(FLASHStatus != FLASH_COMPLETE)

return -1;

for(i=0;i

{

tmp[i+secoff]=buf[i];   //複製

}

Flash_Write_Without_check(secpos ,tmp ,FLASH_PAGE_SIZE);//寫入整個扇區

}

else

{

Flash_Write_Without_check(iAddress,buf,secremain);//寫已經擦除了的,直接寫入扇區剩餘區間.

}

 

if(iNumByteToWrite==secremain) //寫入結束了

break;

else

{

secpos += FLASH_PAGE_SIZE;

secoff = 0;//偏移位置為0

buf += secremain;  //指標偏移

iAddress += secremain;//寫地址偏移

iNumByteToWrite -= secremain;  //位元組數遞減

if(iNumByteToWrite>FLASH_PAGE_SIZE) secremain=FLASH_PAGE_SIZE;//下一個扇區還是寫不完

else secremain = iNumByteToWrite;  //下一個扇區可以寫完了

}

}

 

FLASH_LockBank1();

return iNbrToWrite;

}

 

 

 

int ReadFlashNBtye(uint32_t ReadAddress, uint8_t *ReadBuff, uint32_t ReadNum)

{

int DataNum=0;     //定義一個變數記錄讀取的個數

while(DataNum

{

*(ReadBuff+DataNum)=*(__IO uint8_t*)ReadAddress++;  //讀一個資料地址加一

DataNum++;

}

return DataNum;

}

4.2          HTML部分程式碼

#define INDEX_HTML  ""\

""\

""\

"

"\

""\

""\

"

"\

"

This is a test

"\

""\

"

Pitcure Update

"\

"

"\

"

"\

"

"\

"

"\

"

"\
4.3          HTTP狀態機

該部分是迴圈檢測HTTP請求,獲取請求後經過解析函式獲取請求頭資訊以及URL和資料的型別與長度,再根據這些資料來選擇執行下一步操作,例如接收或者傳送圖片資料;

void do_https(void)

{

uint8 ch=SOCK_HTTPS;     

uint16 len;

st_http_request *http_request;                                                                                                                    

memset(rx_buf,0x00,MAX_URI_SIZE);                      

http_request = (st_http_request*)rx_buf;  

 

switch(getSn_SR(ch))  

{

case SOCK_INIT:     

listen(ch);

break;

 

case SOCK_LISTEN:

break;

 

case SOCK_ESTABLISHED:        

if(getSn_IR(ch) & Sn_IR_CON)

{

setSn_IR(ch, Sn_IR_CON);         

}

if ((len = getSn_RX_RSR(ch)) > 0)

{

len = recv(ch, (uint8*)http_request, len);                                 

*(((uint8*)http_request)+len) = 0;

 

proc_http(ch, (uint8*)http_request);     

disconnect(ch);                                  

}

break;

 

case SOCK_CLOSE_WAIT:             

if ((len = getSn_RX_RSR(ch)) > 0)

{

len = recv(ch, (uint8*)http_request, len);       

*(((uint8*)http_request)+len) = 0;

proc_http(ch, (uint8*)http_request);     

}

disconnect(ch);

break;

 

case SOCK_CLOSED:                                      

socket(ch, Sn_MR_TCP, 80, 0x00);                         

break;

 

default:

break;

}

}

4.4          HTTP響應部分

void proc_http(SOCKET s, uint8 * buf)

{

int8* name;

int8 req_name[32]={0x00,};

unsigned long file_len=0;

uint16 send_len=0;

uint8* http_response;                                    

int8 sub[10],numbuff[5];

uint32 content_len=0,rx_len=0;

uint16 fw_offset = 0;

uint16 wr_len = 0;

uint32 upload_file_len=0;

uint32 n_pages;

uint32 n_erased;

uint8 remain_len = 0;

uint16 i=0;

uint16 tmp_len=0;

uint8 remain_buf[3]={0xff,};

uint32 flash_dest =STARTADDR;

uint32 calc_checksum = 0;

st_http_request *http_request;              

memset(tx_buf,0x00,MAX_URI_SIZE);         

http_response = (uint8*)rx_buf;              

http_request = (st_http_request*)tx_buf;      

parse_http_request(http_request, buf);   

switch (http_request->METHOD)

{

case METHOD_ERR :                                           

memcpy(http_response, ERROR_REQUEST_PAGE, sizeof(ERROR_REQUEST_PAGE));

send(s, (uint8 *)http_response, strlen((int8 const*)http_response)); 

break;

 

case METHOD_HEAD:                                                                                                                                                

 

case METHOD_GET:                                             

name = http_request->URI;                  

if(strcmp(name,"/index.htm")==0||strcmp(name,"/")==0||(strcmp(name,"/index.html")==0)) 

{

file_len = strlen(INDEX_HTML);           

make_http_response_head((uint8*)http_response, PTYPE_HTML,file_len);

send(s,http_response,strlen((char const*)http_response)); 

send_len=0;

while(file_len)

{

if(file_len>1024)

{

if(getSn_SR(s)!=SOCK_ESTABLISHED)

{

return;

}

send(s, (uint8 *)INDEX_HTML+send_len, 1024);

send_len+=1024;

file_len-=1024;

}

else

{

send(s, (uint8 *)INDEX_HTML+send_len, file_len);

send_len+=file_len;

file_len-=file_len;

}

}

}

else if(strcmp(name,"/img.jpg")==0)

{

send_img_jpg_page(s, http_response);

}

else if(strcmp(name,"/w5500.js")==0)

{

memset(tx_buf,0,MAX_URI_SIZE);

make_basic_config_setting_json_callback(tx_buf,ConfigMsg);

sprintf((char*)http_response,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length:%d\r\n\r\n%s",strlen(tx_buf),tx_buf);

send(s, (u_char *)http_response, strlen((char const*)http_response));

}

break;

 

case METHOD_POST:                                                                                         

mid(http_request->URI, "/", " ", req_name);                           

if(strcmp(req_name,"pitcure.cgi")==0)

{

mid((int8*)http_request->URI,"boundary=", "\r\n", (int8*)boundary);//boundary=

#ifdef DEBUG_HTTP

printf("boundary=%s\r\n",boundary);

#endif

 

memset(sub,0,10);

mid((char*)http_request->URI,"Content-Length: ","\r\n",sub);

content_len=atoi32(sub,10);

#ifdef DEBUG_HTTP

printf("content_len=%d\r\n",(uint32_t)content_len);

ee_WriteBytes((uint8_t *)sub,(uint16_t)STARTADDRDATA,strlen((char *)sub));

delay_ms(5);

memset(numbuff,0,5);

numbuff[0]=strlen((char *)sub);

ee_WriteBytes((uint8_t *)numbuff,(uint16_t)STARTADDRDATA+2,1);

#endif

if(content_len<0xffff7)//長度小於1M

{

while(rx_len!=content_len)

{

tmp_len=getSn_RX_RSR(s);

if(tmp_len>0)

{

if(tmp_len>1460) tmp_len=1460;

tmp_len=recv(s, (uint8*)rx_buf, tmp_len);

if(rx_len==0)//the first packet with header

{

int8* pos1;

int8* pos2;

uint16 hdr_len;

#ifdef DEBUG_HTTP

printf("http request=%d\r\n",strlen((int8*)rx_buf));

#endif

pos1=strstr((int8*)rx_buf,(int8*)boundary);

 

#ifdef DEBUG_HTTP

printf("pos1=%d, %s\r\n", strlen(pos1),pos1);

#endif

pos2=strstr((int8*)rx_buf,"\r\n\r\n");

hdr_len=strlen((int8*)boundary)+6+2+(pos2-pos1)+4+2;

upload_file_len=content_len-hdr_len;

#ifdef DEBUG_HTTP

printf("pos2=%d,boundarylen=%d,uploadfilelen=%d\r\n",pos2-pos1,strlen((int8*)boundary),upload_file_len);

#endif

fw_offset = pos2-pos1+4+2;

FLASH_Unlock();

FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);

//erase required page

n_pages = FLASH_PagesMask(upload_file_len);

for(n_erased = 0; n_erased < n_pages; n_erased++)

{

FLASH_ErasePage(STARTADDR + 0x400 * n_erased);

IWDG_ReloadCounter();//feed wdg

}

 

wr_len = tmp_len-fw_offset;

//make a buf with lengh of multiful 4 and save remain bytes

remain_len = wr_len % 4;

memcpy(tmp_buf,&rx_buf[fw_offset],wr_len-remain_len);

wr_len = wr_len - remain_len;//real length

if(remain_len!=0)

{

memcpy(remain_buf,&rx_buf[tmp_len-remain_len],remain_len);

}

 

}else{

if(rx_len+tmp_len==content_len)//the last packet that includes boundary

{

wr_len = tmp_len - strlen((int8*)boundary) - 8;

}else{

wr_len = tmp_len;

}

fw_offset=0;

//make buffer

if(remain_len)

{

memcpy(tmp_buf,remain_buf,remain_len);

}

if(wr_len+remain_len>1460)

{

memcpy(&tmp_buf[remain_len],rx_buf,wr_len-remain_len);

//save remain bytes

memcpy(remain_buf,&rx_buf[tmp_len-remain_len],remain_len);

}else{

memcpy(&tmp_buf[remain_len],rx_buf, wr_len);

memset(&tmp_buf[wr_len+remain_len],0xff,3);

wr_len=wr_len+remain_len;

}

}

 

//program

for(i = 0; i

{

FLASH_ProgramWord(flash_dest, *(uint32*)((uint32)(tmp_buf + i)));

//checksum

calc_checksum += *(uint32*)(flash_dest);

flash_dest += 4;

}

rx_len+=tmp_len;

 

#ifdef DEBUG_HTTP

printf("len=%d, rx len=%d, wr len=%d, @x\r\n",tmp_len, rx_len, wr_len, flash_dest);

#endif

//program over

if(rx_len==content_len)

{

//lock flash again

FLASH_Lock();

make_delay_jump_msg(tx_buf,"The pitcure is updated...",5);

sprintf((char *)http_response,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length:%d\r\n\r\n%s",strlen(tx_buf),tx_buf);

send(s, (uint8 *)http_response, strlen((char *)http_response));

}

 

}

}

}// while 結束

}//end of if(firmware upload)

break;

 

default :

break;

}

}

4.5          傳送圖片給瀏覽器

該部分是在伺服器接收到瀏覽器的圖片請求後從FLASH讀取圖片資料然後傳送給瀏覽器;瀏覽器顯示。

void send_img_jpg_page(SOCKET s, uint8 * buf)

{

uint32 file_len = 0;

uint32 send_len = 0,addr=0;

uint16 len = 0,lenth=0;

uint8  ReadBuff[ReadBuffsize];

uint8  img_size[10],numbuff[5];;

 

memset(numbuff,0,5);

ee_ReadBytes(numbuff,(uint16)STARTADDRDATA+2,1);

printf("num=%d",numbuff[0]);

memset(img_size,0,10);

ee_ReadBytes(img_size,(uint16)STARTADDRDATA,numbuff[0]);

delay_ms(10);

file_len=atoi32((char *)img_size,10);

printf("file_len=%d\r\n",(int)file_len);

make_http_response_head((unsigned char*)buf,PTYPE_JPEG, (u_long)file_len);

send(s, buf, strlen((char const*)buf));

while(file_len!=0)

{

memset(ReadBuff,0,ReadBuffsize);

lenth=ReadFlashNBtye(STARTADDR+addr,ReadBuff,ReadBuffsize);

delay_ms(5);

printf("%d",lenth);

send_len=0;

if(file_len)

{

if(file_len>1024)

{

if(getSn_SR(s)!=SOCK_ESTABLISHED)

{

return;

}

len=send(s, (uint8 *)ReadBuff, 1024);

delay_ms(5);

send_len+=len;

file_len-=len;

addr+=len;

printf("%d",lenth);

}

else

{

send(s, (uint8 *)ReadBuff, file_len);

send_len+=file_len;

file_len-=file_len;

printf("file_len=%d",(int)file_len);

}

}

}

return;

}

5         總結

該實驗其實是一個比較簡單的實驗;只要會寫簡單的HTML網頁描述語言來告訴瀏覽器執行圖片顯示以及上傳的操作,然後伺服器根據接收到的HTTP報文來選擇讀取或寫入圖片資料並將資料傳送給瀏覽器就可以了。最主要的就是我們的W5500開發板的使用,通過呼叫開發板自帶的SCOKET介面函式來接收和傳送HTTP報文,然後在呼叫解析函式解析報文再根據解析的內容選擇執行響應的操作,在W5500開發板中這些關於乙太網的介面函式都已經編寫好了,我們只需要呼叫這些函式然後將接收到的資料或者存放在記憶體的資料傳送到瀏覽器就可以了。

 

 


相關文章