一、做为 TCP 服务器需要具备的条件呢?
1.具备一个可以确知的地址( bind() ):相当于我们要明确知道移动客服的号码,才能给他们电话;
2.让操作系统知道是一个服务器,而不是客户端( listen() ):相当于移动的客服,他们主要的职责是被动接听用户电话,而不是主动打电话骚扰用户;
3.等待连接的到来( accept() ):移动客服时刻等待着,来一个客户接听一个。
接收端使用 bind() 函数,来完成地址结构与socket 套接字的绑定,这样 ip、port 就固定了,发送端即可发送数据给有明确地址( ip+port ) 的接收端。
对于 TCP 服务器编程流程,有点类似于接电话过程:
1.找个可以通话的手机(socket() )
2.插上电话卡固定一个号码( bind() )
3.职责为被动接听,给手机设置一个铃声来监听是否有来电( listen())
4. 有来电,确定双方的关系后,才真正接通不挂电话( accept() )
5. 接听对方的诉说( recv() )
6.适当给些回话( send() )
7.通信结束后,双方说再见挂电话( close())。
int bind( int sockfd, const struct sockaddr *myaddr,socklen_t addrlen );
功能:
将本地协议地址与 sockfd 绑定,这样 ip、port 就固定了
参数:
sockfd:socket 套接字myaddr: 指向特定协议的地址结构指针addrlen:该地址结构的长度
返回值:
成功:返回 0
失败:-1
注意:bind只能绑定自身的地址及端口
使用实例:
int listen(int sockfd, int backlog);
功能:
将套接字由主动修改为被动,使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接。更详细说明,请看《connect()、listen()和accept()三者的关系》。
参数:
sockfd: socket监听套接字
backlog:连接队列的长度
返回值:
成功:返回0
失败:其他
int accept( int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen );
功能:
从已连接队列中取出一个已经建立的连接,如果没有任何连接可用,则进入睡眠等待(阻塞)。更详细说明,请看《connect()、listen()和accept()三者的关系》。
参数:
sockfd: socket监听套接字
cliaddr: 用于存放客户端套接字地址结构
addrlen:套接字地址结构体长度的地址
返回值:
成功:已连接套接字。注意:返回的是一个已连接套接字,这个套接字代表当前这个连接
失败:< 0
tcp_server代码:
用windows的网络调试助手作为客户端,上面代码为服务器
运行结果:
关闭连接:close()
使用 close() 函数即可关闭套接字,关闭一个代表已连接套接字将导致另一端接收到一个 0 长度的数据包,详情请看《 TCP 四次挥手》。
做服务器时
- 关闭监听套接字( socket()和listen()之后的套接字 )将导致服务器无法接收新的连接,但不会影响已经建立的连接;
- 关闭 accept()返回的已连接套接字将导致它所代表的连接被关闭,但不会影响服务器的监听( socket()和listen()之后的套接字 )。
做客户端时
关闭连接就是关闭连接,不意味着其他。
如果客户端和服务器已经连接成功的前提下,通常的情况下,先关闭客户端,再关闭服务器,如果是先关闭服务器,立马启动服务器是,服务器绑定的端口不会立马释放(如下图),要过 1 分钟左右才会释放,为什么会这样的呢?请看《 TCP 四次挥手》。有没有方法让服务器每次启动都能立即成功?请看《端口复用》。