网络通信作为互联网的技术支持,已被广泛应用在软件开发中,无论是Web,服务端,客户端还是桌面应用,都是必须掌握的一门技术。
在软件开发层面实现远程数据交换的编程技术。
要熟悉网络编程,首先需要学习网络协议的相关知识。
什么网络协议呢?网络协议是为网络中进行数据交换定义的规则,以实现按此规范进行传输数据,就可在整个互联网中进行数据交换的目标。所以说网络协议是网络通信的基础。
网络协议中其中最著名就是TCP/IP协议族。TCP/IP协议族通常被认为是一个四层的协议系统。从上到下依次为:
从TCP/IP协议族各层次的职责来看,网路数据的传递是从上到下依次传递。以Http协议为为例,具体的数据传输过程如图所示:
当然,这只是数据的流向,实际上网络数据在传输过程中需要封装与分解,具体的过程如图所示(图片来源于百度图片):
简单来说,数据从本机上传到网络之前,会在TCP/IP协议族的每一层添加协议首部,到达目标主机后,目标主机再进行分解,从而获取需要的数据。
在众多的网络协议中,使用最广泛的应该就是TCP协议(传输控制协议)了,它是一种面向连接,可靠的,基于字节流的传输层协议。其运行过程分为三个阶段:建立连接、交换数据、断开连接。
在使用TCP协议交换数据前,需先建立一条连接,在不需要发送数据的时候,需要断开连接,以释放资源。
TCP协议建立连接时需要三次握手,其过程可使用以下场景来描述:
面试官:说一下TCP建立连接时三次握手的过程。
小明: 三次握手?
面试官: 嗯。
小明:握手完成了。
面试官:what?
没错,从小明开始问三次握手到说握手完成就是三次握手的过程,具体的过程如下:
为什么需要3次握手而不是2次呢?
因为客户端收到服务端的应答后,知道连接已建立成功,但是服务端并不知道自己发送的确认报文客户端是否收到,
所以需要客户端对服务端的报文进行确认。
服务端一定能收到客户端发送的确认报文么?
不一定,如果收不到,那么连接就不会建立,所以,3次握手只是理论上确保建立连接的次数。那能否通过4次握手呢?
不行,再握下去就是鸡生蛋,蛋生鸡的问题了。
TCP断开连接时需要四次握手,为什么需要4次呢?这是由于TCP半关闭的性质造成的。所谓半关闭,就是可以发送数据,却不能接收数据或只能接收数据,不能发送数据。
四次握手的过程:(由于主动断开连接可发送在客户端,也可发送在服务端,所以下面以A,B来区分两端)
B端在对A端进行确认的时候为什么不同时发一个FIN呢?
可以同时发。分开发是为了考虑B端在对A端确认后,可能还会给A继续发送数据的情况。
下面以一张图来描述TCP的状态变迁过程(图片来自百度图片)
图片中的所有状态对应于TCP的建立和连接过程,下面简单介绍一下这几种状态:
LISTEN: 服务端状态,表示服务端正在等待客户端的连接请求,处于监听状态;
SYN收到:服务端状态,服务端已收到客户端的连接请求,并对客户端的请求发送了ACK确认(第二次握手完成);
SYN_SENT: 客户端状态,客户端发送SYN或数据后的状态(第一次握手完成);
ESTABLISHED: 客户端对服务端的SYN进行确认后处于ESTABLISHED,服务端收到客户端发送的ACK后也会处于
ESTABLISHED状态(三次握手完成后的状态);
FIN_WAIT_1: 主动关闭的一端的状态,发送FIN后的状态(断开连接时的第一次握手完成);
FIN_WAIT_2: 主动关闭的一端的状态,收到另一端的ACK确认后的状态(断开连接时的第二次握手完成);
CLOSING:主动关闭的一端的状态,收到另一端的FIN,并对其进行确认后的状态(客户端和服务端同时关闭的情况);
TIME_WAIT: 主动关闭的一端的状态,,收到另一端的FIN或(FIN和ACK)后,对其进行确认后的状态
(断开连接时最后一次握手完成);
CLOSE_WAIT: 被动关闭的一端的状态,收到另一端发送的FIN并对其进行确认后的状态(断开连接时第二次握手完成);
LAST_ACK: 被动关闭的一端的状态,发送FIN后的状态(断开连接时第三次握手完成);
CLOSED:连接彻底断开;
TCP/IP协议族诞生之后,各个平台(Window, Unix)就按照此协议规范在系统层面为开发网络程序提供了统一的接口——Socket。通过这个面向传输层协议的系统接口,我们可通过TCP/UDP协议快速实现网络数据的交换,同时也可用来实现应用层协议,如HTTP, SSL等。
Socket是操作系统为上层应用实现网络数据交换提供的接口,我们可通过以下场景来理解:
当你给别人打电话的时候首先要确认打给谁,其次确认打哪个号码,通过这两个条件就可准确的联系到对方。那么在网络中传输数据也是同样的道理,在网络中定位主机是通过IP来实现的,一个IP代表了一台主机,但是每台主机有很多个端口号,所以要准确地与某个应用进行数据交换,除了IP地址外,还需要一个端口号。有了这两个条件,就可通过Socket实现数据交换。由此可见,Socket其实就相当于一部手机,两部手机之间建立一条通路即可实现通话。
代码实现(Linux C编程):
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int sockfd = 0, n = 0;
char recvBuff[1024];
struct sockaddr_in serv_addr;
if(argc != 2)
{
printf("n Usage: %s n",argv[0]);
return 1;
}
memset(recvBuff, '0',sizeof(recvBuff));
// 创建socket
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("n Error : Could not create socket n");
return 1;
}
// 设置IP和端口
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(5000);
if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
{
printf("n inet_pton error occuredn");
return 1;
}
// 连接到指定的IP和端口 -> 连接成功后即三次握手完成
if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("n Error : Connect Failed n");
return 1;
}
// 读数据
while ( (n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0)
{
recvBuff[n] = 0;
if(fputs(recvBuff, stdout) == EOF)
{
printf("n Error : Fputs errorn");
}
}
if(n < 0)
{
printf("n Read error n");
}
close(scokfd);
return 0;
}
代码实现:(Linux C编程)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
char sendBuff[1025];
time_t ticks;
// 创建socket
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
memset(sendBuff, '0', sizeof(sendBuff));
// 绑定IP地址和端口
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(5000);
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
// 监听客户端口的连接请求 -> 对应于状态图中的LISTEN状态
listen(listenfd, 10);
while(1)
{
// 接收客户端的请求 -> 与客户端三次握手完成
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
ticks = time(NULL);
snprintf(sendBuff, sizeof(sendBuff), "%.24srn", ctime(&ticks));
// 向客户端发送数据
write(connfd, sendBuff, strlen(sendBuff));
// 关闭socket
close(connfd);
sleep(1);
}
}
代码出处 https://www.thegeekstuff.com/2011/12/c-socket-programming/
通过以上代码,我们对socket有了一个简单的认识,同时也了解了数据交换的基本流程。后面会对基于TCP协议的HTTP协议进行一个详细的介绍。
TCP/IP协议族
传输控制协议
《TCP/IP详解 卷一》