Tcp传输过程
创建套接字
- socket()打开一个网络通讯端口,如果成功的话,返回一个文件描述符
- 对于IPv4,family参数指定为AF_INET
- 对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议
- protocol传输层协议类型
- 如果socket()调用出错则返回-1
为套接字绑定地址信息
- 服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接,服务器需要调用bind绑定一个固定的网络地址和端口号
- bind()成功返回0,失败返回-1。
- bind()的作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号
- struct sockaddr *是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度
下面初始化一个地址信息结构举例:
sockaddr_in srvaddr;
bzero(&svraddr, sizeof(srvaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
srvaddr.sin_port = htons(SVR_PORT);
- 将整个结构体清零
- 设置地址类型为AF_INET
- 网络地址为INADDR_ANY,这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP 地址,这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP 地址。
- 端口号设置为SERV_PORT
服务端监听连接
- s是监听套接字描述符
- listen()声明sockfd处于监听状态, 并且最多允许有backlog个客户端处于连接等待状态, 如果接收到更多的连接请求就忽略, 这里设置不会太大
- 监听的时候,一旦有新的连接到来,操作系统会对新的连接分配一个socket,进行1对1服务
- listen()成功返回0,失败返回-1;
客户端发起连接请求
- 客户端需要调用connect()连接服务器;
- connect和bind的参数形式一致, 区别在于bind的参数是自己的地址, 而connect的参数是对方的地址;
- connect()成功返回0,出错返回-1;
服务端接受连接
- 三次握手完成后,服务器调用accept()接受连接
- 如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来
- addr是一个传出参数,accept()返回时传出客户端的地址和端口号
- 如果给addr 参数传NULL,表示不关心客户端的地址
- addrlen参数是一个传入传出参数(value-result argument),传入的是调用者提供的,缓冲区addr的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区)
- accecpt的返回值:返回新建连接的socket描述符——用于与客户端进行单独数据通信
发送数据
- sockfd:套接字描述符(服务端是新建连接的socket描述符)
- msg:要发送的数据
- len:要发送的数据长度
- flags: 0-默认阻塞发送
- 返回值:成功:返回实际发送的数据长度 失败:-1
- 如果通信双方连接断开,send函数会触发异常——SIGPIPE信号,导致进程退出
接受数据
- flags:
0 :默认阻塞接收
MSG_PEEK:从缓冲区取数据,但是数据并不从缓冲区移除 - 返回值:
>0 表示实际接收的数据长度
==0 表示连接断开
<0 出现错误 - 如果返回值为0了说明连接断开了,这时应该及时将套接字关闭