最近項目中須要本身去實現一個http的接口。因此趁這個機會跟你們講一下http和socket的關係,以及與TCP又有什麼聯繫。linux
首先你們必定要明確一點,在網絡分層架構當中,HTTP協議是屬於應用層的,tcp協議是屬於傳輸層的,也就是說它們是一種協議,是通訊雙方規定的一種規則,沒有這種規則,兩臺主機就沒法完成通訊。json
而根據咱們曾經所學的知識能夠知道,兩臺主機要完成通訊,必須在傳輸層規定一套相同的協議,至於要不要在傳輸層就創建鏈接,因協議而異,tcp協議是須要創建鏈接的,而udp就不須要。至於tcp和udp的區別,不在本文的討論範圍,因此暫時不論。由於如今傳輸數據大部分都是使用tcp協議,因此tcp協議是很是重要的,必需要掌握。api
傳輸層使用tcp協議發送數據的話,首先要完成TCP的三次握手過程。爲何要完成三次握手過程?爲了保證數據傳輸的準確性,就是讓數據能夠準確無誤的傳輸到另外一臺主機。至於如何完成三次握手過程,這個知識點在網上有很是多的資料你們能夠去百度看看。服務器
而三次握手創建鏈接,這更像是一種理論的過程,也就是說我告訴你三次握手的過程,可是你要幫我實現這個過程,那怎麼實現呢?這個具體實現的過程就是靠socket來實現的,socket是操做系統爲tcp封裝的一整套創建鏈接,發送數據,斷開鏈接的過程,它是對外提供的一個接口。注意我這裏說的是操做系統,也就是說不一樣的操做系統封裝的socket接口函數可能有所不一樣,這一點你們須要注意。在linux上使用最多的socket函數通常有socket()bind()listen()accept()connect()close()這幾個函數,在window上略有不一樣。網絡
到這裏不知道你們明白了沒有,tcp只是傳輸層上的一個協議,是通訊雙方互相規定的一種協議,而socket就是這種協議的具體實現過程。因此若是你足夠牛逼,你能夠本身給通訊雙方的兩臺主機制定一套屬於本身的傳輸層協議,而後本身寫代碼實現這個過程。但通常沒有人會這麼作,爲何呢?由於這個工做量很是的恐怖,這個恐怖不是體如今制定協議以及寫代碼實現的這個過程,這個恐怖是體如今必須爲通訊雙方的兩臺機子都適配這種協議。服務端還好說,是都是本身的機子,控制權都在本身手上,並且通常都只使用linux系統,可是到客戶端就完全宕機了,客戶端確定不是就一臺的,是千千萬萬臺,並且還有不一樣的操做系統,你要不就本身去一個個系統去適配你的協議,要不就是去斡旋各大操做系統廠商寫入你的協議。因此這樣的事也只有全球有影響力的企業,有影響力的組織才能夠完成的,通常人不可能,也不必。架構
上面說了那麼多,就是告訴同窗們,通訊雙方要完成通訊,要先在傳輸層利用tcp協議創建鏈接。鏈接創建完成以後,就能夠開始發送數據了,那麼接着問題來了。app
1、若是我要發送不一樣結構,不一樣規則的數據的話,我要怎麼發。socket
2、我發出去的數據,確定會收到一個回覆,那麼我怎麼處理這個回來的數據。tcp
若是以上兩個問題,你們不是很明白,不要緊,接下去往下看,你可能就明白了。函數
基於以上兩個問題,就須要在應用層上制定一套屬於通訊雙方本身的協議了,而這套協議是規定雙方發送接收的數據規則。http就是應用層一個很是經典的協議,它是因特網上應用最爲普遍的一種網絡傳輸協議,全部的WWW文件都必須遵照這個標準。固然應用層協議不只僅只有http,還有telnet,ftp,smtp等等這些都是很是經典的應用層協議,通訊雙方都必須按照協議規定的數據格式來發送和接收。並且根據雙方發送數據的需求,還能夠制定屬於本身的應用層協議,來知足本身的本地化需求。只要你有需求,應用層協議隨便你添加。
那麼爲何添加傳輸層協議難如登天,而添加應用層協議卻那麼簡單呢?
簡單一句話歸納就是:傳輸層協議是操做系統級別的,而應用層協議是應用軟件級別的。
因此添加一個傳輸層協議必定是一個浩大的工程,由於要在操做系統級別上更新。而添加一個應用層協議就比較簡單了,由於只是添加在你所開發的軟件或者app的客戶端和服務端上。
用最生活化的例子來比喻,假如要從A地到B地,那麼怎麼過去呢,確定須要修建一條路,那麼修建這條路所須要的設計圖紙就至關於tcp,而工人們修建的過程就至關於socket,不能盲目修建,必須基於設計圖紙來修建,而socket也必須基於tcp協議的理論,而修建一條道路是耗資巨大的工程,因此不可能隨便的添加傳輸層協議。一旦道路修建完成,你能夠採用各類方式過去,走路、跑步、騎自行車、開小車等等均可以,只要你開心,你要爬過去均可以,而採用何種方式過去就是應用層協議,http是其中的一種過去方式。
總的來講,tcp是傳輸層的一個協議,而socket是這個協議的封裝,能夠對外提供接口,讓應用程序調用,而http是應用層的一個協議,是一種對數據的封裝。發起http請求的時候,底層的傳輸層要完成兩臺機子的鏈接,就是tcp三次握手完成鏈接。
如下我給出了一個http封裝的例子,只有客戶端,是當時作項目的時候寫,你們能夠參考參考
1 //http接口 2 int http_post_openapi(const char *pIP, const char *pServ, int port, const char *pSendValue, char *pRecvValue) 3 { 4 int sockfd, ret, i, h; 5 struct sockaddr_in servaddr; 6 char szHttpHead[1024], buf[8192], szSendValueLength[128],szSendBuffer[4096]; 7 int iLen = 0; 8 fd_set t_set1; 9 struct timeval tv; 10 11 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) { 12 printf("建立網絡鏈接失敗,本線程即將終止---socket error!\n"); 13 exit(0); 14 }; 15 16 memset(&servaddr, 0, sizeof(servaddr)); 17 servaddr.sin_family = AF_INET; 18 servaddr.sin_port = htons(port); 19 if (inet_pton(AF_INET, pIP, &servaddr.sin_addr) <= 0 ){ 20 printf("建立網絡鏈接失敗,本線程即將終止--inet_pton error!\n"); 21 exit(0); 22 }; 23 24 if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){ 25 printf("鏈接到服務器失敗,connect error!\n"); 26 exit(0); 27 } 28 printf("與遠端創建了鏈接\n"); 29 30 if (pSendValue == NULL || pRecvValue == NULL) 31 { 32 close(sockfd); 33 printf("\n傳入的pSendValue或者pRecvValue爲空!!!\n"); 34 return -1; 35 } 36 37 char szSendConver[4096] = {0}; 38 TrimAll(pSendValue, szSendConver); 39 40 iLen = strlen(szSendConver); 41 42 memset(szSendValueLength, 0, 128); 43 sprintf(szSendValueLength, "%d", iLen); 44 printf("szSendValueLength after sprintf is :%s\n", szSendValueLength); 45 46 memset(szHttpHead, 0, 256); 47 48 strcat(szHttpHead, "POST "); 49 strcat(szHttpHead, pServ); 50 strcat(szHttpHead, " HTTP/1.1\r\n"); 51 strcat(szHttpHead, "Host: "); 52 strcat(szHttpHead, pIP); 53 strcat(szHttpHead, ":"); 54 char cPort[6]; 55 sprintf(cPort,"%ld",port); 56 strcat(szHttpHead, cPort); 57 strcat(szHttpHead, "\r\n"); 58 strcat(szHttpHead, "User-Agent: Apache-HttpClient/4.1.1\r\n"); 59 strcat(szHttpHead, "Accept: */*\r\n"); 60 strcat(szHttpHead, "Content-Length: "); 61 strcat(szHttpHead, szSendValueLength); 62 strcat(szHttpHead, "\r\n"); 63 strcat(szHttpHead, "Content-Type: application/json; charset=UTF-8"); 64 printf("szSendValueLength is :%s\n", szSendValueLength); 65 66 strcat(szHttpHead, "\r\n\r\n"); 67 memset(szSendBuffer, 0, 4096); 68 strcat(szSendBuffer, szHttpHead); 69 strcat(szSendBuffer, szSendConver); 70 71 strcat(szSendBuffer, "\r\n\r\n"); 72 printf("Print SendBuffer before write :\n%s\n",szSendBuffer); 73 74 ret = write(sockfd,szSendBuffer,strlen(szSendBuffer)); 75 if (ret < 0) { 76 printf("發送失敗!錯誤代碼是%d,錯誤信息是'%s'\n",errno, strerror(errno)); 77 exit(0); 78 }else{ 79 printf("消息發送成功,共發送了%d個字節!\n\n", ret); 80 } 81 82 FD_ZERO(&t_set1); 83 FD_SET(sockfd, &t_set1); 84 85 memset(buf, 0, sizeof(buf)); 86 i= read(sockfd, buf, sizeof(buf)-1); 87 if (i==0) 88 { 89 close(sockfd); 90 printf("讀取數據報文時發現遠端關閉,該線程終止!\n"); 91 return -1; 92 } 93 94 close(sockfd); 95 //在此處找到HTTP的RESPONSE結果碼,若是爲200,則成功,截取包體賦值到pRecvBuff;不然返回-1。 96 char *pRet = NULL; 97 char *pStart = NULL; 98 char *pEnd = NULL; 99 //pRet = strstr(buf, "HTTP/1.1 200 OK"); 100 pRet = strstr(buf, "HTTP/1.1 200"); 101 if(!pRet) 102 { 103 cout << "HTTP Response Error!!!" << endl; 104 return -1; 105 } 106 107 string sRecv; 108 utf82gb(buf, sRecv); 109 printf("sRecv is :\n%s\n", sRecv.c_str()); 110 111 memset(buf, 0, sizeof(buf)); 112 strncpy(buf, sRecv.c_str(), sRecv.length()); 113 114 pStart = strstr(buf, "{"); 115 printf("pStart address is :0x%x\n", pStart); 116 117 pEnd = strrchr(buf, '}') + 1; 118 printf("pEnd address is :0x%x\n", pStart); 119 // 120 121 if(pStart != NULL && pEnd != NULL) 122 { 123 strncpy(pRecvValue, pStart, (int)(pEnd - pStart)); 124 //userlog("消息返回成功-消息,請求消息:\n%s\n ******** 返回消息:\n%s\n", szSendBuffer, buf); 125 } 126 else 127 { 128 printf("\nbuf中未找到匹配的數據!!!\n"); 129 userlog("消息返回失敗-消息,請求消息:\n%s\n ******** 返回消息:\n%s\n", szSendBuffer, buf); 130 return -2; 131 } 132 133 return 0; 134 }
更多精彩內容,請關注同名公衆:一點月光(alittle-moon)