一、網絡編程介紹
c++編程的應用場景在前面分析過,一個重要的方向就是網絡編程。一般來說,開發者說的服務端編程在c++方向上簡單的可以認為是網絡編程。首先需要說明的,本系列不對網絡編程的相關基礎知識展開詳細的說明,因為這種知識在書本上太多了。網絡上各種資料更是滿開飛,沒有必要拷貝來拷貝去的。特別是一些協議等的解析說明,如果不遇到特定的問題不會深入分析說明。
那么最應該明白的網絡編程是什么?那么就得明白網絡是如何而來。網絡,從名字上很好理解,一張把“經絡”連接起來的大網。不過這個經絡不是人體中的“經絡”而是一個個節點,這個節點可以是虛擬的,也可以是物理的,也可以是混合的。它可以是一臺電腦,一個手機,一個終端,也可以是一個局域網、城域網等等。
計算機的網絡技術在計算機技術中算是比較早期的一種技術了,在60年代的中期就已經開始在實際應用了。但真正的普及是美國的國防用網絡。早期學習電腦的或者看過早期電影的,都聽說過某某黑客特別厲害,進入了五角大樓的網絡,盜取了不少軍事資料。可以這樣講,計算機的網絡技術也是從美國開始興起的,然后在全世界開始普及。這也解釋了為什么現在最牛的互聯網公司基本都在美國的一個重要原因。比如耳熟能詳的谷歌、微軟、臉書以及推特等等。
隨著PC的出現和發展,局域網(LAN)出現。美國Xerox公司首先推出了Ethernet網,慢慢其成為了一種標準,大家都稱現在的局域網絡為以太網。有了局域就會有廣域網WAN。不過需要說明的是,所謂局域與廣域是一個相對概念,請大家一定要根據實際場景來確定。
網絡技術其實就是處理PC間連接通信的技術。從物理上講,如何識別網絡中的PC,如何與其它PC交換數據等等。首先需要用物理導線將各個PC連接起來,一開始是電纜,后來光纜,再后來又有無線技術。然后還要有路由器和交換機把數據將有的傳送到指定的PC。而為了實現上述的功能,就需要一系列的通信標準和通信協議。這就引出了網絡協議的五層模型(七層就是個學術的東西,沒啥實際應用的意義)。而這個模型中,則包含是最常見的網絡編程中的TCP/IP、UDP、HTTP等最常見的網絡編程技術。或者說的不準確一些,對大多數的網絡編程人員來說,就是TCP/IP和UDP編程。在移動互聯網中,HTTP則更為普及的被使用。至于其它的技術,基本都是相當專業的人員或者特定領域的開發者才會使用。
二、基本知識
這里不談較老的技術和很新的技術,比如QUIC和HTTP3等。在網絡編程中,可以分成兩大類應用,即B/S開發和C/S開發(P2P以后專門講),這里只談C/S開發。即本系列主要針對C/S開發中的TCP/IP編程以及UDP的編程。只要掌握了它們的編程,其它的編程基本都差不多。在TCP/IP和UDP編程中,需要掌握一些基本的知識:
1、服務器
這個概念是一個非常容易混淆的概念,一定要區別在不同的語境和環境下的定義。在網絡編程的語境下,一般是指承載網絡服務軟件的服務器電腦(硬件)。它可以分成網絡內部自用,比如路由器、交換機等也可以只提供某種網絡服務的電腦如打印服務器、郵箱服務器等。
2、服務端
服務端或服務端軟件,也可以叫網絡服務,在特定到C/S編程中,就是指提供連接服務的程序。一般來說,服務端是被接收連接的。
3、客戶端
客戶端在C/S編程中指發起連接的一端。
4、協議棧
協議棧(Protocol stack),又稱協議堆疊,是計算機網絡協議套件的一個具體的軟件實現。
5、伯克利套接字
伯克利套接字(Berkeley sockets),也稱為BSD Socket。其是一種使用C語言實現的網絡編程抽象接口。現在幾乎成為了互聯網通信的標準接口。
6、五元組和三元組:
五元組包括:源IP地址,源端口,目的IP地址,目的端口和傳輸層協議。這等同于現實世界中的人和人之間的通信地址。
7、協議族
socket函數中的第一個參數中意義,也叫協議域。通常有AF_INET、AF_INET6、AF_LOCAL(或稱AF_UNIX,Unix域socket)、AF_ROUTE等。協議族確定socket的地址類型,即雙方必須使用相同的通信類型才可以進行以通信。如常見的AF_INET需要用32位(類似192.168.0.1)ipv4地址與16位端口號(最大65535)的組合、AF_UNIX需要用一個絕對路徑名作為地址。
當然,還有很多的基礎性的知識和名詞術語。網絡技術是一個發展了很多年的技術,它既成熟又年輕。舉一個簡單的例子,當有一個人說他是搞服務端編程的,如何確定他的技術棧?其實這個定義非常難確定,網上一些大牛的說明其實也不能夠完全覆蓋相關的內容,即他們的定義也是不嚴謹的。但僅從經驗和學識來推斷,特定到C/c++中,它一般是指TCP/IP編程的相關技術棧(當然,它也不嚴謹)。
再舉一個實際的例子,大家去品一下上面這段話,至于能理解多少看自身了。在某電力部門,要求把服務端程序部署在終端上,把客戶端程序部署在服務器上。客戶端要24*7運行,服務端可以允許斷線。
注意:再次說明,本系列不是對網絡編程技術基礎知識的詳細分析說明,是對c++在網絡編程上的應用分析說明,所以只對相關的一些知識點進行指出和簡要的說明。更多的相關知識,請自行查閱下面提供的書籍和資料!
三、簡單示例
雖然網絡編程的例子多之又多,但這里還是要給一個簡單的例子:
服務端:
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>int main() {int server_fd, new_socket;struct sockaddr_in address;int opt = 1;int addrlen = sizeof(address);char buffer[1024] = {0};const char* msg = "hello moto!";//創建socketif ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}//設置選項if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {perror("setsockopt");exit(EXIT_FAILURE);}//地址設置address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons( 8888 );//綁定端口if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {perror("bind err");exit(EXIT_FAILURE);}//監聽if (listen(server_fd, 5) < 0) {perror("listen");exit(EXIT_FAILURE);}//接受連接if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {perror("accept err!");exit(EXIT_FAILURE);}send(new_socket , msg , strlen(msg) , 0 );printf("send msg ...\n");memset(buffer, '\0' , 1024);int ret = recv( new_socket , buffer, 1024,0);printf("%s\n",buffer );return 0;
}
客戶端:
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>int main() {int sock = 0, ret;struct sockaddr_in serv_addr;const char* msg = "hello !";char buffer[1024] = {0};//創建socketif ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {printf("\n Socket error \n");return -1;}serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(8888);//轉換地址if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {printf("\n Invalid address \n");return -1;}//連接serverif (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {printf("\n Connection err \n");return -1;}send(sock , msg , strlen(msg) , 0 );printf("send msg !\n");ret = recv( sock , buffer, 1024,0);printf("%s\n",buffer );return 0;
}
寫這種測試的小例程有一個需要注意的地方,客戶端發送完成后,不要立即退出,否則可能服務端收不到相關的消息。
四、推薦的資料和書籍
一如在VC編程上侯捷教師是一個令開發者仰望的山峰,在網絡編程也有很多更高的高峰。比如常見的推薦的《TCP/IP詳解》三卷和《Unix網絡編程》兩卷的作者W. Richard Stevens(當然他寫的APUE也相當的出名)。不過這些書的缺點也有,就是太老了。導致一些技術已經落后而一些新技術沒有體現出來。其它國外的還有不少就不一一列舉。國內也有幾個比較有名氣的網絡開發者,限定到本文這個場景非常推薦MUDUO庫的作者陳碩。當然就像江湖中一樣,肯定還有很多高手隱身不出。
學習網絡編程的書籍非常多,比如CSDN的孟巖大佬的“四書五經”之說就是為多數大牛推薦的。這里簡單羅列一下:
1、TCP/IP詳解(三卷)(TCP/IP Illustrated)
2、Unix網絡編程(兩卷)(UNIX Network Programming)
3、TCP/IP高級編程(Effective TCP/IP Programming)
4、C++網絡編程(兩卷)(C++ Network Programming)
雖然這些書籍非常不錯,但對于初學者未必就合適,也推薦一些比較容易借鑒學習的書籍:
1、《Linux多線程服務端編程》 陳碩
2、《Linux高性能服務端編程》 游雙
3、《Windows網絡與通信程序設計》 王艷平
并不是說其它的書籍不值得推薦,是覺得這幾本書更容易被學習和接受。至于網絡上的資料就更多了,如陳碩、原網易的云風等人的BLOG都非常值得一看。個人的建議是,要根據自己的實際情況來決定學習成長的路線,不要人云亦云。大牛們給的建議可能對大多數人都是非常好的,但具體到某些個體,可能會有所不妥。大家要知道如何不斷的根據大牛們的建議因地制宜的學習。
另外在網上還存在著大量的網絡框架如C++網絡編程中的ACE,還有libevent,libuv,libev,libeio,libhv,asio,poco等等。畢竟網絡應用是一個非常高頻的應用,也是很多開發者想登頂的希望。
其實還有很多應用程序中也有非常好的例子,比如REDIS,有時間推薦看看內部如何跨平臺實現了網絡服務端的編程。
五、總結
網絡編程是一個復雜的應用,一般來說,很難在一兩年內達到熟練掌握的程度,更不要談精通了。通常,把基礎的網絡知識學習完成,頭腦中有一個相對完整的網絡編程概念,然后在實際應用中不斷的加以印證,才能更快更好的掌握網絡編程。
網絡編程其實是一個簡單應用易,復雜應用極難的技術。它不僅是涉及到網絡相關的技術,還包括內存管理、多線(進)程以及異步編程等很多技術,甚至是否需要跨平臺跨系統等。對大多數開發者言,網絡編程的應用一般都是比較簡單的應用,并發通常也就是十個量級左右,而且經常類似于交互式通信那種情況。對多線程和異步的要求不高甚至沒有,對內存管理和效率的要求也不嚴格。
但當真正到了C10K以上的編程時,復雜程度立刻便上來,導致很多開發者沒有一個過渡便直面這些復雜的應用。也就是說,在網絡編程大多數的編程場景是要么簡單,要么復雜,中等的開發場景非常少。而且從設計上考慮,一旦到了中等的場景,優秀的架構師通常會考慮擴展的情況下設計成更為復雜的框架結構。
復雜的網絡編程,導致很多的框架的出現,而這些框架的出現更是切斷了大多數開發者對背后復雜結構的理解,導致一個從初級網絡編程到高級網絡編程的連續而完整的流程,即要么只會簡單的編程,要么只會在框架下完成各種場景的應用。
更何況,網絡編程的實際要求仍然在不斷的增長,這也是前面分析DPDK和XDP等的一些重要原因。換句話說,網絡編程的技術仍然在不斷的進步。所以,不斷的學習才能保證在網絡編程的方向上有更大的發展。