常用socket函数详解

常用socket函数详解

2023年7月15日发(作者:)

常⽤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和#include下⾯较为详细的介绍各个函数的使⽤⽅法,及返回值判断和处理。另外,若想对函数调⽤后内核的详细动作过程,可参考UNIX⽹络编程第⼀卷或TCPIP详解第⼆卷。

1.

socketintsocket(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服务的初始化,如#includeWSADATA wdata;if ( WSAStartup(MAKEWORD(2,2), &wdata) !=0 ){return INVALID_SOCKET;}后⾯即可调⽤socket函数,参数意义与linux环境⼀致。

2.

bindintbind(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.

listenintlisten(int sockfd,int backlog)______________________返回值:0 – 成功,-1 - 出错 (截图来⾃:《UNIX⽹络编程第⼀卷》)两个队列之和数量不得超过backlog.

4.

connectintconnect(int sockfd,conststruct sockaddr *addr, socklen_t addrlen)______________________返回值:0 – 成功,-1 - 出错 通过此函数建⽴于TCP服务器的连接,实际是发起三次握⼿过程,仅在连接成功或失败后返回。参数sockfd是本地描述符,addr为服务器地址,addrlen是socket地址长度。UDP的connect函数,结果与tcp调⽤不相同,没有三次握⼿过程。内核只是记录对⽅的ip和端⼝号,他们包含在传递给connect的套接⼝地址结构中,并⽴即返回给调⽤进程。

5.

acceptintaccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)______________________返回值:⾮负描述符 – 成功,-1 - 出错 (截图来⾃:《UNIX⽹络编程第⼀卷》)

6.

sendssize_tsend(int sockfd,constvoid *buf, size_t len,int flags)返回值:>0 – 成功拷贝⾄发送缓冲区的字节数(可能⼩于len),-1 – 出错,并置错误号s版本: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.

sendtossize_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.

recvssize_t recv(int sockfd,void *buf, size_t len,int flags)其中:sockfd:接收端套接字描述符;buf:指定缓冲区地址,⽤于存储接收数据;len:指定的⽤于接收数据的缓冲区长度;flags:⼀般指定为0表⽰从接收缓冲区拷贝数据。成功时,返回拷贝的字节数,失败返回-1。阻塞模式下,recv/recvfrom将会阻塞到缓冲区⾥⾄少有⼀个字节(TCP)/⾄少有⼀个完整的UDP数据报才返回,没有数据时处于休眠状态。若⾮阻塞,则⽴即返回,有数据则返回拷贝的数据⼤⼩,否则返回错误-1,置错误码为EWOULDBLOCK。

9.

recvfromssize_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.

closeclose缺省功能是将套接字作“已关闭”标记,并⽴即返回到调⽤进程,该套接字描述符不能再为该进程所⽤:即不能作为read和write(send和recv)的参数,但是TCP将试着发送发送缓冲区内已排队待发的数据,然后按正常的TCP连接终⽌序列进⾏操作(断开连接4次握⼿-以FIN为⾸的4个TCP分节)。

11.

shutdownshutdown不仅可以灵活控制关闭连接的读、写或读写功能,⽽且会⽴即执⾏相应的断开动作(发送终⽌连接的FIN分节等),此时不论有多少进程共享此套接字描述符,都将不能再进⾏收发数据。

发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1689408327a243265.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信