Linux網路編程

來源:互聯網
上載者:User
如果你想進入Linux神奇的網路編程世界,請跟筆者來,在學習之前,筆者只需要你擁有一定的C語言編程知識,就足夠了。筆者會講述編寫網路程式需要的基本知識。  所謂網路,在軟體人員的觀點來看,就是很多的用物理鏈路(比如,乙太網路,無線網路)連在一起的電腦,並且安裝有網路程式。就像打電話,我們需要知道對方的號碼一樣,網路程式也需要知道要和那台電腦通訊,在這裡,就是電腦的網路介面所擁有的IP地址。其實,一個完整的網路程式擁有兩個獨立的程式,他們分別運行在兩個電腦上,一個是網路通訊的伺服器端,一個是網路通訊的用戶端,就像打電話,需要一個打電話的,還要一個接電話的是一樣的,所以,我們需要編寫兩個程式,才是完整的網路通訊程式,我們熟悉的OutLook,FOXMAIL等都是網路程式中的用戶端程式,而Apache,QMail等便是伺服器端程式。只是往往,網路程式的用戶端和伺服器端之間有一定的通訊互動協議,比如SMTP,POP3,HTTP,FTP等,對於我們網路程式的編寫者來說,他們就規定了通用的互動協議,這些協議規定了用戶端,伺服器端應該完成的工作,所以,編寫好網路程式還需要理解好協議,不過,一般說來,我們用不著自己寫協議,有很多的協議,我們可以使用的,都有RFC文檔來說明了,你可以在網路上找到各種協議的RFC.   當然,我們現在將要開始編寫的第一個網路程式,雖然非常簡單,但是卻可以很清楚的說明大部分編寫網路程式需要的基本概念,好了先讓我們看看網路程式的TCP伺服器端的編寫步驟: 1. 首先,你需要建立一個用於通訊的套介面,一般使用socket調用來實現。這等於你有了一個用於通訊的電話:) 2. 然後,你需要給你的套介面設定連接埠,相當於,你有了電話號碼。這一步 一般通過設定網路套介面地址和調用bind函數來實現。 3. 調用listen函數使你的套介面成為一個監聽通訊端。 以上三個步驟是TCP伺服器的常用步驟。 4. 調用accept函數來啟動你的通訊端,這時你的程式就可以等待用戶端的串連了。 5. 處理用戶端的串連請求。 6. 終止串連。   現在讓我們來看看網路程式用戶端的編程步驟: 1. 和伺服器的步驟一樣。 2. 通過設定套介面地址結構,我們說明,用戶端要與之通訊的伺服器的IP地址和連接埠。 3. 調用connect函數來串連伺服器。 4. 發送或者接收資料,一般使用send和recv函數調用來實現。 5. 終止串連。   以上的步驟,是比較普遍的,我們可以從中看出,編寫網路程式是很有意思的,同時,也不是非常複雜,當然,這不包括,高難度的東西:-),下次,我會給出一個簡單的伺服器和一個客戶機程式。 今天,我給出的程式碼封裝括一個伺服器,和一個客戶機程式,但是省略了很多代碼, 比如說錯誤處理代碼,這樣做主要是為了使網路編程的主線清楚,所以,這兩個程式談不上網路安全性,和穩定性,這些是以後的話題。但是對於基本理解 網路編程已經足夠了。我會在下次給出一個完整可啟動並執行程式。下面我會詳細 解釋程式的步驟:先要包括一部分網路編程必須的頭部檔案:#include #include #include #includeint main(int argc,char *argv[]) {int listensock,connsock; /定義兩個socket通訊端,一個用於監聽,一個用於通訊 struct sockaddr_in serveraddr; /定義網路通訊端地址結構 const char buff[] = "Hello! Welcome here! "; /定義要發送的資料緩衝區; listensock = socket(AF_INET,SOCK_STREAM,0); /建立一個通訊端,用於監聽 bzero(&serveraddr,sizeof(servaddr)); /地址結構清零 serveraddr.sin_family = AF_INET; /指定使用的通訊協議族 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /指定接受任何串連,因為伺服器不知道誰會要求串連 serveraddr.sin_port = htons(5000); /指定監聽的連接埠 bind(listensock,(sockaddr *)&serveraddr,sizeof(serveraddr)); /給套介面邦定地址 listen(listensock,1024); /開始監聽connsock = accept(listensock,(sockaddr *)NULL, NULL); /建立通訊的通訊端,accept函數,等待用戶端程式使用connect函數的串連send(connsock,buff,sizeof(buff), 0); /向用戶端發送資料 close(connsock); /關閉通訊通訊端 close(listensock); /關閉監聽通訊端 }這是用戶端的程式:int main(int argc,char **argv) {  char recvbuff[100]; /定義要接收的資料緩衝區  int sockfd; /定義一個socket通訊端,用於通訊  struct sockaddr_in serveraddr;/定義網路通訊端地址結構  if(argc != 2){   printf("Usage: echo ip地址");  exit(0); } sockfd = socket(AF_INET,SOCK_STREAM,0); /建立一個通訊端  bzero(&serveraddr,sizeof(serveraddr));  serveraddr.sin_family = AF_INET; /指定使用的通訊協議族  serveraddr.sin_port = htons(5000);/指定要已連線的服務器的連接埠  inet_pton(AF_INET, argv[1], &serveraddr.sin_addr);  connect(sockfd,(sockaddr *)&serveraddr,sizeof(serveraddr)); /串連伺服器  recv(sockfd,recvbuff,sizeof(recvbuff),0); /接收伺服器的資料  printf("%s ",recvbuff); /列印接收到的資料   close(sockfd); /關閉通訊端 }這兩個程式運行後,當用戶端串連到伺服器後,將接收到伺服器傳來的字串Hello! Welcome here!,不過,程式調試的任務還得又你自己要完成。 你想知道著名的oicq使用的是什麼技術來收發訊息的嗎?今天,我給出的兩個程式,一個伺服器 和一個用戶端程式,便能告訴你,其中的基本技術,當然,我指的不包括,它的介面是怎樣做的:)  這兩個程式使用的是UDP通訊端編程,上一次給出的其實是使用TCP通訊端編程,你自己可以先分析 一下,他們的異同點,我會在下一次,分析這兩種編程的區別。這是發送資料的程式:/*頭部檔案包括網路需要的和基本輸入輸出需要的*/ #include #include #include #include #includeint port = 5500; void main() {  int sockfd;  int count = 0;  int flag;  char buf[80];  struct sockaddr_in address;  sockfd = socket(AF_INET, SOCK_DGRAM, 0); //看到不同的地方了嗎?  memset(&address, 0, sizeof(address));  address.sin_family = AF_INET;  address.sin_addr.s_addr = inet_addr("127.0.0.1");  address.sin_port = htons(port);  flag = 1;  do{   sprintf(buf,"Packet %d ", count);   if(count > 30){   sprintf(buf,"over ");   flag = 0;   }   sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&address, sizeof(address)); // 發現了嗎使用的函數不一樣,它也是發送資料   count++;  }while (flag); }這是接收資料的程式:#include #include #include #include #includechar *hostname = "127.0.0.1"; //這個特殊的ip表示本的電腦void main() {   int sinlen;   int port = 8080;   char message[256];   int sockfd;   struct sockaddr_in sin;   struct hostent *server_host_name; // hostent結構有著機器的名字等資訊   server_host_name = gethostbyname("127.0.0.1"); // 這個函數用來得到“127.0.0.1”的主機名稱字,也就是原生名字   bzero(&sin,sizeof(sin));   sin.sin_family = AF_INET;   sin.sin_addr.s_addr = htonl(INADDR_ANY);   sin.sin_port = htons(port);   sockfd = socket(PF_INET,SOCK_DGRAM,0); //這裡也不一樣   bind(sockfd,(struct sockaddr *)&sin,sizeof(sin));     while(1){      sinlen = sizeof(sin);      recvfrom(sockfd,message,256,0, (struct sockaddr *)&sin,&sinlen);// 它是接受資料的函數      printf(" Data come from server: %s ",message);      if(strncmp(message,"over",4) ==0)break;     }     close(sockfd); }  當你編譯調試通過了後,在一個視窗,運行接收程式,一個視窗運行發送程式,你就可以看到資料被創送了。網路程式是可以在本地機器上,使用兩個不同的視窗來運行,類比用戶端和伺服器的 今天,筆者會解釋網路編程中非常重要的兩個概念:TCP編程和UDP編程,這是真真進入網路編程的燦爛世界必須深入理解的部分。   首先,我們必須明白,一般作業系統包括Windows,Linux,UNIX,他們提供的供應用程式員使用的編程介面,一般的函數名字都差不多。不同的是,他們的作業系統對這些函數(也可以說是系統調用)的實現細節不盡相同,因此各種作業系統,在提供網路服務的時候,就存在著諸如速度、效率及穩定性的差別。   那麼,就網路編程的套介面字的選則來看,一樣存在著以上的差別。也就是說,你選擇TCP通訊端和選擇UDP編程,在傳輸資料時,一樣有著速度、效率及穩定性的差別。首先,明白這一點,對於開始網路編程是非常重要的。   TCP通訊端,作業系統向你提供的是一個穩定的資料通路,從程式員的角度來看,你只需要明白,當你使用TCP編程時,如果你調用 的發送資料函數,比如send()函數,它的返回成功,那麼表示,系統發送出的資料肯定被通訊的對方準確接收到了。而UDP通訊端,作業系統給你提供的是一個不穩定的,不需連線的資料通路,所以當你使用UDP編程時,如果你調用的發送資料的函數,如sendto() 函數,它的返回成功,那麼表示,你要發送的資料已經發送到了網上,而這些資料是否到達了你要發送資料的對方,那時不一定的。所以對於UDP編程,我們如果要保證資料的發送的準確、及時,我們需要自己建立起,一些資料傳送的控制機制,來確保我們的資料成功發送到對方,而不僅僅是把資料發送到了網路上,我們就不過問了。當然對於TCP編程,作業系統已經幫我們做了一系列控制功能,所以我們不需要考慮太多的東西;-)   從以上的分析,你應該可以看出,對於初學者或者說,TCP編程是非常好的入門點,也是很容易的。當然,UDP有它自己的好處,不過它雖然不能保證你調用一次發送函數就把資料發送到對方那裡,但是只要你進行適當的處理,你會發現,UDP發送資料的速度,比TCP編程要快!天下沒有十全十美的!   TCP編程擁有了可靠的資料連線,UDP不具有,但是在速度方面,UDP編程缺優於TCP編程,特別是對於傳輸短訊息,所以我認為OICQ所以選擇UDP編程,這個原因是其中很重要的一個^-^。   現在我已經說明了TCP和UDP編程的重要差別,雖然這些差別是由協議本身引入的,但是對於編程來說,理解了是很有好處的。   當然,那對於一個程式員來說還的明白,選擇TCP編程,和UDP編程到底是怎樣體現在代碼上的,其實如果你仔細分析過我前面給出的兩個程式,你也許已經明白,除了他們使用的發送資料和接收資料的函數不怎樣一樣之外,最重要的差別在於當你使用socket()函數 建立通訊端的時候,你需要的指定的三個參數中,中間的那個參數。如果你要使用TCP編程,你要使用SOCK_STREAM,而如果你要使用UDP編程,使用SOCK_DGRAM,關於TCP和UDP的更加深入的區別和介紹,我會在以後講述。   下次,我會給大家講述一下,伺服器和客戶機的概念和區別:) 伺服器/客戶機模式是網路通訊互動的最常用模式,我們必須要深刻的理解這種互動模式。   其實,網路軟體在很大程度上是對各種網路通訊協定的實現,不管這種協議是官方的,還是你自己定義的。所以,網路軟體的好壞也和協議的好壞有著直接的關係。當然,伺服器/客戶機模式在某種程度上就規定了這樣一種機制: 1. 伺服器方的程式首先啟動,開始等待。 2. 客戶機的程式啟動,向伺服器提出通訊串連的請求。 3. 伺服器決定是否接受客戶機的串連請求。並且,給客戶機一個回答。 4. 如果,伺服器和客戶機建立了串連,通常的協議,會給出那一方應該在這個時候首先發送資料,還有發送資料的內容,格式等等。 5. 這樣,一方發送,另一方接受,然後回答。   伺服器和客戶機就是在這樣的一來一往的情況下,進行通訊的,但是為什麼要選擇這種依次發送的順序了,這些都是因為要解決,在網路上傳輸資料時,可能出現的死結和餓死等現象。 對於這兩種現象,我們可以這樣理解,比如,我向你發了訊息,等你的回答,然後才進行下一步,可是這時候,你可能也在等待我的資料到來,然後發回答給我,可是問題出現了,如果你沒有得到我的訊息(掉了包),那麼我和你可能一直等待下去。當然,常用的各種協議, 比如SMTP,POP3,FTP,WWW都很好的定義了發送和接收資料的順序問題。這也是我們要很好理解的。剛才我說的那個例子,其實就是一種死結,兩方都不能繼續通訊了。至於餓死現象,我們可以這樣想,一個伺服器每次只能處理一個使用者的通訊請求,那麼當他和一個客戶機通訊時,他接收了客戶機的串連請求後,等待客戶機的資料發送過來,可客戶機的資料遲遲不到(可能失掉了,可能客戶機更本沒發送資料),這種情況下,伺服器將不能和別的客戶機通訊,資源都被這個客戶機用了,那麼對於其他的客戶機使用者,就處於一種餓死狀態。   我們當然可以通過,規定很多的東西來保證我們通訊的順利進行。比如一方發送,一方等待。 發送方在沒有得到回答前重複多次發送資料。發送方還可以使用定時器等方法,來保證不出現餓死和死結現象。如果你想學習更多的方法和思想,你可以學學TCP/IP協議和各種應用協議,他們在不同的層次上解決了各種可能遇到的問題。   當然,在網路上會出現的障礙是多種多樣的,你所用的協議及你所寫的代碼,一起決定了你的網路程式的效能和安全。所以,現實生活中的網路程式往往是很複雜的.
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.