天天看點

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。      

繼續閱讀