[计算机网络]---网络编程套接字

2024-02-26 1405阅读

温馨提示:这篇文章已超过389天没有更新,请注意相关的内容是否还可用!

前言

[计算机网络]---网络编程套接字作者:小蜗牛向前冲

[计算机网络]---网络编程套接字名言:我可以接受失败,但我不能接受放弃

  如果觉的博主的文章还不错的话,还请[计算机网络]---网络编程套接字点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 

目录

 一、基础知识

1、源IP地址和目的IP地址

 2、端口号

二、网络套接字

1、网络字节序

2、socket编程接口 

三、基于tcp协议的网络通信 

1、服务器的编写 

 2、客户端的编写

3、日志报告的编写 

 四、TCP协议通讯流程

1、通信流程

2、三次握手和四次挥手 


本期学习:网络基础知识,网络套接字,基于tcp协议的网络编程,tcp协议的三次握手和四次挥手。

 一、基础知识

1、源IP地址和目的IP地址

源IP地址:

  • 源IP地址是指发起网络通信的设备或主机的IP地址。
  • 在TCP/IP协议中,源IP地址用于标识数据包的来源,使得接收方知道从哪里收到数据。
  • 源IP地址包含在网络数据包的IP头部中。

    目的IP地址:

    • 目的IP地址是指网络通信的目标设备或主机的IP地址。
    • 在TCP/IP协议中,目的IP地址用于指定数据包的目标,确保数据包被传递到正确的位置。
    • 目的IP地址同样包含在网络数据包的IP头部中。

      下面我们用唐僧取经的例子来理解:

      唐僧到女儿国,那国王问,高僧从那来,到哪里去 ,在网络中就是问源ip和目的ip。

      也可能会问高僧上一站从那来,下一站到哪里去。在网络中指的就是MAC地址。

      MAC地址:

      MAC地址(Media Access Control address),也称为物理地址或硬件地址,是网络通讯中用于唯一标识网络接口控制器(NIC,网络接口卡)的一个地址。每个网络设备的NIC都有一个全球唯一的MAC地址,这个地址在生产时被固化在硬件中。

      MAC地址的长度通常是48位(6个字节),有时也表示为64位以适应某些特定技术标准。

       MAC地址通常以十六进制数表示,每个字节之间用冒号(:)或者破折号(-)分隔,例如00:1A:2B:3C:4D:5E或00-1A-2B-3C-4D-5E。

       2、端口号

            我们光有IP地址就可以完成通信了嘛? 想象一下发qq消息的例子, 有了IP地址能够把消息发送到对方的机器上, 但是还需要有一个其他的标识来区分出, 这个数据要给哪个程序进行解析。所以就提出用端口号来标识唯一的程序(服务器)。

      端口号(port)是传输层协议的内容:

      • 端口号是一个2字节16位的整数;
      • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
      • IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
      • 一个端口号只能被一个进程占用

        问题1: 我们之前在学习系统编程的时候, 学习了 pid 表示唯一一个进程; 此处我们的端口号也是唯一表示一个进程. 那么这 两者之间是怎样的关系?


        a:系统是系统,网络是网络,这里可以达到解耦的效果

        b: 需要客户端每次都能找到服务端,而pid是在进程每次生成时随机分配的

        c:不是所以的网络进程都需要网络提供网络服务或者请求,但是所以的进程都需要用pid进行标识

        所以说:一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定


        注意:

        • 端口号范围从0到65535,其中0到1023是被知名服务占用的端口号,称为“系统端口”或“保留端口”,而1024到65535是动态或私有端口,用于一般应用程序或自定义服务(下面我们进行的测试常用8080端口)
        • 传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 "数据是谁发的, 要 发给谁"。

          二、网络套接字

          1、网络字节序

          我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏 移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢? 

          在网络中无论是发送主机还是接收主机都是将:缓冲区中的数据按内存地址从低到高的顺序发送或者接收

          因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址

          TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节

          也就是说不管这台主机是大端机还是小端机,到会按照大端机发送。

          [计算机网络]---网络编程套接字

          库函数做网络 字节序和主机字节序的转换。 

          #include
          uint32_t htonl(uint32_t hostlong);
          uint16_t htons(uint16_t hostshort);
          uint32_t ntohl(uint32_t netlong);
          uint16_t ntohs(uint16_t netshort);
          •  这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
          • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
          • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回; 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回.

            2、socket编程接口 

            API(Application Programming Interface)是一组定义在软件中不同组件之间交互的规范和工具。API可以看作是一座桥梁,它定义了如何访问或使用软件组件的方法。在软件开发中,API允许不同的程序部分之间进行通信,以便它们能够相互协作而无需详细了解彼此的内部实现。

            常见的API 

            // 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
            int socket(int domain, int type, int protocol);
            // 绑定端口号 (TCP/UDP, 服务器) 
            int bind(int socket, const struct sockaddr *address,
             socklen_t address_len);
            // 开始监听socket (TCP, 服务器)
            int listen(int socket, int backlog);
            // 接收请求 (TCP, 服务器)
            int accept(int socket, struct sockaddr* address,
             socklen_t* address_len);
            // 建立连接 (TCP, 客户端)
            int connect(int sockfd, const struct sockaddr *addr,
             socklen_t addrlen);

            sockaddr结构

             sockaddr 结构(有时候在不同的系统中会有稍微不同的变体,如 sockaddr_in)是用于表示套接字地址信息的数据结构,在网络编程中经常会遇到。它通常用于指定网络通信中的端点地址,包括 IP 地址和端口号。

            struct sockaddr {
                unsigned short sa_family;    // 地址族,如 AF_INET(IPv4)或 AF_INET6(IPv6)
                char sa_data[14];            // 地址数据
            };

            主要的字段:

            1. sa_family:用于指定地址族,表示地址的类型。例如,AF_INET 表示 IPv4 地址族,AF_INET6 表示 IPv6 地址族等。这个字段是一个无符号短整型(unsigned short)。

            2. sa_data:包含地址的具体数据,通常用于存储 IP 地址和端口号等信息。在不同的地址族下,这个字段的内容会有所不同。

            在实际使用中,为了更方便地表示 IPv4 地址,通常会使用更具体的套接字地址结构,如 sockaddr_in,它的定义如下: 

            struct sockaddr_in {
                short int sin_family;          // 地址族,如 AF_INET
                unsigned short int sin_port;   // 端口号
                struct in_addr sin_addr;       // IPv4 地址
                unsigned char sin_zero[8];     // 未使用的填充字段
            };

            三、基于tcp协议的网络通信 

            上面我们说了怎么多,下面我们就用起来,虽然我们现在还是那么清楚udp和tcp协议,但是我大概清楚了他是用来通信的。

            这里我们要实现一个简单版本的服务器为客户端提供服务,这里我们的服务仅仅需要回显信息。

            我们要写一个服务器

            tcpServer.hpp
            tcpServer.cc

            一个客户端

            tcpClient.hpp
            tcpClinet.cc
            

            一个日志报告

            log.hpp

            1、服务器的编写 

            tcpServer.hpp:这里我们要完成服务器的初始化,启动和销毁。

            服务器的初始化是通过socket创建套嵌字,bind绑定网络,在进行listen监听.

            启动要完成accept获取新链接,在执行程序任务。

            这里我们在执行任务的过程中,我们可以:

            • 让程序自己执行
            • 创建子进程执行
            • 让多线程执行
            • 让线程池执行

              这里为了让程序更好的理解,我们就让子进程执行我们serverio。 

              #pragma once
              #include 
              #include 
              #include 
              #include 
              #include 
              #include 
              #include 
              #include 
              #include 
              #include 
              #include 
              #include 
              #include "log.hpp"
              #include "Task.hpp"
              #include "ThreadPool.hpp"
              namespace server
              {
                  enum
                  {
                      USAGE_ERR = 1, // usage_err
                      SOCKET_ERR,    // sockft_err
                      BIND_ERR,      // bind_err
                      LISTEN_ERR     // listen_err
                  };
                  static const uint16_t gport = 8080;
                  static const int gbacklog = 5;
                  class TcpServer; // 这是一个前置声明,我们在类tcpServer中定义了ThreadData数据的类防止出现循环依赖
                  class ThreadData
                  {
                  public:
                      ThreadData(TcpServer *self, int sock) : _self(self), _sock(sock)
                      {
                      }
                  public:
                      TcpServer *_self;
                      int _sock;
                  };
                  class TcpServer
                  {
                  public:
                      TcpServer(const uint16_t &port = gport) : _listensock(-1), _port(port)
                      {
                      }
                      void initServer()
                      {
                          // 1 创建套接字
                          _listensock = socket(AF_INET, SOCK_STREAM, 0); // sock_stream
                          if (_listensock  0)
                              {
                                  std::cout _sock);
                      //     delete td;
                      //     return nullptr;
                      // }
                      ~TcpServer()
                      {
                      }
                  private:
                      int _listensock; // 用了监听
                      uint16_t _port;
                  };
              }

              下面是一些任务:

              #pragma once
              #include 
              #include 
              #include 
              #include 
              using namespace std;
              void serviceIO(int sock)
              {
                  char buffer[1024];
                  while (true)
                  {
                      ssize_t n = read(sock, buffer, sizeof(buffer) - 1);
                      if (n > 0)
                      {
                          // 把读到的数据当做字符串
                          buffer[n] = 0;
                          cout 
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]