Linux 網路編程是一個基於客戶端/伺服器(即:client/server)的套接字編程結構(即:socket 編程)。
在Linux網路編程的過程中,使用到的協議主要有:TCP/IP(基於連接的協議)、UDP(基於無連接的協議)、ICMP(通常我們在 DOS 狀態下通過使用 ping 命令,檢查網路的通斷,就是依靠該協議)。
在Linux系統的套接字編程中,有標準的 socket( )、client( ) 代碼的編寫風格。涉及到的主要庫函數有:bind( )、listen( )、accept( )、read( )、write( ) 等。
至於說要想學習詳細的Linux網路編程技術實現細節,你可以參考《TCP/IP詳解》一書。一套共三本。
⑵ Windows和Linux下的網路編程方法的異同
Linux下的網路編程與Windows下採用底層的API類似,但是也有區別:區別一:Windows下需加上WSAStartup()函數區別二:關閉socket:Linux為close(),Windows為closesocket()windows下採用上層的API,一般有CSocket和CAsynSocket這兩種類型的類這種情況以下socket函數一般的首字母大寫。而底層的API不管是windows下的還是linux下的socket函數首字母都是小寫的。
⑶ linux網路編程,為什麼要將文件描述符設置成非阻塞模式
非阻塞IO 和阻塞IO:
在網路編程中對於一個網路句柄會遇到阻塞IO 和非阻塞IO 的概念, 這里對於這兩種socket 先做一下說明:
基本概念:
阻塞IO::
socket 的阻塞模式意味著必須要做完IO 操作(包括錯誤)才會
返回。
非阻塞IO::
非阻塞模式下無論操作是否完成都會立刻返回,需要通過其他方
式來判斷具體操作是否成功。(對於connect,accpet操作,通過select判斷,
對於recv,recvfrom,send,sendto通過返回值+錯誤碼來判斷)
IO模式設置:
SOCKET
對於一個socket 是阻塞模式還是非阻塞模式的處理方法::
方法::
用fcntl 設置;用F_GETFL獲取flags,用F_SETFL設置flags|O_NONBLOCK;
同時,recv,send 時使用非阻塞的方式讀取和發送消息,即flags設置為MSG_DONTWAIT
實現
fcntl 函數可以將一個socket 句柄設置成非阻塞模式:
flags = fcntl(sockfd, F_GETFL, 0); //獲取文件的flags值。
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); //設置成非阻塞模式;
flags = fcntl(sockfd,F_GETFL,0);
fcntl(sockfd,F_SETFL,flags&~O_NONBLOCK); //設置成阻塞模式;
並在接收和發送數據時:
將recv, send 函數的最後有一個flag 參數設置成MSG_DONTWAIT
recv(sockfd, buff, buff_size,MSG_DONTWAIT); //非阻塞模式的消息發送
send(scokfd, buff, buff_size, MSG_DONTWAIT); //非阻塞模式的消息接受
普通文件
對於文件的阻塞模式還是非阻塞模式::
方法1、open時,使用O_NONBLOCK;
方法2、fcntl設置,使用F_SETFL,flags|O_NONBLOCK;
消息隊列
對於消息隊列消息的發送與接受::
//非阻塞 msgsnd(sockfd,msgbuf,msgsize(不包含類型大小),IPC_NOWAIT)
//阻塞 msgrcv(scokfd,msgbuf,msgsize(**),msgtype,IPC_NOWAIT);
讀
阻塞與非阻塞讀的區別: //阻塞和非阻塞的區別在於沒有數據到達的時候是否立刻返回.
讀(read/recv/msgrcv):
讀的本質來說其實不能是讀,在實際中, 具體的接收數據不是由這些調用來進行,是由於系統底層自動完成的。read 也好,recv 也好只負責把數據從底層緩沖 到我們指定的位置.
對於讀來說(read, 或者recv) ::
阻塞情況下::
在阻塞條件下,read/recv/msgrcv的行為::
1、如果沒有發現數據在網路緩沖中會一直等待,
2、當發現有數據的時候會把數據讀到用戶指定的緩沖區,但是如果這個時候讀到的數據量比較少,比參數中指定的長度要小,read 並不會一直等待下去,而是立刻返回。
read 的原則::是數據在不超過指定的長度的時候有多少讀多少,沒有數據就會一直等待。
所以一般情況下::我們讀取數據都需要採用循環讀的方式讀取數據,因為一次read 完畢不能保證讀到我們需要長度的數據,
read 完一次需要判斷讀到的數據長度再決定是否還需要再次讀取。
非阻塞情況下::
在非阻塞的情況下,read 的行為::
1、如果發現沒有數據就直接返回,
2、如果發現有數據那麼也是採用有多少讀多少的進行處理.
所以::read 完一次需要判斷讀到的數據長度再決定是否還需要再次讀取。
對於讀而言:: 阻塞和非阻塞的區別在於沒有數據到達的時候是否立刻返回.
recv 中有一個MSG_WAITALL 的參數::
recv(sockfd, buff, buff_size, MSG_WAITALL),
在正常情況下recv 是會等待直到讀取到buff_size 長度的數據,但是這里的WAITALL 也只是盡量讀全,在有中斷的情況下recv 還是可能會被打斷,造成沒有讀完指定的buff_size的長度。
所以即使是採用recv + WAITALL 參數還是要考慮是否需要循環讀取的問題,在實驗中對於多數情況下recv (使用了MSG_WAITALL)還是可以讀完buff_size,
所以相應的性能會比直接read 進行循環讀要好一些。
注意:: //使用MSG_WAITALL時,sockfd必須處於阻塞模式下,否則不起作用。
//所以MSG_WAITALL不能和MSG_NONBLOCK同時使用。
要注意的是使用MSG_WAITALL的時候,sockfd 必須是處於阻塞模式下,否則WAITALL不能起作用。
寫
阻塞與非阻塞寫的區別: //
寫(send/write/msgsnd)::
寫的本質也不是進行發送操作,而是把用戶態的數據 到系統底層去,然後再由系統進行發送操作,send,write返回成功,只表示數據已經 到底層緩沖,而不表示數據已經發出,更不能表示對方埠已經接收到數據.
對於write(或者send)而言,
阻塞情況下:: //阻塞情況下,write會將數據發送完。(不過可能被中斷)
在阻塞的情況下,是會一直等待,直到write 完,全部的數據再返回.這點行為上與讀操作有所不同。
原因::
讀,究其原因主要是讀數據的時候我們並不知道對端到底有沒有數據,數據是在什麼時候結束發送的,如果一直等待就可能會造成死循環,所以並沒有去進行這方面的處理;
寫,而對於write, 由於需要寫的長度是已知的,所以可以一直再寫,直到寫完.不過問題是write 是可能被打斷嗎,造成write 一次只write 一部分數據, 所以write 的過程還是需要考慮循環write, 只不過多數情況下一次write 調用就可能成功.
非阻塞寫的情況下:: //
非阻塞寫的情況下,是採用可以寫多少就寫多少的策略.與讀不一樣的地方在於,有多少讀多少是由網路發送的那一端是否有數據傳輸到為標准,但是對於可以寫多少是由本地的網路堵塞情況為標準的,在網路阻塞嚴重的時候,網路層沒有足夠的內存來進行寫操作,這時候就會出現寫不成功的情況,阻塞情況下會盡可能(有可能被中斷)等待到數據全部發送完畢, 對於非阻塞的情況就是一次寫多少算多少,沒有中斷的情況下也還是會出現write 到一部分的情況.
⑷ 如何在linux下進行網路編程
呵呵,你問對人啦,我就是學習了C語言的基礎知識(譚浩強的那本書),然後學習了網路編程。現在在做linux雲計算 你需要找到《UNIX網路編程第1卷:套介面API》 看這個書的同時,你從網上找些最簡單的網路通訊程序小例子看看
⑸ 請比較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
當內核在所有操作完成後才會通知應用程序
四、socket的一些使用上的優化
A、緩沖區的優化,可以考慮讓應用程序使用比較小的緩沖區,但同時使用多個WSARecv
B、使用socket選項SO_SNDBUF和SO_RCVBUF設置socket緩沖區大小,如果設為0,操作體系統會使用應用程序的緩沖區,這樣避免了從系統緩沖區向用戶區復制的開銷
五、注意這些IO模型有些不光是針對socket的,其他的IO操作也可以使用,最常用使用的是WriteFile,ReadFile等函數。
其它查考網址:
http://blog.163.com/tianle_han/blog/static/6617826200821522743948/
http://blog.csdn.net/yibulianhua/article/details/5374317
⑹ linux socket網路編程怎樣收發包
1.send函數
ssize_t send( SOCKET s, const char *buf, size_t len, int flags );
(1)send先比較待發送數據的長度len和套接字s的發送緩沖的長度, 如果len大於s的發送緩沖區的長度,該函數返回SOCKET_ERROR;
(2)如果len小於或者等於s的發送緩沖區的長度,那麼send先檢查協議是否正在發送s的發送緩沖中的數據,如果是就等待協議把數據發送完,如果協議還沒有開始發送s的發送緩沖中的數據或者s的發送緩沖中沒有數據,那麼send就比較s的發送緩沖區的剩餘空間和len;
(3)如果len大於剩餘空間大小,send就一直等待協議把s的發送緩沖中的數據發送完;
(4)如果len小於剩餘空間大小,send就僅僅把buf中的數據到剩餘空間里(注意並不是send把s的發送緩沖中的數據傳到連接的另一端的,而是協議傳送的,send僅僅是把buf中的數據到s的發送緩沖區的剩餘空間里)。
注意:
(1)如果send函數數據成功,就返回實際的位元組數,如果send在數據時出現錯誤,那麼send就返回SOCKET_ERROR;如果send在等待協議傳送數據時網路斷開的話,那麼send函數也返回SOCKET_ERROR。
(2)要注意send函數把buf中的數據成功到s的發送緩沖的剩餘空間里後它就返回了,但是此時這些數據並不一定馬上被傳到連接的另一端。如果協議在後續的傳送過程中出現網路錯誤的話,那麼下一個Socket函數就會返回SOCKET_ERROR。(每一個除send外的Socket函數在執行的最開始總要先等待套接字的發送緩沖中的數據被協議傳送完畢才能繼續,如果在等待時出現網路錯誤,那麼該Socket函數就返回 SOCKET_ERROR)
(3)在Unix系統下,如果send在等待協議傳送數據時網路斷開的話,調用send的進程會接收到一個SIGPIPE信號,進程對該信號的默認處理是進程終止。
2.recv函數
ssize_t recv(int s, char *buf, size_t len, int flags);
(1)recv先等待s的發送緩沖中的數據被協議傳送完畢,如果協議在傳送s的發送緩沖
中的數據時出現網路錯誤,那麼recv函數返回SOCKET_ERROR。
(2)如果s的發送緩沖中沒有數據或者數據被協議成功發送完畢後,recv先檢查套接字s的接收緩沖區,如果s接收緩沖區中沒有數據或者協議正在接收數 據,那麼recv就一直等待,直到協議把數據接收完畢。當協議把數據接收完畢,recv函數就把s的接收緩沖中的數據到buf中。(注意:協議接收到的數據可能大於buf的長度,所以 在這種情況下要調用幾次recv函數才能把s的接收緩沖中的數據完。recv函數僅僅是數據,真正的接收數據是協議來完成的)
(3)recv函數返回其實際的位元組數。如果recv在時出錯,那麼它返回SOCKET_ERROR;如果recv函數在等待協議接收數據時網路中斷了,那麼它返回0。
注意:在Unix系統下,如果recv函數在等待協議接收數據時網路斷開了,那麼調用recv的進程會接收到一個SIGPIPE信號,進程對該信號的默認處理是進程終止。
Q&A:
(1)兩次send一次recv會發生什麼?
一次性讀取兩次send的內容。
(2)recv之後,接收緩沖區會被清空嗎?
是的。
⑺ linux網路編程中如何實現伺服器端多個read()和客戶端write( )
TCP通信的模式如下圖,比較固定,對著圖編代碼就可以了:
因為客戶端沒有指定IP地址和埠,所以其IP和埠都是內核隨機分配的。
⑻ linux tcp/ip 網路通信編程
/*************************************
文件名:server.c
linux下socket網路編程簡例-服務端程序
伺服器埠設為0x8888(埠和地址可根據實際情況更改,或者使用參數傳入)
伺服器地址設為192.168.1.104
作者:kikilizhm#163.com(將#換為@)
*/
#include<stdlib.h>
#include<sys/types.h>
#include<stdio.h>
#include<sys/socket.h>
#include<linux/in.h>
#include<string.h>
intmain()
{
intsfp,nfp;/*定義兩個描述符*/
structsockaddr_ins_add,c_add;
intsin_size;
unsignedshortportnum=0x8888;/*服務端使用埠*/
printf("Hello,welcometomyserver! ");
sfp=socket(AF_INET,SOCK_STREAM,0);
if(-1==sfp)
{
printf("socketfail! ");
return-1;
}
printf("socketok! ");
/*填充伺服器埠地址信息,以便下面使用此地址和埠監聽*/
bzero(&s_add,sizeof(structsockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr=htonl(INADDR_ANY);/*這里地址使用全0,即所有*/
s_add.sin_port=htons(portnum);
/*使用bind進行綁定埠*/
if(-1==bind(sfp,(structsockaddr*)(&s_add),sizeof(structsockaddr)))
{
printf("bindfail! ");
return-1;
}
printf("bindok! ");
/*開始監聽相應的埠*/
if(-1==listen(sfp,5))
{
printf("listenfail! ");
return-1;
}
printf("listenok ");
while(1)
{
sin_size=sizeof(structsockaddr_in);
/*accept服務端使用函數,調用時即進入阻塞狀態,等待用戶進行連接,在沒有客戶端進行連接時,程序停止在此處,
不會看到後面的列印,當有客戶端進行連接時,程序馬上執行一次,然後再次循環到此處繼續等待。
此處accept的第二個參數用於獲取客戶端的埠和地址信息。
*/
nfp=accept(sfp,(structsockaddr*)(&c_add),&sin_size);
if(-1==nfp)
{
printf("acceptfail! ");
return-1;
}
printf("acceptok! Serverstartgetconnectfrom%#x:%#x ",ntohl(c_add.sin_addr.s_addr),ntohs(c_add.sin_port));
/*這里使用write向客戶端發送信息,也可以嘗試使用其他函數實現*/
if(-1==write(nfp,"hello,welcometomyserver ",32))
{
printf("writefail! ");
return-1;
}
printf("writeok! ");
close(nfp);
}
close(sfp);
return0;
}
/*************************************
文件名:client.c
linux下socket網路編程簡例-客戶端程序
伺服器埠設為0x8888(埠和地址可根據實際情況更改,或者使用參數傳入)
伺服器地址設為192.168.1.104
作者:kikilizhm#163.com(將#換為@)
*/
#include<stdlib.h>
#include<sys/types.h>
#include<stdio.h>
#include<sys/socket.h>
#include<linux/in.h>
#include<string.h>
intmain()
{
intcfd;/*文件描述符*/
intrecbytes;
intsin_size;
charbuffer[1024]={0};/*接受緩沖區*/
structsockaddr_ins_add,c_add;/*存儲服務端和本端的ip、埠等信息結構體*/
unsignedshortportnum=0x8888;/*服務端使用的通信埠,可以更改,需和服務端相同*/
printf("Hello,welcometoclient! ");
/*建立socket使用網際網路,TCP流傳輸*/
cfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==cfd)
{
printf("socketfail! ");
return-1;
}
printf("socketok! ");
/*構造伺服器端的ip和埠信息,具體結構體可以查資料*/
bzero(&s_add,sizeof(structsockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr=inet_addr("192.168.1.104");/*ip轉換為4位元組整形,使用時需要根據服務端ip進行更改*/
s_add.sin_port=htons(portnum);/*這里htons是將short型數據位元組序由主機型轉換為網路型,其實就是
將2位元組數據的前後兩個位元組倒換,和對應的ntohs效果、實質相同,只不過名字不同。htonl和ntohl是
操作的4位元組整形。將0x12345678變為0x78563412,名字不同,內容兩兩相同,一般情況下網路為大端,
PPC的cpu為大端,x86的cpu為小端,arm的可以配置大小端,需要保證接收時位元組序正確。
*/
printf("s_addr=%#x,port:%#x ",s_add.sin_addr.s_addr,s_add.sin_port);/*這里列印出的是小端
和我們平時看到的是相反的。*/
/*客戶端連接伺服器,參數依次為socket文件描述符,地址信息,地址結構大小*/
if(-1==connect(cfd,(structsockaddr*)(&s_add),sizeof(structsockaddr)))
{
printf("connectfail! ");
return-1;
}
printf("connectok! ");
/*連接成功,從服務端接收字元*/
if(-1==(recbytes=read(cfd,buffer,1024)))
{
printf("readdatafail! ");
return-1;
}
printf("readok REC: ");
buffer[recbytes]='