從硬體底層通訊看http協議和https協議的資料流
嵌入式硬體與伺服器的通訊常常採用http協議或是https協議,實際上https協議就是http協議+SSL加密通訊,簡單點說就是把http協議的資料經過一定的演算法加密後傳送出去,接收方收到後再解密出來。http協議是執行在tcp協議的上一層,也就是說協議資料是在TCP資料的流的基礎上再進行編碼獲得。接觸過乙太網底層程式設計的同學都熟悉socket程式設計,那麼一段資料按http協議進行編碼,呼叫write()函式寫入網路socket上,對方收到的資料就是http資料。
http協議是一種無連線,無狀態的連線協議,無連線的意思是說,需要通訊時才建議連線(TCP連線),通訊完成後一般關閉連結。無狀態協議的話就是一次性,只有一個狀態,不像TCP協議一樣,通訊過程中有各種狀態之間依次變換才能完成通訊。協議對話的發起由客戶端發出請求給伺服器,伺服器根據客戶端發來的資料進行響應。
客戶端請求訊息
客戶端傳送一個HTTP請求到伺服器的請求訊息包括以下格式:請求行(request line)、請求頭部(header)、空行和請求資料四個部分組成,下圖給出了請求報文的一般格式。
伺服器響應訊息
HTTP響應由四部分組成,分別是狀態行、訊息報頭,空行和響應正文。
http協議佔用TCP通訊的80埠號,https協議通訊佔用TCP通訊的443埠號。上面只是介紹了協議,可能你還是不太明白網路上的資料流是什麼樣子的。一個示例如下:
/*請百度伺服器*/ #define SIMPLE_GET_REQUEST \ "GET / HTTP/1.1\r\n" \ "Host: www.baidu.com\r\n" \ "Connection: close\r\n" \ "\r\n" /*簡單的HTTP請求*/ void simple_http_get( char* host, char* query ) { OSStatus err; int client_fd = -1; fd_set readfds; char ipstr[16]; struct sockaddr_in addr; HTTPHeader_t *httpHeader = NULL; http_context_t context = { NULL, 0 }; struct hostent* hostent_content = NULL; char rcv_buf[1024]; char **pptr = NULL; struct in_addr in_addr; hostent_content = gethostbyname( host ); require_action_quiet( hostent_content != NULL, exit, err = kNotFoundErr); pptr=hostent_content->h_addr_list; in_addr.s_addr = *(uint32_t *)(*pptr); strcpy( ipstr, inet_ntoa(in_addr)); http_client_log("HTTP server address: %s, host ip: %s", host, ipstr); /*HTTPHeaderCreateWithCallback set some callback functions */ httpHeader = HTTPHeaderCreateWithCallback( 1024, onReceivedData, onClearData, &context ); require_action( httpHeader, exit, err = kNoMemoryErr ); client_fd = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); addr.sin_family = AF_INET; addr.sin_addr = in_addr; addr.sin_port = htons(80); err = connect( client_fd, (struct sockaddr *)&addr, sizeof(addr) ); require_noerr_string( err, exit, "connect http server failed" ); /* Send HTTP Request */ send( client_fd, query, strlen( query ), 0 ); FD_ZERO( &readfds ); FD_SET( client_fd, &readfds ); select( client_fd + 1, &readfds, NULL, NULL, NULL ); if ( FD_ISSET( client_fd, &readfds ) ) { n= read(client_fd, rcv_buf, sizeof(rcv_buf)) } }
void main(void)
{
simple_http_get("www.baidu.com", SIMPLE_GET_REQUEST);
}
通過上面的示例就不難看出,http請求百度伺服器就是發出一個GET請求了,請求的編碼就是按照協議寫一些字串以換行結尾,之後把這些編碼好的字串通過write()函式寫入到百度伺服器的80埠上就可以了。
"GET / HTTP/1.1\r\n" \
"Host: www.baidu.com\r\n" \
"Connection: close\r\n" \
"\r\n"