计算机网络2

网络通信的基本概念

  1. TCP和UDP的区别

    • TCP : 传输控制协议,面向连接的服务(类似打电话),安全、可靠(数据完整)(三次握手<发送前>、响应+重传<发送中>、四次挥手<发送后>)相对UDP慢
    • 应用范围 :
      • 对安全性、完整性有严格要求的场景(ftp、)

      三次握手

      • 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。(客户端的发送能力、服务端的接收能力是正常的。)
      • 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(seq=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;(服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。)
      • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。(客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。因此,需要三次握手才能确认双方的接收与发送能力是否正常。)
      • 完成三次握手,客户端与服务器开始传送数据。
      • 三次握手的好处
      1. 确认双方的接受能力、发送能力是否正常。
      2. 指定自己的初始化序列号,为后面的可靠传送做准备。
      3. 如果是 HTTPS 协议的话,三次握手这个过程,还会进行数字证书的验证以及加密密钥的生成。

    四次挥手

    • 由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭,确保关闭前发送完未发送的数据。
    1. TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送。
    2. 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
    3. 服务器关闭客户端的连接,发送一个FIN给客户端。
    4. 客户端发回ACK报文确认,并将确认序号设置为收到序号加1。
    • UDP : 用户数据报文协议,面向无连接的服务(类似发短信),不保证安全可靠,大多数情况下可靠

      • 应用范围 :
        • 流媒体、视频、音频
  2. 消息流 A发到B

    • A的:应用层->表示层->会话层->网络层->传输层->数据链路层->物理层->
    • B的:物理层->数据链路层->传输层->网络层->会话层->表示层->应用层
  3. 消息包

    • 当socket收到一个要发送的数据时,发送进行拆分成Bit流,然后再组成(放丢失)数据包(可能会丢包)。

套接字

  • socket是一种接口机制,可以让程序无论是用什么端口、协议,都可以从socket进出数据。他负责进程与协议之间的链接。

    1. 编程模型

      • 点对点(p2p) : 一对一通信。
      • 客户机/服务器(C/S) : 一对多通信
    2. 函数

        #include <sys/types.h>          /* See NOTES */
        #include <sys/socket.h>
        int socket(int domain, int type, int protocol);
      
      • 创建socket描述符,发数据类似写文件,收就是读
      • domain
        • AF_UNIX/AF_LOCAL
        • AF_INET
        • AF_INET6
      • type
        • SOCK_STREAM 数据流协议 TCP
        • SOCK_DGRAM 数据报协议 UDP
      • protocol
        • 特殊通信协议一般来说给0即可
      • 返回值
        • socket描述符 类似文件描述符
    3. 通信地址

      //函数接口定义的
      struct sockaddr {
        unsigned short sa_family; /* address family, AF_xxx */
        char sa_data[14]; /* 14 bytes of protocol address */
      };
      
      //实际用的
      struct sockaddr_un{
        __SOCKADDR_COMMON(sun_);// 地址类型  参看上面的domain
        char sun_path(108);// socket文件的路径
      };
      
      struct sockaddr_in {
        __SOCKADDR_COMMON(sin_);// 地址类型  参看上面的domain
        in_port_t sin_port;// 端口号,大端字节序
        struct in_addr sin_addr;// IP地址,大端4字节整数
      };
      
      struct in_addr {
      } IN_ADDR;
      
      
    4. 绑定

      • socket描述符与物理通信载体(网卡或socket文件)绑定在一起。
      • 函数
      #include <sys/types.h>          /* See NOTES */
      #include <sys/socket.h>
      int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
      
      
      • sockfd
      • addr
      • addrlen
    5. 连接

        #include <sys/types.h>          /* See NOTES */
        #include <sys/socket.h>
        int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
      
      
      • sockfd
      • addr
      • addrlen:通信地址结构的字节数
      • 返回值:
        • 本地通信:成功0,失败-1;
        • 不同通信不同
    6. 数据的接受与发送

        #include <sys/types.h>
        #include <sys/socket.h>
        //接收
        ssize_t recv(int sockfd, void *buf, size_t len, int flags);
        //发送
        ssize_t send(int sockfd, const void *buf, size_t len, int flags);
      
      • recv/send与read/write功能一样,多了flags是否阻塞功能(0阻塞,1不阻塞)
    7. 关闭套接字

      • close:如果是网络通信,端口号必不会立即回收,大概会占有3分钟左右
    8. 字节序转换

        #include <arpa/inet.h>
      
        uint32_t htonl(uint32_t hostlong);
        //把32位本机字节序转换为对应的32位网络字节序
        uint16_t htons(uint16_t hostshort);
        //把16位本机字节序转换为对应的16位网络字节序
        uint32_t ntohl(uint32_t netlong);
        //把32位网络字节序转换为对应的32位本机字节序
        uint16_t ntohs(uint16_t netshort);
        //把16位网络字节序转换为对应的16位本机字节序
      
    9. IP地址转换

        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>
        //把点分十进制IP地址(字符串)转换位32位无符号整数,使用结构指针获取
        int inet_aton(const char *cp, struct in_addr *inp);
      
        //把点分十进制IP地址(字符串)转换位32位无符号整数,返回值直接返回
        in_addr_t inet_addr(const char *cp);
      
        //把32位无符号的整数表示的IP地址,转换为点分十进制IP地址(字符串)
        char *inet_ntoa(struct in_addr in);
      
    10. 本地通信

      • 编程模型
      进程A进程B
      创建套接字(AF_LOCAL)创建套接字(AF_LOCAL)
      准备地址(sockaddr_un)准备地址(sockaddr_un)
      绑定(自己的socket与地址)连接(connect,连接进程A地址)
      接受数据发送数据
      关闭套接字关闭套接字
        #include <stdio.h>
        #include <unistd.h>
        #include <sys/socket.h>
        #include <sys/types.h>
        #include <sys/un.h>
        #include <string.h>
      
        #define SOCK_FILE "/tmp/sock"
      
        int main()
        {
          //创建
          int sockfd = socket(AF_LOCAL,SOCK_DGRAM,0);
          if(0 > sockfd)
          {
            perror("socket");
            return -1;
          }
      
          //准备地址
          struct sockaddr_un addr = {};
          addr.sun_family = AF_LOCAL;
          strcpy(addr.sun_path,SOCK_FILE);
          //绑定
          if(bind(sockfd,(struct sochaddr*)&addr,sizeof(addr)))
          {
            perror("bind");
            return -1;
          }
          //接收数据
          char buf{1024};
          while(1)
          {
            int ret = read(sockfd,buf,sizeof(buf));
            if(ret <0)
            {
              perror("read");
              return -1;
            }
            printf("read:%s\n",read);
            if(0 == strcmp("quit",buf))
            {
              printf("通信完成\n");
              break;
            }
          }
          //关闭
          close(sockfd);
          //删除
          unlink(SOCK_FILE);
        }
      
      
        #include <stdio.h>
        #include <unistd.h>
        #include <sys/socket.h>
        #include <sys/types.h>
        #include <sys/un.h>
        #include <string.h>
      
        #define SOCK_FILE "/tmp/sock"
      
        int main()
        {
          //创建
          int sockfd = socket(AF_LOCAL,SOCK_DGRAM,0);
          if(0 > sockfd)
          {
            perror("socket");
            return -1;
          }
      
          //准备地址
          struct sockaddr_un addr = {};
          addr.sun_family = AF_LOCAL;
          strcpy(addr.sun_path,SOCK_FILE);
          //连接
          if(connect(sockfd,(struct sochaddr*)&addr,sizeof(addr)))
          {
            perror("connect");
            return -1;
          }
      
            char buf{1024};
          while(1)
          {
            printf(">");
            gets(buf);
            int ret = write(sockfd,buf,strlen(buf)-1);
            if(ret <0)
            {
              perror("read");
              return -1;
            }
            if(0 == strcmp("quit",buf))
            {
              printf("通信完成\n");
              break;
            }
          }
          //关闭
          close(sockfd);
        }
      

基于TCP协议的C/S模型

  1. 函数

      #include <sys/types.h>          /* See NOTES */
      #include <sys/socket.h>
      int listen(int sockfd, int backlog);
    
    • 功能:设置等待连接的最大数量
    • sockfd:被监听的socket描述符
    • backlog:等待连接的最大数量(排队的数量)
    • 返回值:成功0,失败-1
      #include <sys/types.h>          /* See NOTES */
      #include <sys/socket.h>
      int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
    • 功能:等待连接sockfd连接
    • addr:获取连接的地址
    • addrlen:设置链接地址机构体的长度
    • 返回值:专门用于通信的描述符
  2. 编程模型

    ServerClient
    创建socket套接字创建socket套接字
    准备地址(sockaddr_in,本机地址)准备地址(服务器地址)
    绑定(bind)······
    监听(listen)······
    等待连接(accept、fork(用于服务))连接(connect)
    接收请求(recv/read)发送请求(write/send)
    响应请求(write/send)接受响应(recv/read)
    关闭(close)关闭(close)
  3. 例子

      //server
      #include <stdio.h>
      #include <unistd.h>
      #include <sys/socket.h>
      #include <netinet/in.h>
      #include <arpa/inet.h>
      #include <string.h>
    
      int main()
      {
        printf("服务器创建socketn\n");
        int sockfd = socket(AF_INET,SOCK_STREAM,0);
        if(sockfd < 0)
        {
          perror("socket");
          return -1;
        }
        printf("准备地址\n");
        struct sockaddr_in addr = {};
        addr.sin_family = AF_INET;
        addr.sin_port = htons(7777);
        addr.sin_addr.s_addr = inet_addr("本机ip地址");
        socklen_t len = sizeof(addr);
        printf("绑定scoket与地址\n");
        if(bind(sockfd,(struct sockaddr*)&addr,len))
        {
          perror("bind");
          return;
        }
        printf("设置监听\n");
        if(listen(sockfd,5))
        {
          perror("listen");
          return -1;
        }
        printf("等待客户端连接\n");
        while(1)
        {
          struct sockaddr_in addrcli = {};
          int clifd = accept(sockfd,(struct sockaddr*)&addrcli,&len);
          if(clifd < 0)
          {
            perror("accept");
            continue;
          }
          if(0 == fork())
          {
            char buf[1024] = {};
            while(1)
            {
              printf("read:");
              read(clifd,buf,sizeof(buf));
              printf("%s\n",buf);
              if(0 == strcmp("quit",buf))
              {
                close(clifd);
                return 0;
              }
              printf(">");
              gets(buf);
              write(clifd,buf,strlen(buf)+1);
            }
          }
        }
      }
    
      //clinet
      #include <stdio.h>
      #include <unistd.h>
      #include <sys/types.h>
      #include <sys/socket.h>
      #include <netinet/in.h>
      #include <arpa/inet.h>
      #include <string.h>
    
      int main()
      {
        printf("服务器创建socketn\n");
        int sockfd = socket(AF_INET,SOCK_STREAM,0);
        if(sockfd < 0)
        {
          perror("socket");
          return -1;
        }
        printf("准备地址\n");
        struct sockaddr_in addr = {};
        addr.sin_family = AF_INET;
        addr.sin_port = htons(7777);
        addr.sin_addr.s_addr = inet_addr("本机ip地址");
        socklen_t len = sizeof(addr);
        printf("连接服务器地址\n");
        if(connect(sockfd,(struct sockaddr*)&addr,len))
        {
          perror("connect");
          return;
        }
        while(1)
        {
          char buf[1024] = {};
          printf(">");
          gets(buf);
          write(sockfd,buf,strlen(buf)+1);
          if(0 == strcmp("quit",buf))
          {
            break;
          }
          printf("read:");
          read(sockfd,buf,sizeof(buf));
          printf("%s\n",buf);
        }
        close(sockfd);
      }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值