前面已經說過, 對檔案的操作函數read/write也同樣可以操作socket描述符, 就把socket描述符當作一個檔案來處理. 所以在socket上使用read/write是有意義的, 可以傳遞socket描述符到那些本來設計為處理本地檔案的函數, 還可以安排socket描述符到執行進程的子進程, 該子進程也不需要瞭解socket.
雖然read/write可以執行socket上的資料轉送, 但它不能指定選項, 不能從多個用戶端接收資料, 也不能向多個用戶端發送資料. 如果需要實現這些, 就需要使用socket提供的函數了.
1. 發送資料:
- ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
- 使用時必須已經串連.
- 參數buf, nbytes和write中的含義一致.
- flags是socket的調用標誌, 可以是:
- MSG_DONTROUTE: 不走路由, 也就是說資料不可越出本網.
- MSG_DONTWAIT: 非阻塞操作, 等價於write使用O_NONBLOCK.
- MSG_EOR: 如果協議支援, 此為記錄結束.
- MSG_OOB: 如果協議支援, 發送帶外資料.
- send成功返回時, 僅僅表示資料已經準確無誤地發送到了網路上, 而不關心接收端.
- ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags,
const struct sockaddr *destaddr, socklen_t destlen);
- destaddr指標指向目標地址的結構.
- 其他同send.
- ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
- msghdr的結構定義如下:struct msghdr
...{
void *msg_name; /**//* optional address */
socklen_t *msg_namelen; /**//* address size in bytes */
struct iovec *msg_iov; /**//* array of I/O buffers */
int msg_iovlen; /**//* number of elements in array */
void *msg_control; /**//* ancillary of data */
socklen_t msg_controllen; /**//* number of ancillary bytes */
int msg_flags; /**//* flags for received message */
.
.
.
};
- 該函數指定多重緩衝取傳輸資料, 類似於writev.
2. 接收資料:
- ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
- flags可以是:
- MSG_OOB: 如果協議支援, 接收帶外資料.
- MSG_PEEK: 返回報文內容而不真正取走報文.
- MSG_TRUNC: 即使報文被截斷, 返回的也是報文的實際長度.
- MSG_WAITALL: 等待, 直到所有的資料可用(僅限SOCK_STREAM).
- ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags,
struct sockaddr *restrict addr, socklen_t *restrict addrlen);
- ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
3. 總結:
函數sendto/recvfrom和函數send/recv很類似, 區別在於sendto/recvfrom允許在不需連線的socket上指定一個目標地址.
- 對於連線導向的socket, 目標地址是忽略的, 因為目標地址已經包含在串連中了.
- 對於不需連線的socket, 不能使用send/recv, 除非在調用connect時預先設定了目標地址. 否則, 需要使用sendto/recvfrom來完成報文的傳輸.
這裡介紹了資料轉送的所有函數, 在socket系列文章的後面會有執行個體來調用這些函數.