网络编程 accept
1,accept
从listen 监听队列中接受一个连接。
#include <sys/types.h>
#include <sys/socket.h>int accept(int socked, struct socked *addr, sickle_t *addrlen);
sockfd参数是执行过listen 系统调用的监听socket。
addr参数用来获取被接受连接的远端socket 地址,该socket地址的长度由addrlen参数指出。
accept成功时返回一个新的连接socket,该socket唯一地标识了被接受的这个连接,服务器可通过读写该socket来与被被接受连接对应的客户端通信。
accept失败时返回-1并设置error。
注;监听 socket 和连接 socket 的区别:
其实一个socket是一个五元组(协议,源IP,源端口,目的ip,目的端口)。
监听socket 是服务器用来监听新的连接,状态是 listen。如下图,服务器监听5678的端口。
连接socket表示服务器与客户端建立一次的连接,状态是 ESTABLISHED。如下图,client 连到服务器的5678 端口,accept返回的socket表示的就是这条连接。
2,如果监听队列中处于ESTABLISHED 状态的连接对应的客户端出现网络异常(比如掉线),或者提前退出,那么服务器对这个连接执行的accept调用是否成功???
3,模拟client提前退出,server 再accept的情况。
server在listen后,立马sleep 30秒,此过程中,client connect服务端,建立三次握手,client和server都进入established状态。client连接后,又立马close,并退出程序。此时server还在sleep中,未进入accept,刚好模拟了client在server accept前提前退出的情形。
client 程序:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>int main()
{int sockfd;int ret;sockfd = socket(AF_INET, SOCK_STREAM, 0);assert(sockfd != -1);struct sockaddr_in serAddr;serAddr.sin_family = AF_INET;serAddr.sin_port = htons(5678);serAddr.sin_addr.s_addr = inet_addr("127.0.0.1");ret = connect(sockfd, (struct sockaddr *)&serAddr, sizeof(serAddr));if(ret < 0){close(sockfd);printf("connect error\n");return -1;}else{printf("connetc succ\n"); }sleep(5);printf("client quit\n");close(sockfd);//shutdown(sockfd, SHUT_RDWR);return 0;}
服务端:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>int main()
{int listenFd, clientFd;int ret;listenFd = socket(AF_INET, SOCK_STREAM, 0);assert(listenFd != -1);struct sockaddr_in serAddr;serAddr.sin_family = AF_INET;serAddr.sin_port = htons(5678);serAddr.sin_addr.s_addr = inet_addr("127.0.0.1");ret = bind(listenFd, (struct sockaddr *)&serAddr, sizeof(serAddr));assert(ret != -1);ret = listen(listenFd, 5);assert(ret != -1);printf("listen...\n");sleep(30);printf("sleep over\n");struct sockaddr_in cliAddr;socklen_t len = sizeof(cliAddr);clientFd = accept(listenFd, (struct sockaddr *)&cliAddr, &len);if(clientFd < 0){close(listenFd);printf("accept error\n");return -1;}else{printf("client [fd: %d] [ip: %s] [port: %d] connect\n", clientFd, inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port));printf("pause before\n");pause();printf("pause after\n");close(clientFd);}close(listenFd);return 0;}
A,启动服务器监听,紧接着服务器进入sleep 30秒。
B,启动客户端,连接服务器,client进入sleep,close前。此时,已经完成了三次握手,建立了连接。
wireshark 抓包也能看出来此过程,client发起了syn连接。
C,client sleep时间到了,5秒后,close掉socket,客户端随之退出。(此时服务器还在sleep中,还未accept)
此时,client的socket状态是FIN_WAIT_2,服务器的socket状态是 CLOSE_WAIT。是由于client close动作,向服务器单方面发起了fin请求,服务端回复了ack。wireshark抓包情况:
client打印:
D,当服务器的sleep 30秒到期后,进行accept,看下服务端此时的打印情况:
说明,accept成功返回了。再次查看服务端的状态:
由此可见,accept只是从监听队列中取出连接,而不论连接处于何种状态,如上面的CLOSE_WAIT状态。
E,ctrl+c强制退出服务端后,查看服务端的状态:
wireshark的抓包如下:
奇怪的是,此时client程序早已经退出了,ctrl+c服务端,server会发送fin请求,但client居然回复了ack,相应的状态也是TIME_WAIT???
查阅书籍发现,原来连接停留在FIN_WAIT_2状态的情况可能发生在:客户端执行半关闭后,未等服务器的关闭连接就强行退出了。(上面模拟情况,client connect建立连接后,server sleep 30秒后才accept,这期间client已经退出了,未等server最后的fin请求,这时客户端属于半关闭状态)
此时客户端连接由内核来接管,可称之为孤儿连接(和孤儿进程类似)。linux为了防止孤儿连接长时间存留在内核中,定义了两个内核变量:/proc/sys/net/ipv4/tcp_max_orphans和 /proc/sys/net/tcp_fin_timeout。前者指定内核能接管的孤儿连接数目,后者指定孤儿连接在内核中的生存时间。
如果,accept成功后,server 向该连接socket发送数据会怎么样呢???(即将上图中的ctrl+c操作变成send 数据)
wireshark抓包情况:
最后发现客户端回了一个rst复位报文段,终止了此次连接,只剩下listen socket。
上面蓝色对应的是client提前退出,服务端的状态(close_wait)。
红色,是当server accept后,继续send后的情况,此时client发送了rst,导致终止了此次连接。
4,模拟client 断开网络连接。
启动客户端程序后,立即断开该客户端的网络连接(连接和断开连接的过程要在服务器启动后的30秒内完成,此时server在sleep)。结果发现accept调用也能正常返回。
A,启动服务器,并相应启动客户端。
其实已经建立了连接,立马剥掉客户端的网线。
待服务器sleep时间到,并调用accept的时候,打印如下:表示accept正常返回。
如果此时服务器send数据会怎么样???
wireshar抓包情况:
由于网络是断开的,这包数据会一直重传,重传的过程中,服务器的状态是:
依然是建立完成态,如果不是send,而是close,表示发送的是fin,那此时应该是fin_wait_1,一直重传fin报文,未收到client的ack。当达到一定重传次数后,服务器发送rst报文,终止了此次连接。
而此时client的状态是:
如果在server重发data的过程中,client网络连接上了,会怎么办呢???
表明client连上后,在某次重传后收到了该数据,并进行啦ack回复。
网络编程 accept相关推荐
- tcp/ip网络编程--accept()函数返回的套接字
tcp/ip网络编程–accept()函数返回的套接字 套接字:1)套接字是对网络中不同主机的应用进程之间进行双向通信的端点的抽象:一个套接字就是网络进程通信的一端.[1] 2)套接字是用来与另一个进 ...
- 网络编程--connect()、listen()、accept()
基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: connect()函数 对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三 ...
- linux网络编程二:基础socket, bind, listen, accept, connect
linux网络编程二:基础socket, bind, listen, accept, connect 1. 创建socket #include <sys/types.h> #inc ...
- TCP网络编程中connect()、listen()和accept()三者之间的关系
https://blog.csdn.net/tennysonsky/article/details/45621341 基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: ...
- 【Linux网络编程】TCP网络编程中connect listen和accept三者之间的关系
00. 目录 文章目录 00. 目录 01. TCP服务端和客户端流程 02. connect函数 03. listen函数 04. 三次握手 05. accept函数 06. 附录 01. TCP服 ...
- 【Linux网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系
基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: connect()函数 对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三 ...
- 【Linux网络编程学习】socket API(socket、bind、listen、accept、connect)及简单应用
此为牛客Linux C++课程和黑马Linux系统编程笔记. 1. 什么是socket 所谓 socket(套接字),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象. 一个套接字就是网络 ...
- accept 阻塞_TCP网络编程中connect()、listen()和accept()三者之间的关系
基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: connect()函数 对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三 ...
- 网络编程(1)--socket/bind/listen/accept的简单介绍
网络编程1--socket/bind/listen/accept的简单介绍 背景介绍 网络编程接口 socket bind listen accept 背景介绍 最近在学习APUE和Linux高性能服 ...
最新文章
- PL/SQL开发五年工作经验精典实例
- MongoDB中如何优雅地删除大量数据
- 5.3 使用tensorflow搭建GoogLeNet网络 笔记
- SecureCRT 全屏切换
- hfss和matlab,hfss和MATLAB联合仿真
- html5 中 video 标签,H5页面中 video 标签的坑
- catia装配体怎么把零件旋转180度_工件180度翻转装置的设计
- Java制作一个更加真实的按钮
- 概率论-多维随机变量及其分布思维导图
- [转载]GBK 汉字内码扩展规范编码表(1.0 版)
- “为了对电脑进行保护,已经阻止此应用。”
- 我的世界服务器无限开号,我的世界开挂指令大全表一览!39条命令无限可能性
- office2007 打开Excel 提示 工作表中的公式包含一个或多个无效引用的解决方法
- 知数堂全网通缉这个人
- 表单域修饰符numebr、trim、lazy
- ViT/vit/VIT详解
- TkMybatis使用学习以及Example条件设置
- 查询结构树下的所有子节点包括要查询的节点
- ADSL接入网的结构和工作方式
- CLIP:Contrastive Language-Image Pre-Training