tcp和socket有什麼關係,與http又有什麼聯繫?用最直白的語言從頭到腳爲你講清楚

最近項目中須要本身去實現一個http的接口。因此趁這個機會跟你們講一下http和socket的關係,以及與TCP又有什麼聯繫。linux

首先你們必定要明確一點,在網絡分層架構當中,HTTP協議是屬於應用層的,tcp協議是屬於傳輸層的,也就是說它們是一種協議,是通訊雙方規定的一種規則,沒有這種規則,兩臺主機就沒法完成通訊。json

而根據咱們曾經所學的知識能夠知道,兩臺主機要完成通訊,必須在傳輸層規定一套相同的協議,至於要不要在傳輸層就創建鏈接,因協議而異,tcp協議是須要創建鏈接的,而udp就不須要。至於tcp和udp的區別,不在本文的討論範圍,因此暫時不論。由於如今傳輸數據大部分都是使用tcp協議,因此tcp協議是很是重要的,必需要掌握。api

tcp和socket有什麼關係,與http又有什麼聯繫?

 

傳輸層使用tcp協議發送數據的話,首先要完成TCP的三次握手過程。爲何要完成三次握手過程?爲了保證數據傳輸的準確性,就是讓數據能夠準確無誤的傳輸到另外一臺主機。至於如何完成三次握手過程,這個知識點在網上有很是多的資料你們能夠去百度看看。服務器

而三次握手創建鏈接,這更像是一種理論的過程,也就是說我告訴你三次握手的過程,可是你要幫我實現這個過程,那怎麼實現呢?這個具體實現的過程就是靠socket來實現的,socket是操做系統爲tcp封裝的一整套創建鏈接,發送數據,斷開鏈接的過程,它是對外提供的一個接口。注意我這裏說的是操做系統,也就是說不一樣的操做系統封裝的socket接口函數可能有所不一樣,這一點你們須要注意。在linux上使用最多的socket函數通常有socket()bind()listen()accept()connect()close()這幾個函數,在window上略有不一樣。網絡

到這裏不知道你們明白了沒有,tcp只是傳輸層上的一個協議,是通訊雙方互相規定的一種協議,而socket就是這種協議的具體實現過程。因此若是你足夠牛逼,你能夠本身給通訊雙方的兩臺主機制定一套屬於本身的傳輸層協議,而後本身寫代碼實現這個過程。但通常沒有人會這麼作,爲何呢?由於這個工做量很是的恐怖,這個恐怖不是體如今制定協議以及寫代碼實現的這個過程,這個恐怖是體如今必須爲通訊雙方的兩臺機子都適配這種協議。服務端還好說,是都是本身的機子,控制權都在本身手上,並且通常都只使用linux系統,可是到客戶端就完全宕機了,客戶端確定不是就一臺的,是千千萬萬臺,並且還有不一樣的操做系統,你要不就本身去一個個系統去適配你的協議,要不就是去斡旋各大操做系統廠商寫入你的協議。因此這樣的事也只有全球有影響力的企業,有影響力的組織才能夠完成的,通常人不可能,也不必。架構

tcp和socket有什麼關係,與http又有什麼聯繫?

 

上面說了那麼多,就是告訴同窗們,通訊雙方要完成通訊,要先在傳輸層利用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)

相關文章
相關標籤/搜索