zoukankan      html  css  js  c++  java
  • 经常使用socket函数具体解释

    经常使用socket函数具体解释

    关于socket函数,每一个的意义和基本功能都知道,但每次使用都会去百度,參数究竟是什么,返回值代表什么意义。就是说用的少,也记得不够精确。

    每次都查半天。常常烦恼于此。索性都弄得清楚、通透,并记录下来。一来便于自己记忆,再者以防日后查阅、回想。

     

    主要介绍:socket、bind、listen、connect、accept、send、sendto、recv、recvfrom、close、shutdown

     

    网络中的进程是通过socket来通信的,那什么是socket呢?socket起源于Unix,而Unix/Linux基本哲学之中的一个就是“一切皆文件”。都能够用“打开open –> 读写write/read –> 关闭close”模式来操作。

    我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件。

    其在linux和windows环境下的头文件主要是:#include<sys/socket.h>#include<WinSock2.h>

    以下较为具体的介绍各个函数的用法,及返回值推断和处理。

    另外。若想对函数调用后内核的具体动作过程,可參考UNIX网络编程第一卷或TCPIP具体解释第二卷。

     

    1.  socket

    intsocket(int domain,int type, int protocol)

    _________________________返回值:非负描写叙述符 – 成功,-1 - 出错

    当中:

    family指明了协议族/域,通常AF_INET、AF_INET6、AF_LOCAL等;

    type是套接口类型。主要SOCK_STREAM、SOCK_DGRAM、SOCK_RAW;

    protocol一般取为0。成功时,返回一个小的非负整数值,与文件描写叙述符类似。

           对于windows环境下,在调用该函数之前需首先调用WSAStartup函数完毕对Winsock服务的初始化,如

    #include<WinSock2.h>

    WSADATA wdata;

    if ( WSAStartup(MAKEWORD(2,2), &wdata) !=0 ){

    return INVALID_SOCKET;

    }

    后面就可以调用socket函数,參数意义与linux环境一致。

     

     

    2.  bind

    intbind(int sockfd,const struct sockaddr* myaddr,socklen_t addrlen)

    _________________________返回值:0 – 成功,-1 - 出错

           当socket函数返回一个描写叙述符时,仅仅是存在于其协议族的空间中,并没有分配一个详细的协议地址(这里指IPv4/IPv6和port号的组合)。bind函数能够将一组固定的地址绑定到sockfd上。

    当中:

    sockfd是socket函数返回的描写叙述符;

    myaddr指定了想要绑定的IP和port号。均要使用网络字节序-即大端模式;

    addrlen是前面struct sockaddr(与sockaddr_in等价)的长度。

    为了统一地址结构的表示方法,统一接口函数,使得不同的地址结构能够被bind()、connect()、recvfrom()、sendto()等函数调用。但一般的编程中并不直接对此数据结构进行操作。而使用还有一个与之等价的数据结构sockaddr_in。

           通常server在启动的时候都会绑定一个众所周知的协议地址,用于提供服务。客户就能够通过它来接连server。而client能够指定IP或port也能够都不指定,未分配则系统自己主动分配。这就是为什么通常server端在listen之前会调用bind(),而client就不会调用,而是在connect()时由系统随机生成一个。

           Windows下的版本号:

        Int bind( IN SOCKET s, IN const struct sockaddr FAR * name, IN int namelen);

    3.  listen

    intlisten(int sockfd,int backlog)

    ______________________返回值:0 – 成功,-1 - 出错

                                                                                                                                                                                                           (截图来自:《UNIX网络编程第一卷》)

    两个队列之和数量不得超过backlog.

     

     

    4.  connect

    intconnect(int sockfd,conststruct sockaddr *addr, socklen_t addrlen)

    ______________________返回值:0 – 成功,-1 - 出错

           通过此函数建立于TCPserver的连接。实际是发起三次握手过程,仅在连接成功或失败后返回。參数sockfd是本地描写叙述符,addr为server地址。addrlen是socket地址长度。

    UDP的connect函数,结果与tcp调用不同样,没有三次握手过程。内核仅仅是记录对方的ip和port号。他们包括在传递给connect的套接口地址结构中,并马上返回给调用进程。

     

     

    5.  accept

    intaccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

    ______________________返回值:非负描写叙述符 – 成功,-1 - 出错

                                                                                                                                                                               (截图来自:《UNIX网络编程第一卷》)

     

     

    6.  send

    ssize_tsend(int sockfd,constvoid *buf, size_t len,int flags)

    返回值:

    >0 – 成功拷贝至发送缓冲区的字节数(可能小于len),

    -1 – 出错,并置错误号errno.

    Windows版本号:

    int send(IN SOCKET s, IN const char FAR * buf, IN int len, IN int flags)

           失败时返回 -1/SOCKET_ERROR

    当中:

           sockfd:发送端套接字描写叙述符(非监听描写叙述符)

           buf:应用要发送数据的缓存

           len:实际要发送的数据长度

           flag:一般设置为0

    每一个TCP套接口都有一个发送缓冲区,它的大小能够用SO_SNDBUF这个选项来改变。调用send函数的过程,实际是内核将用户数据拷贝至TCP套接口的发送缓冲区的过程:若len大于发送缓冲区大小。则返回-1。否则,查看缓冲区剩余空间是否容纳得下要发送的len长度,若不够,则拷贝一部分。并返回拷贝长度(指的是非堵塞send,若为堵塞send,则一定等待全部数据拷贝至缓冲区才返回,因此堵塞send返回值必然与len相等)。若缓冲区满,则等待发送,有剩余空间后拷贝至缓冲区;若在拷贝过程出现错误。则返回-1。关于错误的原因,查看errno的值。

           假设send在等待协议发送数据时出现网络断开的情况,则会返回-1。

    注意:send成功返回并不代表对方已接收到数据,假设兴许的协议传输过程中出现网络错误。下一个send便会返回-1发送错误。TCP给对方的数据必须在对方给予确认时,方可删除发送缓冲区的数据。否则,会一直缓存在缓冲区直至发送成功(TCP可靠传输数据决定的)。

     

     

    7.  sendto

    ssize_t sendto(int sockfd,const void *buf, size_t len, int flags,

                          const struct sockaddr *dst_addr, socklen_t addrlen);

           windows版本号:

    int sendto(IN SOCKET s, IN const char FAR * buf, IN int len, IN int flags, IN const struct sockaddr FAR * to, IN int tolen)

     

    通经常使用于UDP套接口。用数据报方式进行传输数据。因为无连接的数据报模式下。没有建立连接,需指明目的地址。addrlen通常为sizeof(sockaddr)的长度。成功时返回发送的字节数,失败返回-1。

           当本地与不同目的地址通信时,仅仅需指定目的地址,可使用同一个UDP套接口描写叙述符sockfd,而TCP要预先建立连接,每一个连接都会产生不同的套接口描写叙述符,体如今:client要使用不同的fd进行connect,服务端每次accept产生不同的fd。

           由于UDP没有真正的发送缓冲区,由于是不可靠连接,不必保存应用进程的数据拷贝,应用进程中的数据在沿协议栈向下传递时,以某种形式复制到内核缓冲区,当数据链路层把数据传出后就把内核缓冲区中数据拷贝删除。因此它不须要一个发送缓冲区。

    写UDP套接口的sendto/write返回表示应用程序的数据或数据分片已经进入链路层的输出队列,假设输出队列没有足够的空间存放数据,将返回错误ENOBUFS.

     

    关于TCP/UDP套接口的发送缓冲区理解:

    (1)下图展示了应用进程写数据到TCP套接口的过程:

    每个TCP套接口有一个发送缓冲区。我们能够用SO_SNDBUF套接口选项来改变这个缓冲区的大小。当应用程序调用write时,内核从应用程序进程的缓冲区中拷贝全部数据到套接口的发送缓冲区。

    如果套接口的发送缓冲区容不下应用程序的全部数据(或是应用程序的缓冲区大于套接口发送缓冲区。或是套接口发送缓冲区还有其它数据),应用进程将被挂起(睡眠)。

    这里如果套接口是堵塞的。它是通常的缺省设置(还有非堵塞的套接口)。

    内核将不从write系统调用返回,直到应用程序缓冲区中的全部数据都复制到套接口发送缓冲区。因此从写一个TCP套接口的write调用成功返回只表示我们能够又一次使用应用进程的缓冲区。

    它并不告诉我们对端的TCP或应用程序已接收到数据。

    TCP取套接口发送缓冲区的数据并把它发送给对端TCP,其过程基于TCP数据传送的全部规则。对端TCP必需确认收到数据,仅仅有收到对端的ACK,本端TCP才干删除套接口发送缓冲区中已确认的数据。TCP必需保留数据拷贝直到对端确觉得止。

    TCP以MSS大小的或更小的块把数据传递给IP,同一时候给每一个数据块安上一个TCP头部以构成TCP分节。当中的MSS是由对端通告的。当对端未通告时就用536这个值(IPv4的最小重组缓冲区字节数576减去IPv4头部字节20和TCP头部字节数20)。

    IP给每一个TCP分节安上IP头部以构成IP数据报。查找其宿IP地址的路由表项以确定外出接口。然后把数据报传递给对应的数据链路。IP可能在把数据报传递给数据链路之前将其分片,只是我们已经谈到MSS选项的目的之中的一个就是试图避免分片,而较新的实现又使用了路径MTU发现功能。每一个数据链路都有一个输出队列。假设该队列已满,那么新到的分组将被丢弃,并沿协议栈向上返回一个错误,从链路层到IP层,再从IP层到TCP层。TCP将注意到这个错误,并在以后某个时刻重传对应分片。

    应用进程并不知道这样的临时情况。

          

    (2)下图展示了应用进程写数据到UDP套接口的过程:

    这一次我们展示的套接口发送缓冲区用虚线框,由于它并不存在。

    UDP套接口有发送缓冲区大小(我们能够用SO_SNDBUF套接口选项改动),不过它不过写到套接口的UDP数据报的大小上限。

    假设应用进程写一个大于套接口发送缓冲区大小的数据包,内核将返回一个EMSGSIZE错误。

    既然UDP是不可靠的。它不必保存应用程序的数据拷贝,因此无需一个真正的发送缓冲区。

    (应用进程的数据在沿协议向下传递时,以某种形式复制到内核的缓冲区,然而数据链路层在送出这些数据后将丢弃该拷贝)

    UDP简单地给用户数据报安上它的8个字节的头部以构成UDP数据报,然后传递给IP。IPv4或IPv6给UDP数据报安上对应的IP头部以构成IP数据报。运行路由操作确定外出接口,然后直接把数据包增加数据链路层输出队列(假设适合于MTU),或者分片后再把每一个片增加数据链路层的输出队列。假设某个UDP应用进程发送大数据报。那么它比TCP应用进程更有可能分片,由于TCP会把应用数据划分成MSS大小的块,而UDP却没有对等的手段。

    从写UDP套接口的write调用成功地返回表示用户写入的数据报或其全部片段已被增加数据链路层的输出队列。假设该队列没有足够的空间存放该数据报或它的某个片段,内核通常将给应用程序返回一个ENOBUFS错误。

     

    8.  recv

    ssize_t recv(int sockfd,void *buf, size_t len,int flags)

    当中:

    sockfd:接收端套接字描写叙述符;

    buf:指定缓冲区地址。用于存储接收数据;

    len:指定的用于接收数据的缓冲区长度;

    flags:一般指定为0

    表示从接收缓冲区拷贝数据。

    成功时,返回拷贝的字节数,失败返回-1。堵塞模式下,recv/recvfrom将会堵塞到缓冲区里至少有一个字节(TCP)/至少有一个完整的UDP数据报才返回,没有数据时处于休眠状态。

    若非堵塞。则马上返回,有数据则返回拷贝的数据大小。否则返回错误-1,置错误码为EWOULDBLOCK。

     

    9.  recvfrom

    ssize_t recvfrom(int sockfd,void *buf, size_t len, int flags,

    struct sockaddr *src_addr, socklen_t *addrlen)

    windows版本号:

    int recvfrom(IN SOCKET s, OUT char FAR * buf, IN int len, IN int flags, OUT struct sockaddr FAR * from,IN OUT int FAR * fromlen)

    着重强调參数:

           sockfd:接收端套接字描写叙述

           buf:用于接收数据的应用缓冲区地址

           len:指名缓冲区大小

           flags:通常为0

           src_addr:数据来源端的地址

           addrlen:src_addr地址的长度

    注意后两个參数是输出參数,当中addrlen既是输入又是输出參数。即值-结果參数,须要在调用时,指明src_addr的长度。另外,假设不关心数据发送端的地址。能够将后两者均设置为NULL。

     

    10. close

    close缺省功能是将套接字作“已关闭”标记,并马上返回到调用进程,该套接字描写叙述符不能再为该进程所用:即不能作为read和write(send和recv)的參数,可是TCP将试着发送发送缓冲区内已排队待发的数据,然后按正常的TCP连接终止序列进行操作(断开连接4次握手-以FIN为首的4个TCP分节)。

     

    11. shutdown

    shutdown不仅能够灵活控制关闭连接的读、写或读写功能。并且会马上运行对应的断开动作(发送终止连接的FIN分节等),此时不论有多少进程共享此套接字描写叙述符,都将不能再进行收发数据。

  • 相关阅读:
    利用...来字符检測(swift)
    iOS开发中WebP格式的64位支持处理
    Hadoop与分布式开发
    Lua代码解析-写给C和C++开发人员
    logback使用
    【mysql】更新“”空字符串为NULL
    【java】代码优化点
    【java】java获取对象属性类型、属性名称、属性值
    【mybatis】mybatis中放置IN查询拼接sql过长,IN查询进行分批次查询的处理
    【java】java中替换中括号[ ]操作
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/7246631.html
Copyright © 2011-2022 走看看