Linux網路編程3——socket,linux網路編程
宏定義
首先介紹兩個宏定義,看如下代碼
代碼1
/************************************************************************* > File Name: test.c > Author: KrisChou > Mail:zhoujx0219@163.com > Created Time: Thu 28 Aug 2014 10:11:59 AM CST ************************************************************************/#include <stdio.h>#define __JOIN__(post_fix) tag_##post_fixtypedef int tag_element;typedef int tag_arr[10];int main(int argc, char *argv[]){ __JOIN__(element) val1; __JOIN__(arr) val2; int index; for(index = 0; index < 9; index++) { val2[index] = index + 1; printf("%3d", val2[index]); } printf("\n"); return 0;}
運行結果:
[purple@localhost 0828]$ gcc test.c[purple@localhost 0828]$ ./a.out 1 2 3 4 5 6 7 8 9
看一下預先處理後的代碼
typedef int tag_element;typedef int tag_arr[10];int main(int argc, char *argv[]){ tag_element val1; tag_arr val2; int index; for(index = 0; index < 9; index++) { val2[index] = index + 1; printf("%3d", val2[index]); } printf("\n"); return 0;}
即宏定義#define __JOIN__(post_fix) tag_##post_fix 將tag_與參數post_fix串連起來了
舉例
觀察結構體struct sockaddr_in的某個參數,之前有宏定義如下:
#define __SOCKADDR_COMMON(sa_prefix) sa_family_t sa_prefix##family
那麼有:
__SOCKADDR_COMMON (sin_); --> sa_family_t sin_family
代碼2
#include <stdio.h>#define STR(s) #s//#define IP 192.168.1.1int main(int argc, char *argv[]){ char buf[] = STR(helloworld!); printf("%s \n", buf); return 0;}
運行結果
[purple@localhost 0828]$ gcc test1.c[purple@localhost 0828]$ ./a.outhelloworld!
小結
我們使用#把宏參數變為一個字串,用##把兩個宏參數貼合在一起。
使用TCP協議伺服器端
1. 標頭檔
#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <stdio.h>#include <stdlib.h>
2. socket函數:產生一個套介面描述符。
原型: int socket(int domain,int type,int protocol);
參數: domainAF_INET: Ipv4網路通訊協定;AF_INET6:IPv6網路通訊協定。 type tcp:SOCK_STREAM;udp:SOCK_DGRAM。protocol指定socket所使用的傳輸協議編號。通常為0。
傳回值:成功則返回套介面描述符,失敗返回-1。
3. bind函數:用來綁定一個連接埠號碼和IP地址,使套介面與指定的連接埠號碼和IP地址相關聯。
原型:int bind(int sockfd,struct sockaddr * my_addr,int addrlen);
參數:sockfd 為前面socket的傳回值。my_addr 為結構體指標變數 addrlen 為sockaddr的結構體長度。通常是計算sizeof(struct sockaddr)
傳回值:成功則返回0,失敗返回-1
對於不同的socket domain定義了一個通用的資料結構,如下。注意,此sockaddr結構會因使用不同的socket domain而有不同結構定義。
struct sockaddr //此結構體不常用{ unsigned short int sa_family; //調用socket()時的domain參數,即AF_INET值。 char sa_data[14]; //最多使用14個字元長度};
例如使用AF_INET domain,其socketaddr結構定義如下:
struct sockaddr_in //常用的結構體{ unsigned short int sin_family; //即為sa_family AF_INET uint16_t sin_port; //為使用的port編號 struct in_addr sin_addr; //為IP 位址 unsigned char sin_zero[8]; //未使用};
其中,struct in_addr 為:
struct in_addr{ uint32_t s_addr;};
4. listen函數:使伺服器的這個連接埠和IP處於監聽狀態,等待網路中某一客戶機的串連請求。如果用戶端有串連請求,連接埠就會接受這個串連。
原型:int listen(int sockfd,int backlog);
參數:sockfd為前面socket的傳回值,即sfd。backlog指定同時能處理的最大串連要求,通常為10或者5,最大值可設至128。
傳回值:成功則返回0,失敗返回-1
5. accept函數:接受遠端電腦的串連請求,建立起與客戶機之間的通訊串連。伺服器處於監聽狀態時,如果某時刻獲得客戶機的串連請求,此時並不是立即處理這個請求,而是將這個請求放在等待隊列中,當系統空閑時再處理客戶機的串連請求。當accept函數接受一個串連時,會返回一個新的socket標識符,以後的資料轉送和讀取就要通過這個新的socket編號來處理,原來參數中的socket也可以繼續使用,繼續監聽其它客戶機的串連請求。(也就是說,類似於移動營業廳,如果有客戶打電話給10086,此時伺服器就會請求串連,處理一些事務之後,就通知一個話務員接聽客戶的電話,也就是說,後面的所有操作,此時已經於伺服器沒有關係,而是話務員跟客戶的交流。對應過來,客戶請求串連我們的伺服器,我們伺服器先做了一些綁定和監聽等等操作之後,如果允許串連,則調用accept函數產生一個新的通訊端,然後用這個新的通訊端跟我們的客戶進行收發資料。也就是說,伺服器跟一個用戶端串連成功,會有兩個通訊端。)
原型:int accept(int s,struct sockaddr * addr,int * addrlen);
參數:s為前面socket的傳回值,即sfd。addr為結構體指標變數,和bind的結構體是同種類型的,系統會把遠程主機的資訊(遠程主機的地址和連接埠號碼資訊)儲存到這個指標所指的結構體中。addrlen表示結構體的長度,為整型指標。
傳回值:成功則返回新的socket描述符,用於與用戶端通訊。失敗返回-1
6. recv函數:用新的通訊端來接收遠端主機傳來的資料,並把資料存到由參數buf 指向的記憶體空間。
原型:int recv(int sockfd,void *buf,int len,unsigned int flags);
參數:sockfd為前面accept的傳回值,即new_fd,也就是新的通訊端。buf表示緩衝區。len表示緩衝區的長度。flags通常為0。
傳回值:成功則返回實際接收到的字元數,可能會少於你所指定的接收長度。失敗返回-1。
7. send函數:用新的通訊端發送資料給指定的遠端主機。
原型:int send(int s,const void * msg,int len,unsigned int flags);
參數:s為前面accept的傳回值,即new_fd。msg一般為常量字串。len表示長度。flags通常為0。
傳回值:成功則返回實際傳送出去的字元數,可能會少於你所指定的發送長度。失敗返回-1。
8. close函數:當使用完檔案後若已不再需要則可使用close()關閉該檔案,並且close()會讓資料寫回磁碟,並釋放該檔案所佔用的資源。
原型:int close(int fd);
傳回值:若檔案順利關閉則返回0,發生錯誤時返回-1。
小結
1. listen函數使得主動串連套介面變為被動串連套介面,使得一個進程可以接受其它進程的請求,從而成為一個伺服器處理序。在TCP伺服器編程中listen函數把進程變為一個伺服器,並指定相應的通訊端變為被動串連。listen函數在一般在調用bind之後,調用accept之前調用,其函數原型如下:
int listen(int sockfd, int backlog)
2. socket函數返回的通訊端fd,預設是一個主動串連的通訊端,也就是此時系統假設使用者會對這個通訊端調用connect函數,期待它主動與其它進程串連,然後在伺服器編程中,使用者希望這個通訊端可以接受外來的串連請求,也就是被動等待使用者來串連。由於系統預設一個通訊端是主動串連的,所以需要通過某種方式來告訴系統,而使用者進程正是通過listen函數來完成這件事。
3. 當伺服器處理序處理串連請求時,可能同時還存在其它的串連請求。核心會在自己的進程空間裡維護一個隊列(客戶串連請求隊列)用於存放伺服器監聽到的串連請求的連絡方式(連接埠號碼與IP)。
4. 服務程式調用accept函數從處於監聽狀態的流通訊端的客戶串連請求隊列中取出排在最前的一個客戶請求,並且建立一個新的通訊端來與客戶通訊端建立串連通道,如果串連成功,就返回新建立的通訊端的描述符,以後與客戶通訊端交換資料的是新建立的通訊端;如果失敗就返回 INVALID_SOCKET。該函數的第一個參數指定處於監聽狀態的流通訊端;作業系統利用第二個參數來返回新建立的通訊端的地址結構;作業系統利用第三個參數來返回新建立的通訊端的地址結構的長度。
用戶端
connect函數:用來請求串連遠程伺服器,將參數sockfd 的socket 連至參數serv_addr 指定的伺服器IP和連接埠號碼上去。
原型:int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
參數:sockfd為前面socket的傳回值,即sfd。serv_addr為結構體指標變數,儲存著遠程伺服器的IP與連接埠號碼資訊。addrlen表示結構體變數的長度。
傳回值:成功則返回0,失敗返回-1。
linux socket網路編程代碼
Linux是多任務的作業系統,可在運行在Intel 80386及更高檔次的PC機、ARMS、MIPS和PowerPC等多種電腦平台,已成為應用廣泛、可靠性高、功能強大的電腦作業系統,Linux具有核心小、效率高、原始碼開放等優點,還內含了TCP/IP網路通訊協定,很適合在伺服器領域使用,而伺服器主要用途之一就是進行網路通訊,隨著電腦辦公自動化處理技術的應用與推廣,網路的不斷普及,傳統的紙張式檔案傳輸方式已經不再適合發展的需要,人們更期待一種便捷、高效、環保、安全的網路傳輸方式.
協議概述TCP/IP即傳輸控制通訊協定/網路通訊協定[1](Transmission Control Protocol/Internet Protocol),是一個由多種協議組成的協議族,他定義了電腦通過網路互相通訊及協議族各層次之間通訊的規範,圖1描述了Linux對IP協議族的實現機制[2]。
Linux支援BSD的通訊端和全部的TCP/IP協議,是通過網路通訊協定將其視為一組相連的軟體層來實現的,BSD通訊端(BSD Socket)由通用的通訊端管理軟體支援,該軟體是INET通訊端層,用來管理基於IP的TCP與UDP連接埠到連接埠的互聯問題,從協議分層來看,IP是網路層協議,TCP是一個可靠的連接埠到連接埠的傳輸層協議,他是利用IP層進行傳接報文的,同時也是連線導向的,通過建立一條虛擬電路在不同的網路間傳輸報文,保證所傳輸報文的無丟失性和無重複性。使用者資料報文協議(User Datagram Protocol,UDP)也是利用IP層傳輸報文,但他是一個非連線導向的傳輸層協議,利用IP層傳輸報文時,當目的方網際協議層收到IP報文後,必須識別出該報文所使用的上層協議(即傳輸層協議),因此,在IP前序上中,設有一個"協議"域(Protocol)。通過該域的值,即可判明其上層協議類型,傳輸層與網路層在功能說的最大區別是前者提供進程通訊能力,而後者則不能,在進程通訊的意義上,網路通訊的最終地址不僅僅是主機地址,還包括可以描述進程的某種標識符,為此,TCP/UDP提出了協議連接埠(Protocol Port)的概念,用於標識通訊的進程,例如,Web伺服器處理序通常使用連接埠80,在/etc/services檔案中有這些註冊了的連接埠地址。
對於TCP傳輸,傳輸節點間先要建立串連,然後通過該串連傳輸已排好序的報文,以保證傳輸的正確性,IP層中的代碼用於實現網際協議,這些代碼將IP頭增加到傳輸資料中,同時也把收到的IP報文正確的傳送到TCP層或UDP層。TCP是一個連線導向協議,而UDP則是一個非連線導向協議,當一個UDP報文發送出去後,Linux並不知道也不去關心他是否成功地到達了目的的主機,IP層之下,是支援所有Linux網路應用的網路裝置層,例如點到點協議(Point to Point Protocol,PPP)和乙太網路層。網路裝置並非總代表物理裝置,其中有一些(例如回送裝置)則是純粹的軟體裝置,網路裝置與標準的Linux裝置不同,他們不是通過Mknod命令建立的,必須是底層軟體找到並進行了初始化之後,這些裝置才被建立並可用。因此只有當啟動了正確設定的乙太網路裝置驅動程式的核心後,才會有/dev/eth0檔案,ARP協議位於IP層和支援位址解析的協議層之間。
網路通訊原理所有的網路通訊就其實現技術可以分為兩種,線路交換和包交換,電腦網路一般採用包交換,TCP使用了包交換通訊技術,......餘下全文>>
比較Linux與Windows在網路編程方面的特點
找了一段,大致涉及到了您的問題:
一、socket的模式
socket一般有兩種模式:同步和非同步(windows網路編程技術中也可叫鎖定和非鎖定,Linux網路編程叫阻塞和非阻塞)。
二、socket的類型
socket一般有三種類型,基於TCP的流式通訊端,基於UDP的資料通訊端和原始通訊端。
三、socket的IO模型
socket
的IO模型是編程中使用socket兩種模式的策略,它們適用的場合不同,在不同的作業系統上支援的模型也不同,例如windows從NT版本才開始支援
完成連接埠模型。Linux和Windows所支援的模型也有區別,當然也有相同的地方,可能叫法不一樣,但大致思路是一樣的,下面分別介紹windows
和Linux的IO模型
1、 Windows下的通訊端IO模型:
A、 Select(選擇)模型
用於同步socket的狀態檢測模型,又叫(Linux)多工,可以同時檢測多個socket的狀態
B、 WSAAsyncSelect(非同步選擇)模型
用於非同步socket的非同步事件設定,它是基於Windows訊息的模型,必須先開啟一個視窗,然後把視窗和socket的訊息綁定,這樣,在socket有訊息通知時,作業系統便通知視窗,然後在視窗進行處理。
C、 WSAEventSelect(非同步事件)模型
用
於非同步socket的非同步事件,它是基於網路事件的模型,先使用CreateEvent建立一個事件,然後使用WSAEventSelect進行事件綁
定,然後可以使用WaitForMultipleObject(Event)進行事件監聽,可以同時監聽多個事件,不光是socket的,比如可以監聽使
用CreateWaitableTimer建立的Timer等。
D、 重疊IO模型
用
於非同步socket,在建立socket時需要在建立函數WSASocket中使用WSA_FLAG_OVERLAPPED標誌,然後在投遞IO請求的時
候將一個Overlapped結構體指標賦給投遞函數,可以使用WSAWaitForMultipleObject來監聽事件,然後使用
WSAGetOverlappedResult來擷取IO的狀態,也可以在Overlapped結構體中使用完成常式來處理,即在投遞函數中把完成常式賦
給投遞函數。
E、 完成連接埠模型
它
是迄今為止最複雜的一種IO模型,當應用程式需要管理眾多的通訊端並且希望隨著系統內安裝的CPU數目的增多,應用程式的效能也可以線性增加,就可以使用
這種模型,它的原理是每個CPU可以單獨負責一個線程的執行,避免線程的頻繁切換。使用這種模型往往可以達到最佳的系統效能。
首
先需要使用CreateIOCompletePort來建立完成連接埠,然後將IO控制代碼和此連接埠綁定,綁定也是使用此函數,當然也可以一次完成。接著是建立
工作者線程,工作者線程會使用GetQueuedCompletionStatus進入完成連接埠維護的線程池,當有完成事件時,會啟用一個線程。
2、 Linux下的IO模型
A、阻塞IO
B、非阻塞IO
C、IO多工(選擇)
D、訊號驅動
用於非同步socket,首先設定訊號處理函數,然後使用fcntl函數設定socket的擁有者,像windows下使用WSAAsncSelect設定socket的視窗一樣。使用這種模型,當核心操作可以被操作的時候通知我們的應用程式
E、非同步IO
當核心在所有操作完成後才會通知應用程式
四、......餘下全文>>