read write 替代 send recv


Linux下C語言編程中間夾雜一篇文章吧~~

writer: demonalex
email: demonalex#dark2s.org


前言:環境是:FreeBSD 5.1-RELEASE  &&  Linux 2.6.5-1.358。



BSD Socket的I/O處理調用方式有三種基本的配搭方式,當然,你可以在自己的需要在不違背運作機制的情況
“混合”這些方式來進行I/O處理的。


1)connect()+write()+read()  [適用於TCP]
這是一個“古老”的I/O搭配了,“遠在”無名管道的工作部分就已經有詳細地記錄了,不過在這里偶還是要
講一下。write()+read()主要出現在管道I/O工作上,無論是有名管道還是無名管道,它們主要都是依靠writ
e()+read()進行基本的數據通信的,這個搭配屬於UNIX系統中非常低層的I/O系統調用了,而TCP開始設計時
就被當作是網絡上傳輸管道數據的替代品,當然也就順理成章地繼承這個基本的I/O調用搭配了。下面講解的
write()+read()調用主要是以BSD Socket下的運用方式為主。關於connect()因為在上一篇文中已作了介紹,
所以這里就不多作說明了。

必須頭:
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

關於write()
ssize_t write(int d,const void *buf,size_t nbytes);
調用成功返回成功寫入的字節數,調用失敗則返回-1。參數1為對象的句柄;參數2是寫入的內容;參數3是前
者的大小。

關於read()
ssize_t read(int d,void *buf,size_t nbytes);
正常調用返回成功讀入的字節數,當讀到句柄對象的底部時返回0,調用失敗返回-1。參數1為對象句柄;參
數2是讀入容器的地址;參數3是前者的大小。


2)sendto()+recvfrom()  [適用於TCP、UDP,多數用於UDP]
這是一個比較“游動”的I/O方式調用,僅適用於UDP傳輸(也是UDP的I/O調用的魅力所在),因為UDP本身的
特性(不需要三次握手),所以它需要一種非常靈活的傳輸模式,有了這個模式,服務端與客戶端的模式就
沒有一個很籠統的分化了(在范例中我們可以看出服務端與客戶端在程序的編排上幾乎是一致的,除了服務
端多了一個bind()調用與一個循環結構以外),程序設計者可以按照需求更巧妙地設計自己的程序。

必須頭:
#include <sys/types.h>
#include <sys/socket.h>

關於sendto()
ssize_t sendto(int s,const void *msg,size_t len,int flags,const struct sockaddr *to,socklen_t
 tolen);
調用成功返回成功寫入的字節數,調用失敗則返回-1。參數1是套接字句柄;參數2是需要發送的數據;參數3
是前者的大小;參數4是特殊傳輸標識,其值多為0;參數5是發送目的地的sockaddr結構主機地址的指針值;
參數6是前者的長度。

關於recvfrom()
ssize_t recvfrom(int s,void *buf,size_t len,int flags,struct sockaddr *from,socklen_t *fromlen
);
正常調用返回成功讀入的字節數,調用失敗返回-1。參數1是套接字句柄;參數2是成功接收到遠程傳輸過來
后的數據時放入的變量地址;參數3是前者的大小;參數4是特殊傳輸標識,其值多為0;參數5是接收到的數
據的發送端的sockaddr結構主機地址的指針值;參數6是前者長度的地址值。

/*
以前總以為sendto()+recvfrom()調用只能用於UDP傳輸,原來這個論點是錯的,這里偶做了個小實驗...服務
端用我在《BSD Socket在傳輸層中的應用范例(TCP)》一文中的服務端,而客戶端是:
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>

int main(int argc,char *argv[]){
int sock;
struct sockaddr_in addr;
char msg[101];
bzero((char *)msg,101);

if((sock=socket(AF_INET,SOCK_STREAM,0))==-1){  //這里用的是TCP
printf("fail for socket./n");
exit(-1);
}

bzero((char *)&addr,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(argv[1]);
addr.sin_port=htons(atoi(argv[2]));

printf("message:");
scanf("%100s",msg);
if(sendto(sock,msg,sizeof(msg),0,(struct sockaddr *)&addr,sizeof(addr))){
//I/O這里我用了sendto()來代替send()
printf("send is ok./n");
close(sock);
exit(0);
}else{
printf("send is fail./n");
close(sock);
exit(-1);
}
}
上面我用了sendto()來代替send(),並去除開始時的connect()調用(根據UDP的調用模式),結果發現必須
運行兩次服務端才能接收msg的數據,可見第一次sendto()代替connect()了,這樣才能正常地運作。但如果
在前面一開始就調用connect()的話sendto()是可以完全代替send()的,不過因為sendto()多了兩個參數,所
以相信還不會有人會這樣調用的,這樣的使用方法僅僅用於實驗用途。
*/


3)connect()+send()+recv()  [適用於TCP、UDP,多數用於TCP]
因為connect()調用在前一篇文中已經介紹過了,所以我就不多說了,下面主要談談send()+recv()。在TCP的
I/O傳輸中它們可以完全地取代write()+read()(而且在功能上回更優勝一些,看它的最后一個調用參數就知
道了);在UDP則不能完全取代sendto()+recvfrom(),原因顯而易見,因為send()+recv()少了“游動”地址
部分,所以如果在UDP中調用它們的話套接字根本就不知道應該發到哪里去,這也是為什么在UDP中要用conne
ct()調用作為綁定了,又因為UDP不存在所謂的“三次握手”(需要發送連接請求),所以在UDP中我們可以
把“綁定”這種行為簡單地歸結為:bind()調用綁定本地地址,connect()調用綁定遠程地址。

必須頭:
#include <sys/types.h>
#include <sys/socket.h>

關於send()
ssize_t send(int s, const void *msg, size_t len, int flags);
調用成功返回成功寫入的字節數,調用失敗則返回-1。參數1是套接字句柄;參數2是需要發送的數據;參數3
是前者的大小;參數4是特殊傳輸標識,其值多為0。

關於recv()
ssize_t recv(int s, void *buf, size_t len, int flags);
正常調用返回成功讀入的字節數,調用失敗返回-1。參數1是套接字句柄;參數2是成功接收到遠程傳輸過來
后的數據時放入的變量地址;參數3是前者的大小;參數4是特殊傳輸標識,其值多為0。

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: