YSMull
<-- home

基本TCP套接字编程

1. connect()函数

#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);//成功返回0,出错返回-1

1.1 出错情况

  1. TCP客户没有收到SYN分节响应(超时机制),返回ETIMEDOUT错误。
  2. 目标端口没有进程在等待连接,客户收到RST(复位)分节,返回ECONNREFUSED错误(硬错误)。

    RST分节产生的条件

    1. 端口上没有正在监听的服务器
    2. TCP想取消一个连接
    3. 收到一个不存在的连接的分节
  3. TCP客户发送的SYN分节在某路由上引发“不可达”(destination unreachable)ICMP错误(软错误)(超时机制),则返回EHOSTUNREACHENETUNREACH错误。额外两种情况也触发该错误:本地系统转发表无到达远程系统的路径;connect()不等待就返回。

1.2 注意

  • connect函数导致socket由CLOSED状态转移到SYN_SENT状态,若成功,再转移到ESTABLISHED状态。
  • connect失败后导致套接字不可再用,不能对该套接字再次调用connect()需要close()

2. bind()函数

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);//成功返回0,出错返回-1

给套接字一个地址(把IP地址捆绑到套接字上),套接字是主体,地址和端口是客体

  • 若未调用bind()就调用了connect()listen(),内核就要为相应的套接字选择一个临时端口。
  • 端口号为0则内核选择端口,IP地址为通配地址(INADDR_ANY),则内核选择IP地址。
  • 常见错误:EADDRINUSE(地址已使用)

3. listen()函数

#include <sys/socket.h>
int listen(int sockfd, int backlog);//成功返回0,出错返回-1。

仅由TCP服务器调用,做两件事:

  1. socket()创建的主动套接字转为被动套接字,导致socket由CLOSED状态转移到LISTEN状态。
  2. 规定内核为相应套接字排队的最大连接数。
    TCP为监听套接字维护的两个队列

TCP三路握手和监听套接字的两个队列

当客户SYN到达时,如果队列已满,TCP则忽略该分节,不发送RST,因为队满只是暂时的。


4. accept()函数

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen)//成功返回非负描述符,出错返回-1。 由服务器调用,用于从已完成连接队列对头返回下一个已完成连接,如果已完成连接队列为空,那么进程被投入睡眠(阻塞)。
  • 函数中的第一个参数 sockfd监听套接字,函数返回的是已连接套接字
  • 暂时不清楚 cliaddr (客户进程的协议地址)和 addrlengetsockname()getpeername() 得到的地址的区别。

5. close()函数

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

通常的unix close函数,可以用来关闭套接字,终止TCP连接。
关闭过程如下图:

客户端或服务器都可以主动关闭。


TCP为监听套接字维护的两个队列