C/C++ socket编程教程:1天玩转socket通信技术
一些基本概念
socket:套接字,计算机之间进行通信的一种约定。
网络编程:编写程序使两台联网的计算机相互交换数据。
典型应用:Web 服务器和浏览器。浏览器获取用户输入的URL,向服务器发起请求,服务器分析接收到的URL,将对应的网页内容返回给浏览器,浏览器再经过解析和渲染,就将文字、图片、视频等元素呈现给用户。
确定计算机位置:IP地址。有了IP地址可以找到目标计算机,但仍不能通信,端口号用来区分不同的网络程序(web ftp smtp)。端口(Port)是一个虚拟的、逻辑上的概念。
协议(Protocol)就是网络通信的约定,通信的双方必须都遵守才能正常收发数据。
数据传输方式:
1) SOCK_STREAM 表示面向连接的数据传输方式(http协议 网页)
2) SOCK_DGRAM 表示无连接的数据传输方式(QQ视频和语言)
socket() 函数用来创建套接字,确定套接字的各种属性,然后服务器端要用 bind() 函数将套接字与特定的IP地址和端口绑定起来,只有这样,流经该IP地址和端口的数据才能交给套接字处理;而客户端要用 connect() 函数建立连接。
对于服务器端程序,使用 bind() 绑定套接字后,还需要使用 listen() 函数让套接字进入被动监听状态,再调用 accept() 函数,就可以随时响应客户端的请求了。listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen() 后面的代码会继续执行,直到遇到 accept()。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。
可以使用 write()/send() 函数发送数据,使用 read()/recv() 函数接收数据。数据的接收和发送是无关的,read()/recv() 函数不管数据发送了多少次,都会尽可能多的接收数据。也就是说,read()/recv() 和 write()/send() 的执行次数可能不同。例如,write()/send() 重复执行三次,每次都发送字符串"abc",那么目标机器上的 read()/recv()可能分三次接收,每次都接收"abc";也可能分两次接收,第一次接收"abcab",第二次接收"cabc";也可能一次就接收到字符串"abcabcabc"。
数据的“粘包”问题:客户端发送的多个数据包被当做一个数据包接收。也称数据的无边界性,read()/recv() 函数不知道数据包的开始或结束标志(实际上也没有任何开始或结束标志),只把它们当做连续的数据流来处理。
TCP套接字的阻塞模式。所谓阻塞,就是上一步动作没有完成,下一步动作将暂停,直到上一步动作完成后才能继续,以保持同步性。每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区,write()/send(),read()/recv()可能会发生阻塞。
close()/closesocket()和shutdown()。
shutdown() 用来关闭连接,而不是套接字,不管调用多少次 shutdown(),套接字依然存在,直到调用 close() / closesocket() 将套接字从内存清除。
TCP三次握手
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的通信协议,数据在传输前要建立连接,传输完毕后还要断开连接。
Seq:序号
Ack:确认号
SYN:标志位,表示该数据包用来建立连接
三次握手的关键是要确认对方收到了自己的数据包,这个目标就是通过“确认号(Ack)”字段实现的。计算机会记录下自己发送的数据包序号 Seq,待收到对方的数据包后,检测“确认号(Ack)”字段,看Ack = Seq + 1是否成立,如果成立说明对方正确收到了自己的数据包。
主机A分2次(分2个数据包)向主机B传递200字节的过程。
注意Ack 号为 1301 而不是 1201,原因在于 Ack 号的增量为传输的数据字节数。假设每次 Ack号不加传输的字节数,这样虽然可以确认数据包的传输,但无法明确100字节全部正确传递还是丢失了一部分,比如只传递了80字节。
因此按如下的公式确认Ack 号:Ack号 = Seq号 + 传递的字节数 + 1
TIME_WAIT 要等待 2MSL(Maximum Segment Lifetime,报文最大生存时间) 才会进入 CLOSED 状态。ACK 包到达服务器需要 MSL 时间,服务器重传 FIN 包也需要 MSL时间,2MSL 是数据包往返的最大时间,如果 2MSL 后还未收到服务器重传的 FIN 包,就说明服务器已经收到了 ACK 包。
UDP 是非连接的传输协议,没有建立连接和断开连接的过程。UDP不必调用 listen() 和 accept() 函数。UDP中只有创建套接字的过程和数据交换的过程。