IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    socket网络通信—读和写

    cjhust发表于 2014-12-04 18:59:56
    love 0

    1、知识百科

    当client和server端建立好链接后,就可以调用网络IO进行读写操作了,网络IO操作主要有下面几组:

    (1)read()和write();

    (2)recv()和send();

    (3)recvfrom()和sendto();

    (4)readv()和writev();

    (5)recvmsg()和sendmsg();

    2、数据结构

    sa_family_t

    typedef unsigned short sa_family_t;

    in_port_t

    typedef uint16_t in_port_t;

    in_addr

    struct in_addr {

    unsigned long s_addr;

    }

    sockaddr

    struct sockaddr {

    unsigned short sa_family;

     char sa_data[14];

    };

    sa_family是通信类型,最常用的值是 AF_INET;

    sa_data14字节,包含套接字中的目标地址和端口信息;

    备注:sockaddr的缺陷:sa_data把目标地址和端口信息混在一起了。

    sockaddr_in

    struct sockaddr_in

    {

    short int sin_family;

    in_port_t sin_port;

    struct in_addr sin_addr;

    unsigned char sin_zero[8];

    };

    备注:字符数组sin_zero[8]的存在是为了保证结构体struct sockaddr_in的大小和结构体struct sockaddr的大小相等,sizeof (struct sockaddr) -__SOCKADDR_COMMON_SIZE -sizeof (in_port_t) -sizeof (struct in_addr)=8;

    备注:sockaddr_in结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中。

    实例:

    struct sockaddr_in saddr;

    saddr.sin_family = AF_INET;

    saddr.sin_addr.s_addr = INADDR_ANY;

    saddr.sin_port = htons(80);

    iovec

    struct iovec {

    void *iov_base; /* Starting address */

    size_t iov_len; /* Number of bytes to transfer */

    };

    msghdr

    struct msghdr {

    void *msg_name; //TCP模式下为NULL

    socklen_t msg_namelen; //TCP模式下为0

    struct iovec *msg_iov; //缓冲区数组

    int msg_iovlen;

    void *msg_control; //辅助数据

    socklen_t msg_controllen;

    int msg_flags;

    };

    (1)msg_name和msg_namelen这两个成员用于套接字未连接的场合(譬如未连接的UDP套接字),它们类似recvfrom和sendto的第五个和第六个参数。msg_name指向一个套接字地址结构,用户存放接收者(对于sendmsg)或发送者(对于recvmsg)的协议地址。如果无需指明协议地址(例如TCP套接字或已连接UDP套接字),msg_name应置为空指针,msg_namelen对于sendmsg是一个值参数,对于recvmsg却是一个值-结果参数。

    (2)msg_iov和msg_iovlen这两个成员指定输入或输出缓冲区数组(即iovec结构数组),类似readv或writev的第二个和第三个参数。

    (3)msg_control和msg_controllen这两个成员指定可选的辅助数据的位置和大小,msg_controllen对于recvmsg是一个值-结果参数;

    备注:对于recvmsg和sendmsg,我们必须区别它们的两个标志变量,一个是传递值flags参数,另一个是所传递msghdr结构的msg_flags成员,它传递的是引用,因为传递给函数的是该结构的地址。只有recvmsg使用msg_flags成员,recvmsg被调用时,flags参数被复制到msg_flags成员,并由内核使用其值驱动接收处理过程,内核还根据recvmsg的结果更新msg_flags成员的值。sendmsg则忽略msg_flags成员,因为它直接使用flags参数驱动发送处理过程。这一点意味着如果想在某个sendmsg调用中设置MSG_DONTWAIT标志,那就把flags参数设置为该值,把msg_flags成员设置为该值不起作用。

    3、操作函数

    3.1 read

    #include

    ssize_t read(int fd,void *buf,size_t nbyte);

    函数功能:负责从fd中读取内容,当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误。

    (1)如果错误为EINTR说明读是由中断引起的;

    (2)如果是ECONNREST表示网络连接出了问题;

    3.2 write

    #include

    ssize_t write(int fd, const void*buf,size_t nbytes);

    函数功能:write函数将buf中的nbytes字节内容写入文件描述符fd,成功时返回写的字节数,失败时返回-1,并设置errno变量。

    (1)如果错误为EINTR表示在写的时候出现了中断错误;

    (2)如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接);

    3.3 recv

    #include types.h><font>

    #include socket.h><font>

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

    备注:前3个参数和read一样,不同的是flags,如果flags等于0,则用法和read一样;

    (1)MSG_DONTROUTE:不查找表;

    (2)MSG_OOB:接受或者发送带外数据;

    (3)MSG_PEEK:查看数据,并不从系统缓冲区移走数据,下次读取数据还是一样;(SSL中有用到)

    (4)MSG_WAITALL:等待所有数据;

    3.4 send

    #include types.h><font>

    #include socket.h><font>

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

    备注:前3个参数和write一样,不同的是flags,如果flags等于0,则用法和write一样;

    (1)MSG_DONTROUTE:不查找表;

    (2)MSG_OOB:接受或者发送带外数据;

    (3)MSG_PEEK:查看数据,并不从系统缓冲区移走数据,下次读取数据还是一样;(SSL中有用到)

    (4)MSG_WAITALL:等待所有数据;

    3.5 recvfrom(UDP)

    #include types.h><font>

    #include socket.h><font>

    int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);

    备注:recvfrom一般用于UDP协议中,但是如果在TCP中connect函数调用后也可以用。

    • from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口;
    • fromlen常置为sizeof (struct sockaddr);

    备注:当recvfrom返回时,fromlen包含实际存入from中的数据字节数,返回接收到的字节数或当出现错误时返回-1,并置相应的errno。

    备注:当你对于数据报socket调用了connect函数时,你也可以利用send和recv进行数据传输,但该socket仍然是数据报socket,并且利用传输层的UDP服务,但在发送或接收数据报时,内核会自动为之加上目地和源地址信息。

    3.6 sendto(UDP)

    #include types.h><font>

    #include socket.h><font>

    int sendto(int sockfd,const void *msg,int len,unsigned int flags,const struct sockaddr *to,int tolen);

    备注:sendto一般用于UDP协议中,但是如果在TCP中connect函数调用后也可以用。

    • to表示目地机的IP地址和端口号信息;
    • tolen常常被赋值为sizeof(struct sockaddr);

    备注:Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。

    3.7 readv(分散读)

    #include uio.h><font>

    ssize_t readv(int fd, const struct iovec *iov, int iovcnt);

    函数功能:可以从多个不连续的缓冲里读入,即分散读,成功则返回读的字节数,错误返回-1。(一般先填满第一个缓冲区,再填写下一个,返回的是读到的总字节数,如果遇到文件结尾,无数据可读,则返回0)

    3.8 writev(集合写)

    #include uio.h><font>

    ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

    函数功能:可以从多个不连续的缓冲里写出,即集合写,成功则返回写的字节数,错误返回-1。

    char *str0 = "hello ";

    char *str1 = "world\n";

    struct iovec iov[2];

    ssize_t nwritten;

    iov[0].iov_base = str0;

    iov[0].iov_len = strlen(str0);

    iov[1].iov_base = str1;

    iov[1].iov_len = strlen(str1);

    nwritten = writev(STDOUT_FILENO, iov, 2);

    3.9 recvmsg

    #include types.h><font>

    #include socket.h><font>

    ssize_t recvmsg(int sockfd,struct msghdr *msg,int flags);

    备注:若成功则返回读入的字节数,若出错则为-1。

    3.10 sendmsg

    #include types.h><font>

    #include socket.h><font>

    ssize_t sendmsg(int sockfd,struct msghdr *msg,int flags);

    备注:若成功则返回写出的字节数,若出错则为-1。

    4、参考资料

    Read/write和recv/send:

    http://blog.csdn.net/petershina/article/details/7946615

    recvfrom和sendto:

    http://blog.csdn.net/liangkaiyang/article/details/5931901

    sockaddr_in结构体介绍:

    http://blog.sina.com.cn/s/blog_6151984a0100etj1.html

    readv和writev:

    http://blog.chinaunix.net/uid-26822401-id-3158225.html

    http://man7.org/linux/man-pages/man2/writev.2.html

    recvmsg和sendmsg:

    http://man7.org/linux/man-pages/man2/sendmsg.2.html

    http://blog.sina.com.cn/s/blog_976bcdc201018su5.html

    socket通信:

    http://blog.csdn.net/xiaoweige207/article/details/6211577



沪ICP备19023445号-2号
友情链接