经常使用socket函数详解

经常使用socket函数详解linux

关于socket函数,每一个的意义和基本功能都知道,但每次使用都会去百度,参数究竟是什么,返回值表明什么意义,就是说用的少,也记得不够精确。每次都查半天,常常烦恼于此。索性都弄得清楚、通透,并记录下来,一来便于本身记忆,再者以防往后查阅、回顾。编程

 

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

 

网络中的进程是经过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)dom

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

其中:tcp

family指明了协议族/域,一般AF_INET、AF_INET六、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和端口号的组合),bind函数能够将一组固定的地址绑定到sockfd上。

其中:

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

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

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

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

       一般服务器在启动的时候都会绑定一个众所周知的协议地址,用于提供服务,客户就能够经过它来接连服务器;而客户端能够指定IP或端口也能够都不指定,未分配则系统自动分配。这就是为何一般服务器端在listen以前会调用bind(),而客户端就不会调用,而是在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 - 出错

       经过此函数创建于TCP服务器的链接,实际是发起三次握手过程,仅在链接成功或失败后返回。参数sockfd是本地描述符,addr为服务器地址,addrlen是socket地址长度。

UDP的connect函数,结果与tcp调用不相同,没有三次握手过程。内核只是记录对方的ip和端口号,他们包含在传递给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要预先创建链接,每一个链接都会产生不一样的套接口描述符,体如今:客户端要使用不一样的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分节等),此时不论有多少进程共享此套接字描述符,都将不能再进行收发数据。

相关文章
相关标签/搜索