深入理解基本套接字编程(3)

  当一个客户端SYN达到时,若这些队列是满的,TCP就忽略该分节,也即是不发送RST,这样做是暂时的,客户端将重新发送SYN,期望不就就能得到服务。假如服务端响应一个RST,客户端的connect就会返回错误,而不是让重传机制来处理,这样客户无法区分SYN的RST是因为"该端口没有在监听"还是"该端口在监听,只不过它的队列满了"。

  在三路握手完成之后,但在服务端调用accept之前到达的数据应由服务端TCP排队,最大数据量为相应已连接套接字的接收缓冲区大小。

  在TCP服务端套接字编程中,执行完listen后,而没有执行accept,客户端是可以成功建立连接的,只不过是该连接被加入到了已连接队列中,当调用accept时会被提取出来。

accept函数

#include <sys/socket.h> int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen); // 返回:成功返回已连接描述符(非负),出错-1

  accept函数有TCP服务器调用,用于从已完成队列中列头返回下一个已完成连接,如果已完成队列为空,则进程被投入睡眠(如果该套接字为阻塞方式的话)。如果accept成功,那么其返回值是由内核自动生成的一个全新套接字,代表与返回客户的TCP连接,函数的第一个参数为监听套接字,返回值为已连接套接字。

close函数

#include <unistd.h> int close(int sockfd); // 若成功返回0,出错-1

  close一个TCP套接字的默认行为是把该套接字标记为已关闭,然后立即返回到调用进程。注意,close实质把该套接字引用值减1,如果该引用值大于0,则对应的套接字不会被真正关掉。

服务器、客户端交互流程图

深入理解基本套接字编程

TCP状态转换图

深入理解基本套接字编程

getsockname和getpeername函数

#include <sys/socket.h> int getsockname(int sockfd, struct sockaddr *localaddr, &addrlen); int getpeername(int sockfd, struct sockaddr *peeraddr, &addrlen); // 返回:成功为0, 出错为-1

  getsockname获取sockfd对应的本端socket地址,并将其存储于address参数指定的内存地址,该socket长度存储于addrlen指向的变量中。getpeername获取远端的socket地址。

  UDP客户端如果调用connect之后也是可以使用getpeername的。

recv和send函数

#include <sys/socket.h> ssize recv(int sockfd, void *buff, size_t nbytes, int flags); ssize send(int sockfd, void *buff, size_t nbytes, int flags); // 返回:成功为读入或写入的字节数,出错为-1

  TCP流数据读写操作函数。flag取值如下所示:

深入理解基本套接字编程

MSG_OOB 对于send,表明将要发送带外数据,TCP连接上只有一个字节可以作为带外数据发送,对于recv,本标志表明即将要读入的是带外数据而不是普通数据。

MSG_PEEK 该标志适用于recv和recvfrom,它允许我们查看已可读取的数据,而且在系统不在recv和recvfrom返回丢弃其这些数据

  注意的是,flags参数只对send和recv的当前调用有效,当然也可以通过setsockopt系统调用永久性地修 改socket的某些属性。

recvfrom和sendto函数

#include <sys/socket.h> ssize_t recvfrom(int sockfd, void *buf, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen); ssize_t recvto(int sockfd, void *buf, size_t nbytes, int flags, struct sockaddr *to, socklen_t addrlen); // 返回:成功为读或写的字节数,失败为-1

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/6e5eba01d2a35ae1dd7ecba1d872f6c5.html