锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 在线教育 / SOCKET网络通信开发公开课 / Sockets-Input / Output Operations Socket输入 / 输出操作
服务方向
人工智能数据处理
人工智能培训
kaldi数据准备
小语种语音识别
语音识别标注
语音识别系统
语音识别转文字
kaldi开发技术服务
软件开发
运动控制卡上位机
机械加工软件
软件开发培训
Java 安卓移动开发
VC++
C#软件
汇编和破解
驱动开发
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft

3.20 Sockets-Input/Output Operations Socket输入/输出操作


要点

  • write(),read()--
  • send(),recv()
  • sendto(),recvfrom()
  • sendmsg(),recvmsg()
  • sendfile()
  • socketpair()

3.20.1 write()和read()函数

  • 到现在为止,我们都是用read()和write()来处理socket描述符,这样觉得不伦不类。然而,这些函数在在处理常用文件描述符和socket描述符时,机制不一样。
  • 特殊点是,读写返回长度和要求长度相比少是正常的。可能的原因有缓冲限制(特别是写操作)。或者在读时发现socket里还没有足够的数据。
  • 这和处理管道是类似的,这不让人惊奇,因为socket基本上是一个全双工的管道,只不过对端是在网络上的任何点上。
  • 当然,有时我们要求读取很大数字的字节,因为我们不知道后面会有多少数据;在这样情况下我们有可能不想用后面数据。
  • 对于简单调用read(sd,buf,nbytes)的改进如下:
char *tmp;
int rc,n=nbytes;
tmp=buf; while((rc=read(sd,tmp,n))>0) { n-=rc; tmp+=rc; if(n<=0) break; }

这样写是确保收到足够的数据。

更简短的形式为:

for(n=nbytes,tmp=buf;n>0;tmp+=rc,n-=rc)
if((rc=read(sd,temp,n))<0) break;
  • 然而,这也不是足够好。我们没有考虑下面2个可能:
    • 读取有可能被信号打断;在这个情况下我们应该继续循环
    • 读取遇到数据结束
  • 下面是read()函数封装的更完善的版本:
ssize_t  readit(int sd,char *buf,int nbytes)
{
int n;
char *tmp;
tmp=buf;
n=nbytes;
while(n>0) 
int rc=read(sd,tmp,n);          
if(rc>0) {/*main  case,successful read of some bytes主要的分支,成功读取了一些字节数*/          
n-=rc;          
tmp+=rc;          
continue;           
}           
if(rc==0) /*end of file遇到文件结束*/
break;
/*reached here only if  error,rc<0只在错误时才能执行到这一点*/
if(errno==EINTR) /*interrupted by  a signal信号中断*/
continue;
return(-1);
}
return  (nbytes-n);
}
  • 写函数也是类似,除了没有下面的语句
if(rc==0) /*end  of file*/
break;
除非你使用了非阻塞写操作,你不会得到返回0情况。

  • 这里对完善上不再多述,但是你应该知道这种复杂的情况非常常见。信号对流程的影响一定要掌握。这是Linux下软件架构的难点,如果把握不住就把信号全部阻塞不处理。

3.20.2 send()和recv()函数

  • 使用send()可以发送数据,使用recv()可以接收数据。

#include <sys/types.h>

#include <sys/socket.h>

int send(int sd,const void *buf,size_t len,int flags);

int recv(int sd, void *buf,size_t len,int flags);

  • 这些函数要在连接后才能使用,对于客户端的连接指的是connect()函数成功调用后,对于服务器端是在accept()成功调用后,当然accept()前要调用bind()和listen()。
  • 前面三个参数和read()与write想像,但是最后一个参数可以设置些标志进行更多控制。使用flags=0就会和read(),write()一样了。
  • 在成功时返回实际发送或接收的字节数。
  • flags参数能够控制每次I/O请求。对连接进行控制要用socket选项设置,用fcntl()或ioctl()。flags里允许出现的值对于上面2个函数或有不同。
  • send()函数的flags可以是下面标志的位组合:

MSG_OOB:发送带外数据(高优先数据,后发先到);两个类型(SOCK_STREAM这样的分类)的socket和协议(TCP这样的分类)必须支持这个。对于TCP,它应该只是一个字节数据,被认为是紧急请求。

MSG_DONTROUTE:目标是在本机网络上,所以不通过网关路由。通常在检测或路由程序里使用这个标志。和设置socket的SO_DONTROUTE选项功效一样。

MSG_DONTWAIT:在I/O操作上不阻塞;如果操作会阻塞,EAGAIN会返回。这和设置socket选项O_NONBLOCK效果一样,设置要用fcntl()函数。

MSG_NOSIGNAL:在流式socket处理遇到对方中断连接时不发送SIGPIPE信号;EPIPE依然会做为错误返回出来。

MSG_CONFIRM:通知链接层,有一个对端发来的回复成功接收到了。这个只对SOCK_DRGRAM和SOCK_RAW有效,也只适用于TCP。这是Linux才扩展出来的功能。

  • 对于recv(),flags标志可以是下面位标志的组合:

MSG_OOB:考虑接收带外数据。

MSG_PEEK:在接收缓冲里检查数据,但是不从队列里丢弃。

MSG_WAITALL:阻塞,直到要求的字节数接收完全,或信号捕获到了,或连接断开,或错误发生。

MSG_NOSIGNAL:在流式socket处理遇到对方中断连接时不发送SIGPIPE信号;EPIPE依然会做为错误返回出来。

  • send()和recv()返回的错误值出自socket标准错误,请参考man。

3.20.3 sendto()和recvfrom()函数

  • 在使用UDP类型的socket时,要使用sendto()和recvfrom()函数:

#include <sys/types.h>

#include <sys/socket.h>

int sendto(int sd,const void*buf,size_t len,int flags,const struct sockaddr*to,socklen_t tolen);

int recvfrom(int sd,void*buf,size_t len,int flags,struct sockaddr*from,socklen_t*fromlen);

  • 第一个参数和flags参数和send及recv一样。
  • 在成功时,这些函数返回实际发送或接收的字节数
  • sendto的第后2个参数指定了发送目标地址,recvfrom的最后2个参数指定了接收自何处的数据。在发送情况下,后2个参数不会被修改。在接收情况下,会被修改。结果值是发送数据
  • 地址。
  • 注意recvfrom()的最后一个参数是地址结构体变量的长度;在调用这个函数前要设置为传递进去的变量的长度,在返回它它里面会写入实际地址结构体变量的长度。
  • 在客户端情况下,不需要用connect()函数来连接。也就是说sendto()代表了connect()和send()。
  • 在服务器端情况下,不需要使用listen()和accpt()函数。recvfrom()代表了listen(),accept()和recv()。
  • 这些函数适用于TCP。sendto,recvfrom只适用于UDP。
  • 错误值是socket处理里的错误常见值。
友情链接
版权所有 Copyright(c)2004-2021 锐英源软件
公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768
地址:郑州市金水区郑州大学北校区院(文化路97号院)内