1. 程式人生 > >socket程式設計 read/write send/resv 的區別

socket程式設計 read/write send/resv 的區別

判斷斷開:

read write返回值, 成功的話返回位元組數,讀到檔案結尾返回0,失敗返回-1,攜帶errno

select返回值 -1  socket斷開

sendto/recvfrom 一般用於UDP資料報的傳送,它的引數中帶有UDP的埠和IP資訊。

其實在connect結束之後,是可以通用的

###############################################################

一旦,我們建立好了tcp連線之後,我們就可以把得到的fd當作檔案描述符來使用。
由此網路程式裡最基本的函式就是read和write函數了。
ssize_t write(int fd, const void*buf,size_t nbytes);
write

函式將buf中的nbytes位元組內容寫入檔案描述符fd.成功時返回寫的位元組數.失敗時返回-1. 並設定errno變數. 在網路程式中,當我們向套接字檔案描述符寫時有兩可能.
1)write的返回值大於0,表示寫了部分或者是全部的資料. 這樣我們用一個while迴圈來不停的寫入,但是迴圈過程中的buf引數和nbyte引數得由我們來更新。也就是說,網路寫函式是不負責將全部資料寫完之後在返回的。
2)返回的值小於0,此時出現了錯誤.我們要根據錯誤型別來處理.
如果錯誤為EINTR表示在寫的時候出現了中斷錯誤.
如果為EPIPE表示網路連接出現了問題(對方已經關閉了連線).
為了處理以上的情況,我們自己編寫一個寫函式來處理這幾種情況.

int my_write
(int fd,void *buffer,int length)
{
int bytes_left;
int written_bytes;
char *ptr;

ptr=buffer;
bytes_left=length;
while(bytes_left>0)
{
        
         written_bytes=write(fd,ptr,bytes_left);
         if(written_bytes<=0)
         {       
                 if(errno==EINTR)
                         written_bytes=0;
                 else             
                         return(-1);
         }
         bytes_left-=written_bytes;
         ptr+=written_bytes;     
}
return(0);
}

讀函式read
ssize_t read(int fd,void *buf,size_t nbyte)
read函式是負責從fd中讀取內容.當讀成功 時,read返回實際所讀的位元組數,如果返回的值是0 表示已經讀到檔案的結束了,小於0表示出現了錯誤.如果錯誤為EINTR說明讀是由中斷引起 的, 如果是ECONNREST表示網路連接出了問題. 和上面一樣,我們也寫一個自己的讀函式.

int my_read(int fd,void *buffer,int length)
{
int bytes_left;
int bytes_read;
char *ptr;
  
bytes_left=length;
while(bytes_left>0)
{
    bytes_read=read(fd,ptr,bytes_read);
    if(bytes_read<0)
    {
      if(errno==EINTR)
         bytes_read=0;
      else
         return(-1);
    }
    else if(bytes_read==0)
        break;
     bytes_left-=bytes_read;
     ptr+=bytes_read;
}
return(length-bytes_left);
}

資料的傳遞
有了上面的兩個函式,我們就可以向客戶端或者是服務端傳遞資料了.比如我們要傳遞一個結構.可以使用如下方式



struct my_struct my_struct_client;
write
(fd,(void *)&my_struct_client,sizeof(struct my_struct);


char buffer[sizeof(struct my_struct)];
struct *my_struct_server;
read(fd,(void *)buffer,sizeof(struct my_struct));
my_struct_server=(struct my_struct *)buffer;    

在網路上傳遞資料時我們一般都是把資料轉化為char型別的資料傳遞.接收的時候也是一樣的注意的是我們沒有必要在網路上傳遞指標(因為傳遞指標是沒有任何意義的,我們必須傳遞指標所指向的內容)


6.1 recv和send
recv和send函式提供了和read和write差不多的功能.不過它們提供了第四個引數來控制讀寫操作.

int recv(int sockfd,void *buf,int len,int flags)
int send(int sockfd,void *buf,int len,int flags)

前面的三個引數和read,write一樣,第四個引數可以是0或者是以下的組合
_______________________________________________________________
| MSG_DONTROUTE | 不查詢表 |
| MSG_OOB | 接受或者傳送帶外資料 |
| MSG_PEEK | 檢視資料,並不從系統緩衝區移走資料 |
| MSG_WAITALL | 等待所有資料 |
|--------------------------------------------------------------|

MSG_DONTROUTE:是send函式使用的標誌.這個標誌告訴IP.目的主機在本地網路上面,沒有必要查詢表.這個標誌一般用網路診斷和路由程式裡面.
MSG_OOB:表示可以接收和傳送帶外的資料.關於帶外資料我們以後會解釋的.

MSG_PEEK:是recv函式的使用標誌,表示只是從系統緩衝區中讀取內容,而不清除系統緩衝區的內容.這樣下次讀的時候,仍然是一樣的內容.一般在有多個程序讀寫資料時可以使用這個標誌.

MSG_WAITALL是recv函式的使用標誌,表示等到所有的資訊到達時才返回.使用這個標誌的時候recv回一直阻塞,直到指定的條件滿足,或者是發生了錯誤. 1)當讀到了指定的位元組時,函式正常返回.返回值等於len 2)當讀到了檔案的結尾時,函式正常返回.返回值小於len 3)當操作發生錯誤時,返回-1,且設定錯誤為相應的錯誤號(errno)


MSG_NOSIGNAL is a flag used by send() in some implementations of the Berkeley sockets API.

This flag requests that the implementation does not to send a SIGPIPE signal on errors on stream oriented sockets when the other end breaks the connection. The EPIPE error is still returned as normal.

Though it is in some Berkely sockets APIs (notably Linux) it does not exist in what some refer to as the reference implementation, FreeBSD, which instead uses a socket option SO_NOSIGPIPE?. 對於伺服器端,我們可以使用這個標誌。目的是不讓其傳送SIG_PIPE訊號,導致程式退出。

如果flags為0,則和read,write一樣的操作.還有其它的幾個選項,不過我們實際上用的很少,可以檢視 Linux Programmer's Manual得到詳細解釋