Linux网络编程——协议(C/S模型进行TCP通信)

协议:

  • 一组规则。

分层模型结构:

  • OSI七层模型:物、数、网、传、会、表、应

  • TCP/IP 4层模型。网(链路层/网络接口层)、网、传、应

    1. 应用层:http、 ftp、nfs、 ssh、 te1neto 。 。
    2. 传输层:TCP、UDP
    3. 网络层:IP、ICMP、IGMP
    4. 链路层:以太网帧协议、ARP
  • B/S

      优点:缓存大量数据、协议选择灵活、速度快
      缺点:安全性、跨平台、开发工作量较小
    
  • C/S

      优点:安全性、跨平台、开发工作量较小
      缺点:不能缓存大量数据、严格遵守http
    
  • 网络传输流程:
    数据没有封装之前,是不能在网络中传递。

  • 以太网帧协议;
    ARP协议。根据Ip地址获取mac地址。
    以太网帧协议:根据mac地址,完成数据包传输。

  • IP协议:

     版本:IPv4、IPv6--4位
     TTL: time to live 。设置数据包在路由节点中的跳转上限。每经过一个路由节点,该值-1,减为O的路由,有义务将该数据包丢弃
     源IP:32位。--- 4字节          192.168.1.108 ---点分十进制  IP地址(string) ---二进制
     目的IP:32位。--- 4字节
    
    
     IP地址:可以在网络环境中,唯一标识一台主机。
     端口号:可以网络的一台主机上,唯一标识一个进程。
     ip地址+端口号。可以在网络环境中,唯一标识一个进程。
    
  • UDP:
    16位。源端口号。 2^~16= 65536
    16位:目的端口号。

  • TCP协议:
    16位: 源端口号。 2^16 = 65536
    16位: 目的端口号。
    32序号:
    32确认序号。
    6个标志位。
    16位窗口大小。 2^16 = 65536

网络字节序:

  • 小端法:(pc本地存储)高位存高地址。地位存低地址。 int a = 0x12345678

  • 大端法:(网络存储)高位存低地址。地位存高地址。

     htonl -->本地-->网络(IP)       
     		192.168.1.11 --> string --> atoi --> int --> htonl --〉网络字节序
     htons -->本地--》网络(port)      
     ntohl -->网络--》本地(IP)
     ntohs -->网络--》本地(Port)
    

IP地址转换函数:

  • int inet_pton(int af,const char *src,void *dst) ;
    af: AF_INET、AF_INET6
    src:传入,IP地址(点分十进制)
    dst:传出,转换后的网络字节序的IP地址。
    返回值:成功:1异常:0,说明src指向的不是一个有效的ip地址。失败:-1
  • const char *inet_ntop(int af,const void *src,char *dst,socklen_t
    size); 网络字节序—〉本地字节序(string IP)
    af: AF_INET、AF_INET6
    src:网络字节序IP地址
    dst:本地字节序(string IP)size:dst的大小。
    返回值:成功: dsto;失败:NULL

socaddr地址结构:

struct sockaddr_in addr ;
addr.sin_family = AF_INET/AF_INET6
addr.sin_port = htons(9527);
int dst ;
inet_pton(AF_INET,“192.157.22.45",(void *)&dst) ;
addr. sin_addr.s_addr = dst;

【*】addr.sin_addr.s_addr = hton1(INADDR_ANY ); 取出系统中有效的任意IP地址。二进制类型。

  • bind(fd,(struct sockaddr *)&addr,size);

socket函数:

include <sys/socket.h>
int socket(int domain, int type, int protoco1);	
创建一个套接字
	domain: AF_INET、AF_INET6、AF_UNIX
	type: SOCK_STREAI、SOCK_DGRAM
	protocol: 0
	返回值:
		成功:新套接字所对应文件描述符
		失败: -1 errno

int bind(int sockfd,const struct sockaddr *addr,sock1en_t adtrlen);		
给socket绑定一个地址结构(IP+port)
	sockfd: socket函数返回值
	struct sockaddr_in addr ;
	addr. sin_family = AF_INET;
	addr.sin_port = htons(8888) ;
	addr.sin_addr. s_addr = hton1 (INADDR_ANY) ;
	addr: (struct sockaddr *)&addr
	addrlen: sizeof(addr)地址结构的大小。
	返回值:
		成功:0
		失败: -1 errno

int listen(int sockfd, int back1og);	
设置同时与服务器建立连接的上限数。(同时进行3次握手的客户端数量)
	sockfd: socket函数返回值
	backlog:上限数值。最大值128.
	返回值:
		成功:0
		失败:-1 errno

int accept(int sockfd, struct sockaddr *addr,socklen_t *addr1en);
阻塞等待客户端建立连接,成功的话,返回一个与客户端成功连接的socket文件描述符
	sockfd: socket函数返回值
	addr:传出参数。成功与服务器建立连接的那个客户端的地址结构(IP+port)
		
		socklen_t clit_addr _len = sizeof(addr);
	addrlen:传入传出。&clit_addr_len
			入:addr的大小。出:客户端addr实际大小。
	返回值:
		成功:能与服务器进行数据通信的 socket对应的文件描述。
		失败:-1 , errno

int cornect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
使用现有的 socket与服务器建立连接
	sockfd: socket函数返回值
			struct sockaddr_in srv_addr ;		//服务器地址结木
			srv_addr.sin_farmi1y = AF_INET;
			srv_addr.sin_port = 9527			//跟服务器bind时设定的 port完全一致。
			inet_pton(AF_INET,“服务器的IP地址",&srv_adrr.sin_addr.s_addr);
	addr:传入参数。服务器的地址结构
	addrlen:服务器的地址结构的大小
	返回值:
		成功:0
		失败: -1 errno
如果不使用bind绑定客户端地址结构,采用"隐式绑定”.

CS模型TCP通信

服务端:

int main(int argc,char *argv[])
{
    
    
	int lfd = 0,cfd = 0;
	int ret;
	char buf[BUFSIZ];//4096
	struct sockaddr_in serv_addr,clit_addr;
	socklen_t clit_addr_len;

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(SERV_PROT);//SERV_PROT 9527
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	lfd = socket(AF_INET,SOCK_STREAM,0);		
	if(lfd = -1){
    
    
		//-1:error
	}
	
	bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
	
	listen(lfd,128);
	
	clit_addr_len = sizeof(clit_addr);
	cfd = accept(lfd,(struct sockaddr *)&clit_addr,&clit_addr_len);
	if(cfd == -1){
    
    
		//-1:error
	}
	ret = read(cfd,buf,sizeof(buf));
	//处理数据,转换成大写字母
	for(int i = 0;i < ret;i++)
		buf[i] = toupper(buf[i]);
	//
	write(cfd,buf,ret);
	close(lfd);
	close(cfd);
	return 0} 

客户端:

int main(){
    
    
	int cfd;
	char buf[BUFSIZ];
	
	struct sockaddr_in serv_addr;//服务器地址结构
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(9527);//SERV_PROT 9527
	inet_pton(AF_INET,"127.0.0.1",&serv_addr.sin.s_addr ); 
	
	cfd = socket(AF_INET,SOCK_STREAM,0);
	if(cfd == -1){
    
    
		//error
	}
	int ret = connect(cfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
	if(ret != 0){
    
    
		//error
	}
	write(cfd,"hello",5);
	read(cfd,buf,sizeof(buf)); 
	return 0;
}

TCP通信时序

在这里插入图片描述

三次握手:

  1. 主动发起连接请求端,发送 SYN标志位,请求建立连接。携带序号号、数据字节数(O)、滑动窗口大小。
  2. 被动接受连接请求端,发送ACK标志位,同时携带SYN 请求标志位。携带序号、确认序号、数据字节数(O)、滑动窗口大小
  3. 主动发起连接请求端,发送 ACK标志位,应答服务器连接请求。携带确认序号。

四次挥手:

  1. 主动关闭连接请求端,发送FIN标志位。

  2. 被动关闭连接请求端,应答ACK标志位。
    -----半关闭完成。

  3. 被动关闭连接请求端,发送FIN 标志位。

  4. 主动关闭连接请求端,应答ACK标志位。
    -----连接全部关闭

TCP状态时序图:

在这里插入图片描述

1.  主动发起连接请求端:CLOSB --发送STNM-- SND_SYNM --接收 ACK、SIN -- SEND_STN
					  --发送 ACX--ESTABLTSHED(数据通信态)
2.  主动关闭连接请求端:ESTABLISHED(数据通信态)-- 发送FIN-- FIN_WATT_1 --接收ACK
					 -- FIM_VAIT_2(半关闭)--接收对端发送FIN --FIN_VAIT_2(半关闭)-- 回发ACK
					 -- TIME_VAIT(只有主动关闭连接方,会	经历该状态)--等2MSL时长-- CLOSE

在这里插入图片描述

3.被动接收连接请求端:CLOSB -- LISTENM --接收STNM -- LISTEN --发送 ACX、STNM
					 -- SIM_RCVD--接收ACX--- ESTABLISED(数据通信态
4.被动关闭连接请求端:ESTABLISHED(数据通信态)--接收FIN ESTABLISHED(数据通
					信态)--发送ACK-- CLOSB_WAIT(说明对端【主动关闭连接端】处于半关闭
					状态)--发送FIN-- LAST_ACK -- 接收ACK -- CLOSE

2MSL时长:

一定出现在【主动关闭连接请求端】。— TIME_亚AIT

保证,最后一个ACK能成功被对端接收。(等待期间,对端没收到我发的ACK,对端会再次发送FIN请求。)

半关闭:

通信双方中,只有一端关闭通信。— FIN_WAIT_2

close (cfd) ;

shutdown(int fd, int how);

	how :
		SHUT_RD关读端
		SHUT_WR 关写端
		SHUT_RDR关读写

shutdown在关闭多个文件描述符应用的文件时,采用全关闭方法。close,只关闭一个。

猜你喜欢

转载自blog.csdn.net/Strive_LiJiaLe/article/details/128683384