linux网络编程-TCP协议

1通讯时序(3次握手4次挥手)

1.1标志位

No.报文标识英文含义
1SYNsynchronous建立连接
2ACKacknowledgement确认
3FINfinish结束
4PSHpush传送
5RSTreset重置
6URGurgent紧急

1.2流程图

1.2.1三次握手

  • SYN 1000(0) <mss 1460>
    • 1000:发送的数据包的包号
    • (0):表示数据包中数据的大小位0
    • <mss 1460>后面数据传输最大1460
  • SYN 8000(0) ACK 1001<mss 1024>
    • 1001:表示前1001号包的数据都接受到了‘
  • ACK 8001
    • 8001 表示服务器发送的数据包8001之前都接受到了

1.2.2数据传输

  • 1001(20)ACK 8001
    • 1001(20)client发送一个数据包包号为1001数据大小的20字节
    • 8001 读到了前8001位

1.2.3四次挥手

  • FIN(1) +ACK(10)
  • ACK(FIN+1=2)
  • FIN(ACK=10)+ACK(ACk=2)
  • ACK(ACK+1=3)

在这里插入图片描述

1.3TCP数据包格式

在这里插入图片描述

  • ACK 1022:确认序号
  • SYN 1001:序号
  • win 6823:窗口带小

2滑动窗口

  • 如果客户端发的数据量特别大是,而数据要存到内核,在当服务器比较慢时,内核缓存区就被写满,所以数据要么阻塞,要莫数据覆盖所以出现流滑动窗口
  • win 4096:表示自己的滑动端口大小
  • 当数据大小大于滑动端口时 发送端先阻塞:接受端处理,然后发送新的滑动窗口大小
    在这里插入图片描述

3 TCP状态转换图

在这里插入图片描述

3.1主动端总结

  • 主动发起连接请求端:CLOSE–(SYN)–>SYN_SEND–(接受ACK,SYN)–>SYN_SEND–(ACK)–>ESTABLISHED(数据通信态)
  • 主动关闭连接请求端:ESTABLISHED(数据通信态)–>发送FIN–>FIN_WAIT_1–接受ACK–>FIN_WAIT_2(半关闭)–接受对方发送FIN–>FIN_WAIT_2(半关闭)–发送ACK–>TIME_WAIT(只有主动关闭连接方,才会有这个状态,后面的流程也只有主动关闭一段才会进行,所以一定要先关闭客户端 要不然服务器下次就打不开了)---->等2MSL时长---->CLOSE
    在这里插入图片描述

3.2被动端总结

  • 被动接受连接请求端:CLOSE---->LISTEN–(接受SYN)–>LISTEN–(发送ACK,SYN)–>SYN_RCVD–(接受ACK)–>ESTABLISHED(如果主动端一直没有ACK ,则被动端就会一直发SYN+ACK,直到主动端有响应
  • 被动关闭连接请求端:ESTABLISHED–(接受FIN)–>ESTABLISHED–(发送ACK)–>CLOSE_WAIT(说明对段【主动关闭连接端】,处于半关闭状态)–(发送FIN)–>LAST_ACK–(接受ACK)–>CLOSE
    在这里插入图片描述

4 2MSL时长

  • 存在意义
    • 保证最后一个ACK成功被对端接受(等待期间,对端没有接受我发的ACK,读端会再次发送FIN请求)
      -一定出现在主动关闭连接请求端

5TCP服务器

5.1TCP基本流程

在这里插入图片描述

5.2客户端 流程

  • 打开套节字
connfd = socket()
  • 连接服务器
connect(connfd,...);// 阻塞
  • 写入读取数据
write(connfd)/read(connfd)
  • 关闭套节字
close(connfd)

5.3服务器流程

  • 打开监听套节字
listenfd = socket()
  • 设置监听套节字地址
struct sockaddr_in
  • 绑定
bind(listenfd)
  • 设置监听上限
listen(listenfd,backlog)
  • 阻塞监听 打开连接套节字
connfd = accept(listenfd,...) // 阻塞
  • 读写数据
read(connfd)/write(connfd)
recv(connfd)/send(connfd)
recvfrom(connfd)/sendto(connfd);
  • 关闭套节字
close(connfd)
close(listenfd)

5.4server代码实现

#include"wrap.h"
#define SERVER_PORT 9521
int main(){
	//创建套接字
	int lfd=0,cfd=0;
	struct sockaddr_in serv_addr,client_addr;
	socklen_t cli_addr_len;
	int ret;
	char buf[BUFSIZ];
	char client_IP[1024];
	
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_port=htons(SERVER_PORT);
	serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	
	lfd=socket(AF_INET,SOCK_STREAM,0);
	int flag = 1;//设置端口复用
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&flag,sizeof(flag)); 	
	int ret1=bind(lfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
	if(ret1!=0){
		sys_error("bind error");
	}
	Listen(lfd,128);
	
	cli_addr_len=sizeof(client_addr);
	cfd=accept(lfd,(struct sockaddr*)&client_addr,&cli_addr_len);
	if(cfd== -1){
		sys_error("accpet error");
	}
	printf("accept success\n");
	while(1){	
		ret=read(cfd,buf,sizeof(buf));
		write(STDOUT_FILENO,buf,ret);
		int i;
		for(i=0;i<ret;i++){
			buf[i]=toupper(buf[i]);
		}
		write(cfd,buf,ret);
	}
	close(lfd);
	close(cfd);
return 1;
}

5.5 端口/地址复用

使用下面的代码,是为了避免出现Bind error: Address already in use

int flag = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));  

启用SO_REUSEADDR选项后,bind()函数将允许地址的立即重用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值