TCP和UDP区别及原理

linux系统有丰富而稳定的网络协议栈,其范围是从协议无关层到各种网络协议的实现
计算机网络通信采用同步和异步两种方式,但传送效率最高的是同步方式。

网络模型

在这里插入图片描述

TCP/IP协议族体系结构

在tcp、ip模型中,在系统间提供可靠的数据传输的层次是网络层。

在这里插入图片描述
数据链路层
实现了网卡接口的网络驱动程序,以处理 数据在网络媒介上的传输
网络层
实现数据包的选路和转发。
两台主机是通过多个中间节点连接的,网络层就是选择这些节点,来确定两台主机间的通信路径
传输层
为两台主机上的应用程序提供端到端的通信
传输层只关心通信的起始端和目的端,不在乎数据包的中转过程
应用层
处理应用程序的逻辑

ping:应用程序,不是协议,调试网络环境

应用层协议:
telnet:远程登陆协议
DNS:机器域名到ip的转换
HTTP:超文本传输协议
规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议

DHCP:动态主机配置协议

IP协议

为数据输入/输出网络提供基本算法,为高层协议提供无连接的传输服务。(不向发送者或接收者报告包的状态,不处理所遇到的故障)
IPv4 & IPv6
IPv4协议是传统的IP协议,它的IP地址是由一个32 bits的二进制数表示的,常用点分十进制的形式表示出来(例如,172.21. 16. 115等)。但是随着互联网的飞速发展,计算机数量的激增,IP地址紧张的问题逐渐浮出水面。
目前有以下三种解决方案:
1、划分子网( Subneting):
利用子网掩码将IP地址的网络部分右移,也就是在同一网络中划分出几个不同的子网。
这样可以更加经济高效地使用IP地址,减少了主机地址的冗余。
2、保留IP地址( Reserved IP Address):
公共专网地址可在不同的单位重复使用。
缺点是保留的IP地址不能被连到Intemnet上,只能在企业内部使用,企业使用时需要有公网连接手段。
3、 IPv6:
前两种方案只能在某种条件下减缓IP地址耗竭的速度,并不能从根本上解决IP地址的紧张问题,因为它们仍然是基于IPv4协议的,地址空间的长度并没有发生改变。而只有IP6的出现才是IP地址紧张的终极解决方案。行了地址空间的范围,从而支持更多的IP

IPv6是IP协议的第6版,它在IP4的基础上扩大了地址空间范围,从根本上解决了IP地址紧张的问题。

IPv6 做了如下的改进:
1)在IPv6中,P地址空间由过去的32bits扩大到128bits,这样可表示的IP地址理论上增大至2的128次个
2)IPv6支持前缀的地址类型,不同类型的地址有不同的前级,这样地址类型可以更加方便地区分出来。
3) IPv6 支持接口的自动配置。
4) IPv6 改进了对组播的支持。
5) IPv6支持了内置的认证加密机制,提高了安全性。
6) IP6提供了IPv4到IPv6的升级方式,减少了从IPv4到IPv6升级的成本消耗。

IPv6 是IPv4的升级,其最显著的特点是增加了IP 地址的长度,从而在根本上解决了IP地址紧张的问题。
IPv6 更加适应现代复杂多变的互联网环境。

TCP(Transmission Control Protocol传输控制协议)

TCP三次握手四次挥手:
建立TCP连接时,需要服务器和客户端总共发送三个包。

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

在这里插入图片描述
TCP连接删除时,需要发送四个包
第一次挥手:Client ,设置Sequence Number和Acknowledgment Number,向 Server发送一个FIN报文段;此时,Client 进入FIN_WAIT_1状态;这表示 Client 没有数据要发送给 Server了。

第二次挥手:Server 收到了 Client 发送的FIN报文段,向 Client 回一个ACK报文段,Acknowledgment Number 为 Sequence Number 加 1;Client 进入 FIN_WAIT_2 状态;Server 告诉 Client ,我“同意”你的关闭请求

第三次挥手:Server 向 Client 发送 FIN 报文段,请求关闭连接,同时 Server 进入 CLOSE_WAIT 状态。

第四次挥手:Client 收到 Server 发送的 FIN 报文段,向 Server 发送 ACK 报文段,然后 Client 进入TIME_WAIT 状态;Server 收到 Client 的 ACK 报文段以后,就关闭连接;此时,Client等待2MSL后依然没有收到回复,则证明 Server 端已正常关闭,那好,Client 也可以关闭连接了。

在这里插入图片描述
TCP模型

在这里插入图片描述
chat.h

 #ifndef CHAT_H
 #define CHAT_H
  
 struct ChatInfo
 {
      int tofd;
      char text[1024];
 };
 typedef struct ChatInfo info;
  
 #endif

服务器:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#nclude <arpa/inet.h>
#include <string. h>
#include <pthread. h>
#include " chat. h"

void  *ClientHandler(void  *arg)
{
	int fd =*(int *)arg ,  ret ;
	printf(" 线程启动, fd = %d\n", fd);
	pthread_ detach(pthread_self());   //线程运行完自动释放资源
	info buf;
	while (1)
	{
		ret = recv(fd, &buf.,sizeof(buf),0);    //从客户端接受数据
		if (-1 == ret)
		{
			perror(" recv" );
			exit(1 ) ;
		}
	
		if (0 == ret)
 	         {
       		             printf("client %d offline!\n", fd);
 		             break;
 	         }
  
 	         if (!strcmp(buf.text, "bye"))
 	         {
 	               break;
                 }  
	         ret = send(buf.tofd, &buf, sizeof(buf), 0);
                 if (ret == -1)
                 {
      	            perror("send");
                 }
                  memset(&buf, 0, sizeof(buf));
         }
    close(fd);
 }

 int main()
 {
        int ret;
  
        //创建socket(文件)
        int sockfd = socket(PF_INET, SOCK_STREAM, 0);  
         //IPV4地址族     流式套接字(TCP)  具体的协议类型(默认0)
        if (-1 == sockfd)
        {
        	perror("socket");
                exit(1);
        }
  
        int opt = 1; 
        setsockopt(sockfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));  
         //设置地址可以被复用(方便调试)
  
        struct sockaddr_in server_addr;   //保存服务器本身的信息
        struct sockaddr_in client_addr;   //保存客户端的信息

	memset(&server_addr, 0, sizeof(server_addr));   //清空结构体
        server_addr.sin_family = PF_INET;    
        //地址族  同socket第一个参数
        server_addr.sin_port = 7000;       
          //端口号(大于1024 小于65536) 客户端会向7000端口发起连接
        //查man手册,包含头文件 man inet_addr
        server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
         //ip地址 ifconfig获取  127.0.0.1回环ip(用于测试)
  
        //绑定信息
        ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
        if (-1 == ret)
        {
  	      perror("bind");
              exit(1);
        }
	
	//设置监听队列
        ret = listen(sockfd, 10);
        if (-1 == ret)
        {
            perror("listen");
            exit(1);
        }
 
        printf("等待客户端连接...\n");
        //接受客户端的连接,并且把客户端的信息保存在结构体中
       int length = sizeof(struct sockaddr_in);

       int fd;
       while (1)
       {
            fd = accept(sockfd, (struct sockaddr *)&client_addr, &length); 
              //创建TCP连接 收发数据通过fd完成
            if (-1 == fd)
            {
                perror("accept"); 
                exit(1);
            }
            printf("接受客户端的连接 %d 客户端端口号 %d\n", fd, client_addr.sin_port);

	    pthread_t pid; 
            ret = pthread_create(&pid, NULL, ClientHandler, &fd);
            if (ret != 0)
            {
                 perror("pthread_create");
                 exit(1);
           }
            usleep(1000);
        }

       close(sockfd);   //关闭socket

     return 0;
}

客户端:

  #include <stdio.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include "chat.h"
  
 void *ClientRecv(void *arg)
  {
      int fd = *(int *)arg;
      info buf;
      int ret;
  
      while (1)
      {
           memset(&buf, 0, sizeof(buf));
           ret = recv(fd, &buf, sizeof(buf), 0);
           if (-1 == ret)
           {
               perror("recv");
               exit(1);
           }
           
           printf("\t\t%s\n", buf.text);
      }
}
 
  int main()
  { 
       int sockfd = socket(PF_INET, SOCK_STREAM, 0);
       if (-1 == sockfd)
       {
           perror("socket");
           exit(1);
       }
  
        //客户端向服务器发起连接
        struct sockaddr_in server_addr;  
        memset(&server_addr, 0, sizeof(server_addr));
        //跟服务器保持一致
        server_addr.sin_family = PF_INET;
        server_addr.sin_port = 7000;
        server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  
        int ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
        if (-1 == ret)
        {
            perror("connect"); 
            exit(1);
        }
  
        pthread_t tid;
        ret = pthread_create(&tid, NULL, ClientRecv, &sockfd);
        if (ret != 0)
        {
           perror("pthread_create");
           exit(1);
        }
     
        info buf;
        while (1)
        {
            scanf("%s%d", buf.text, &buf.tofd);
            ret = send(sockfd, &buf, sizeof(buf), 0); //write
            if (-1 == ret)
            {
                perror("send");
                exit(1);
            }
           
            if (!strcmp(buf.text, "bye"))
            {
                break;
            }
        }
        close(sockfd);   //关闭TCP连接
 
      return 0;
}

UDP(User Datagram Protocol用户数据报协议)

UDP特点:
1、沟通简单,相信网络通路默认就是很容易送达的,不容易被丢弃的
2、不会建立连接,虽然有端口号,但是监听在这个地方,谁都可以传给他数据,也可以传给任何人数据
3、不会根据网络的情况进行发包的拥塞控制,无论网络丢包丢成啥样了,它该怎么发还怎么发

UDP的实际应用:
1、网页或者APP的访问
2、实时游戏
3、移动通信领域
4、流媒体的协议(直播、视频传输)

UDP使用场景:
1、需要资源少,在网络情况比较好的内网,或者对于丢包不敏感的应用
2、不需要一对一沟通,建立连接,而是可以广播的应用
3、需要处理速度快,时延低,可以容忍少数丢包,即便网络堵塞,也毫不退缩,一往无前的时候

UDP模型
在这里插入图片描述
服务器

  #include <stdio.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/stat.h>
  #include <fcntl.h>
 
  int main()
  {
      int sockfd = socket(PF_INET, SOCK_DGRAM, 0);   //第二个参数 数据报套接字
      if (-1 == sockfd)
      {
          perror("socket");
          exit(1);
      }
  
      struct sockaddr_in server_addr;
      struct sockaddr_in client_addr;
 
      memset(&server_addr, 0, sizeof(server_addr));
      server_addr.sin_family = PF_INET;
      server_addr.sin_port = 7000;
      server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  
      int ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
      if (-1 == ret)
      {
           perror("bind");
          exit(1);
      }
  
      char buf[32] = {0};
      int length = sizeof(client_addr);
  
      //接收文件名
      recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &length);
 
      //创建并打开文件
      int fd = open(buf, O_WRONLY | O_CREAT | O_EXCL, 00700);

      while (1)
      {
          //接收客户端的消息,并且把客户端信息保存在client_addr中
          ret = recvfrom(sockfd,buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &length);
          if (-1 == ret)
          {
              perror("recvfrom");
              exit(1);
          }
     
          if (!strcmp(buf, "bye"))
          {   
              printf("文件接收完毕!\n");
              break;
          }
          write(fd, buf, strlen(buf));
  
          memset(buf, 0, sizeof(buf));
      }
 
      close(fd);
      close(sockfd);
 
      return 0;
 }

客户端

 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <sys/stat.h>
 #include <fcntl.h>
  
 int main(int argc, char *argv[])
 {
      int sockfd = socket(PF_INET, SOCK_DGRAM, 0);
      if (-1 == sockfd)
      {
          perror("socket");
          exit(1);
      }
  
      char buf[32] = {0};
      int ret;
      struct sockaddr_in server_addr;
  
      memset(&server_addr, 0, sizeof(server_addr));
      server_addr.sin_family = PF_INET;
      server_addr.sin_port = 7000;
      server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
 
      //发送文件名
      sendto(sockfd, argv[1], strlen(argv[1]), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
 
      //打开文件
      int fd = open(argv[1], O_RDONLY);
 
      while (1)
      {
           //读文件
           ret = read(fd, buf, sizeof(buf) - 1);
           if (0 == ret)
           {
              printf("文件发送完毕!\n");
              strcpy(buf, "bye");
              sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
              break;  
           }
 
          ret = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&server_addr, sizeof(server__addr));
          if (-1 == ret)
          {   
              perror("sendto");
              exit(1);
          }
      
          memset(buf, 0, sizeof(buf));
      }
      
      close(fd);
      close(sockfd);
      
      return 0;
  }
TCPUDP
面向连接面向无连接
面向字节流的,发送的时候发的是一个流,没头没尾的继承了IP的特性,基于数据报的,一个个发,一个个收
可以有拥堵控制的,可以根据网络环境调整自己的行为应用让我发,我就发,管它洪水滔天
(有状态的服务)可以精确的记着,自己发送了没有,接收到没有,发送到哪个了,应该接收到哪个了,错一点儿都不行(无状态服务)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值