一、客户端、服务端
1、客户端
客户端也称用户端,是指与服务器相应,为客户提供本地服务的程序。目前较常用的客户端有浏览器、终端柜面、ATM等自助设备。
2、服务端
服务端是为客户端服务的,服务的内容主要包括向客户端提供资源、保存客户端数据、进行大型业务逻辑处理。
二、客户端通信模型
客户端与服务端关系不见得一定建立在两台分开的机器,同一台机器也有这种主从关系的存在。提供服务的服务端及接收服务的客户端同属一台
机器,如提供网页的服务器与浏览器页面这种情况。
目前主流的两种网络连接模式为C/S和B/S模式,即客户端/服务器端和浏览器端/服务器端。所说的客户端更应该理解为C/S,即通过向客户机上写一些程序,保证即使服务器端出现故障或者维护时候,客户端依然可以在保证工作的前提下、实现脱机工作。
客户端通信主要是选择TCP做为传输层协议,也有少数选择UDP或SCTP协议的,而TCP连接有两种工作方式:短连接方式和长连接方式。短连接是当客户端有请求,会建立一个TCP连接,接收到服务器响应后,就断开连接。下次有请求时,再建立连接,收到响应后,再断开,如此循环。而长连接是客户端与服务端建立TCP连接后,会一直使用这条连接进行数据交互,直到没有数据传输或异常断开。在空闲期间,通常会使用心跳数据包保
持链路不断开。
客户端请求方式主要有一个请求、一个响应以及多个请求多个响应两种消息交互方式。一个请求、一个响应方式是客户端发出请求后,程序就阻塞在那里,直到收到服务器的响应或者超时。多个请求多个响应方式是客户端一次可以发送多个请求,而每个请求会带有一个消息标识,这样客户端收到响应时,就可以根据客户端的响应标识和请求对应起来。
三、服务端通信模型
服务端启动服务后,服务程序会一直处理接收状态,等待客户端访问,当客户端发起访问时,服务端可以选择直接受理或增加一个子进程并交给子进程进行处理。
四、 TCP的三次握手和四次握手(高阶)
1、三次握手
三次握手用于TCP建立连接。最初服务端与客户端都处于通信关闭状态,首先启动服务器端,服务器端处于监听状态。
- 第一次握手:当客户端发起请求时,此时发生第一次握手。根据TCP规定,第一次通信不能携带任何数据。客户端处于同步-发送状态。
- 第二次握手:当服务端接收到客户端发起请求后,如果同意建立连接,则发出确认报文,此时发生第二次握手。同样,这次通信不能携带任何数
据。服务端处于同步-接收状态。 - 第三次握手:TCP客户端收到确认后,还要给服务端回复确认报文,此时发生第三次握手。根据TCP规定,此时客户端可以选择是否携带数据。
发送回复确认报文,客户端处于已建立连接状态,当服务端收到回复确认报文,服务端处于已建立连接状态。
2、第三次握手存在意义
正常理解两次握手就可以达到建立连接通路的目的。第三次握手主要是为了防止如果客户端发起请求时服务端因为网络原因没有及时回答,客户端以为没有建立通路而重新发起请求,当请求正常完成后,上次的请求正常返回给客户端,如果仅需要两次握手就可以建立通路,此时又建立好连接,但并没有需要传输的数据,造成资源浪费。如果是三次握手,当客户端收到服务端响应时,不发起第三次握手则不会建立连接。
3、四次握手
四次握手用于TCP断开连接。最初服务端与客户端都处于已建立连接状态。
- 第一次握手:当客户端发起连接释放报文并停止发送数据,此时发生第一次握手。根据TCP规定,第一次通信不能携带任何数据。客户端处于终
止状态1。 - 第二次握手:当服务端接收到客户端连接释放报文后,发出确认报文,此时发生第二次握手。服务端处于关闭等待状态,此时客户端已经没有数据要发送了,但服务端若发送数据,客户端依然要接收。客户端收到服务器的确认请求后,客户端进入终止等待状态2。等待服务端发送连接释放报文。
- 第三次握手:服务端将最后的数据发送完毕后,就向客户端发送连接释放报文,服务端进入最后确认状态。
- 第四次握手:客户端收到服务端连接释放报文后,必须发出确认,此时客户端进入时间等待状态。客户端经过2*最大报文段寿命后进入关闭状态。服务端收到确认报文后进入关闭状态,服务端收到确认报文后进入关闭状态。
4、探测报文
当客户端很长时间不访问服务端时,双方不能保证连接是否正常,当达到最大保活时间(通常2小时)TCP会自动发送探测报文。 通常每75分钟发送一次,连续发送10次仍没有反应,则服务端认定客户端发生故障,服务端主动关闭连接。
五 、C语言代码实现。
/*******************************************************
* 程 序 名: TcpCliOpen.c
* 程序功能: 打开SOCKET(支持ipv4的DNS解析功能)
* 输入参数:
* ip IP地址
* port 端口号
* 输出参数:
* 返 回 值:
* <0 失败
* >0 SOCKET ID
*******************************************************/
#include "kernel/syspub.h"
int TcpCliOpen(char *ip, int port)
{
struct sockaddr_in sin;
int sock;
memset(&sin, 0x00, sizeof(sin));
/* add by suitianmou */
struct hostent *h;
/* 首字符 和 尾字符 不是数字的话,就跑DNS解析,否则不跑 */
if (!isdigit(ip[0]) && !isdigit(ip[strlen(ip)-1]) )
{
h = gethostbyname(ip);
if (h == NULL)
{
sin.sin_addr.s_addr = inet_addr(ip);
}
else
{
memcpy(&sin.sin_addr, h->h_addr, h->h_length);
}
XIPLOG("D", "DNS解析域名[%s]->IP[%s]", ip, inet_ntoa(sin.sin_addr));
}
else
{
/*输入的hostname是一个点分型的IP地址*/
sin.sin_addr.s_addr = inet_addr(ip);
}
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
XIPLOG("E", "socket : %s", strerror(errno));
return -5;
}
if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
XIPLOG("E", "connect : %s", strerror(errno));
close(sock);
return -10;
}
return sock;
}