IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 服务器和客户端的套接字通信 -> 正文阅读

[网络协议]服务器和客户端的套接字通信

服务器和客户端的套接字通信

大家好,我是小学五年级在读的蒟蒻,专注于后端,一起见证蒟蒻的成长,您的评论与赞与关注是我的最大动力,如有错误还请不吝赐教,万分感谢。一起支持原创吧!纯手打有笔误还望谅解。

  • TCP/IP的四层参考模型,从上到下分别是:应用层,传输层,网络层,网络接口层。

    • 应用层协议:HTTP,FTP,DNS等

    • 传输层协议:TCP,UDP

    • 网络互连层协议:IP,ARP,RARP,ICMP

    • 网络接口层协议:Ethernet,ISDN等

  • 在TCP/IP四层参考模型中,从上往下有四种层次:应用层,传输层,网络层,网络接口层,应用层包括HTTP,FTP,DNS等协议,而传输层包括TCP,UDP两种协议,网络层则包含IP,ARP等协议,网络接口层较为底层。

  • 传输层的作用

    • 传输层的根本目的是在网络层提供的数据通信服务基础上,实现主机的进程间通信的可靠服务。

    • 有以下两个要点:位于两个主机内部的两个应用进程之间提供通信服务,提供可靠的通信服务

  • 套接字(socket)

    • 是一个套用C语言写成的应用程序开发库,实现进程间通信和网络编程

    • 套接字是一个抽象层,应用程序可以通过它发生和接收数据,可对其进行想对文件一样的打开,读写和关闭等操作。套接字允许应用程序与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合

  • 端口(port)

    • 在计算机中,端口大致有两种意思:一是物理意义上的端口,比如RJ45,集线器,交换机,路由器等

    • 二是逻辑意义上的端口,一般指TCP/IP协议中的端口,端口号的范围从0到65535,比如浏览网页的80端口,FTP服务的21端口

    • 在TCP中一般都是软件形式的端口,根据提供的服务不同,一般可分为两种

      • TCP端口

        • 特点

          • 面向连接的传输服务。打电话式,会话式通信

          • 面向字节流传输的通信(而UDP是面向报文通信)。字节管道,字节按序传输和到达

          • 全双工通信.一个应用进程可以同时接收和发送数据,捎带确认;通信双方都设置有发送和接收缓冲区,应用程序将要发送的数据字节提交给发送缓冲区,实际发送由TCP协议控制,接收方收到数据字节后将它接收放在缓冲区,等待高级应用程序读取

          • 可建立多个并发的TCP连接.如Web服务器可同时与多个用户程序建立连接会话.

          • 可靠传输服务.不丢失数据,保持数据有序,向上不重复提交数据(通过确认机制,拥塞控制等方式实现),想象一下ATM机转账应用就需要上述的可靠性

      • UCP端口

        • 特点

          • 无连接:在发送数据之前不需要建立连接,因此减少了开销和发送数据前的时间

          • 仅最大努力交付:即不保证可靠交付,因此主机不需要维持复杂的连接状态表

          • 面对报文的:UDP对于应用层传递下来的报文,既不合并,也不拆分,而是保留这些报文的编辑。UDP对于应用程序提交的报文,添加头部后就向下提交给网络层

          • 支持多对多的交互通信

        • 适用场景

          • 适用于少量(几百个字节)的数据

          • 对性能的要求高于数据完整的要求,如播放视频,P2P,DNS等

          • 需要”简短快捷 “的数据交换 简单的请求与应答报文交互,如在线游戏

          • 需要多播和广播的应用,源主机以恒定的速率发送报文,拥塞时允许丢弃部分报文,如本地广播,隧道VPN。

    • 端口分类

      • 熟知端口号:给每种服务器分配的确定的全局端口号,每个用户进程都知道的相应的服务器进程的熟知端口号,范围是0-1023,它是统一分配和控制的

      • 注册端口号:在IANA注册的端口号,数值范围为1024-49151

      • 临时端口号:客户端程序使用的零时端口号,它是运行在客户端上的TCP/IP软件随机选取的,范围在49152-65535

      • 平时网络编程,服务器一般使用注册端口号,而客户端的端口号则是系统随机分配的,即临时端口号

    • TCP报文图

      重点的标志位和意义

  • TCP的三次握手过程

    • TCP的连接包括连接建立,报文传输,连接释放三个阶段,其中连接建立的三次握手过程较为重要.

    • 建立三次握手的过程

      • 客户端准备发起一次TCP连接,首先向服务器发送第一个"SYN"报文(控制位SYN=1)

      • 服务器收到SYN报文后,如果同意建立连接,则向客户端发送第二个"SYN+ACK"报文(控制位 SYN=1,ACK=1),该报文表示对第一个SYN报文请求的确认

      • 接收到SYN+ACK报文后,客户端发送第三个ACK报文,表示对SYN+ACK报文的确认

  • 用C++进行TCP套接字网络编程

    • 网络编程中我们一般会使用C/S架构,即包含服务器端和客户端
  • TCP网络编程的服务器端

    • 服务器端一般先用socket创建一个套接字,然后用bind给这个套接字绑定地址(IP和端口号),然后调用listen把这个套接字设置为监听状态,随后调用accept函数从已完成连接队列中取出成功建立连接的套接字,以后就在这个新的套接字上调用spend,recv来发送数据,接收数据,最后调用close来断开连接释放资源即可

      过程

  • TCP网络编程的客户端

    • 与服务器不同,客户端并不需要bind绑定地址,因为端口号是系统自动分配的,而且客户端也不需要设置监听的套接字,因此也不需要listen.客户端在用socket创建套接字后直接调用connect向服务器发起连接即可,connect函数通知Linux内核完成TCP三次连接握手,最后将连接的结果作为返回值.成功建立连接后我们就可以调用send和recv来发送数据,接收数据,最后调用close来断开连接释放资源

      过程

  • 完整流程

    TCP网络编程的整体流程

  • TCP网络编程的相关数据结构

    • 地址结构:

      • sockaddr

        • 在头文件 #include<sys/socket.h> 中定义

        • 缺陷:sa_data把目标地址和端口信息混在一起了

          struct sockaddr {  
               sa_family_t sin_family;//地址族
              char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息               
             }; 
          
      • sockaddr_in

        • 在头文件#include<netinet/in.h>或#include<arepa/inet.h>中定义

        • 该结构体解决了sockaddr的缺陷,将port和addr分开存储在两个变量中

          struct sockaddr_in{
              short int sin_family;//地址族
              unsigned short int sin_port;//端口号
              struct in_addr sin_addr;//ip地址
              unsigned char sin_zero[8];//不使用
          };
          

          该结构体中提到的另一个结构体in_addr定义如下,用来存放32位IP地址

          struct in_addr
          {
              unsigned long s_addr;//32位IPv4地址
          };
          
    • sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序(HBO).

      • TCP网络编程各函数的定义

        • socket():creating a socket

          int socket(int domain,int type,int protocol);
          return file(socket)descriptor if OK,-1 on error;
          /*
          功能:创建一个新的套接字,返回套接字描述
          参数说明:
          domain:域类型,指明使用的协议栈,如tcp使用的是PF_INET,还有AF_INET6,AFUNIX等
          tyoe:指明需要服务的类型,如
          SOCK_DGRAM:数据报服务,UDP协议
          SOCK_STREAM:刘服务,TCP协议
          protocol:一般都取0(由系统根据服务类型选择默认的协议)
          */
          
        • bind():binding a socket to an address

          int bind(int sockfd,const struct sockaddr* my_addr,
          socklen_t addrlen);
          return 0 on success,or -1 on error
          /*
          功能:为套接字绑定地址
          TCP/IP协议使用sockaddr_in结构,包含IP地址和端口号,服务器使用它来
          指明熟知的端口号,然后等待连接
          参数说明:
          sockfd:套接字描述符,指明创建连接的套接字
          my_addr:本地地址,IP地址和端口号
          addrlen:地址长度
          */
          
        • listen():listening for incoming connections

          int listen(int sockfd,int backlog);
          return 0 on success,or -1 on error
          /*
          功能:
          将一个套接字设置为监听模式,准备接收传入连接.用于服务器,指明某个套接字
          连接是被动的监听状态
          参数说明:
          Sockfd:套接字描述符,指明创建连接的套接字
          backlog:linux内核2.2之前,backlog参数=半连接队列长度+已连接队列长度;
          linux内核之后,backlog参数=已连接队列(Accept队列)长度
          */
          
        • accept():accepting a connection

          int accept(int sockfd,struct sockaddr *restrict_addr,
          socklen_t *restrict_len);
          return file(socket)descriptor if OK,-1 on error
          /*
          功能:从已完成连接队列中取出成功建立连接的套接字,
          返回成功连接的套接字描述符.
          Sockfd:套接字描述符,指明正在监听的套接字
          addr:提出连接请求的主机地址
          addrlen:地址长度
          */
          
        • send():TCP类型的数据发送

          int send(int sockfd,const void *msg,int len,int flags);
          /*
          功能:在TCP连接上发送数据,返回成功传送数据的长度,出错时返回-1.
          send会将数据移到发送缓冲区中.
          参数说明:
          sockfd:发送端套接字描述符(非监听描述符)
          msg:待发送数据的缓冲区(将其内容的len长度拷贝到socket的发送缓冲区),
          其类型是指针,指向发送数据的缓冲区
          len:待发送数据的字节长度
          flags:一般情况下置为0
          */
          
        • recv():TCP类型的数据接收

          int recv(int sockfd,void *buf,int len,unsigned int flags);
          /*
          功能:接收数据,返回实际接收的数据长度,出错返回-1
          参数说明:
          sockfd:套接字描述符
          buf:指向内存块的指针
          buf_len:内存块大小,以字节为单位
          flags:一般情况下置为0
          */
          
        • close():撤销套接字

          connect():connecting to a peer socket

          close(int sockfd)
          /*
          功能:撤销套接字.如果只有一个进程使用,立即终止连接并撤销该套接字,
          如果多个进程共享该套接字,则将引用数减一,直到引用数降到0,
          则关闭连接撤销套接字.
          参数说明:
          sockfd:套接字描述符
          */
          int connect(int sockfd,structsockaddr *server_addr,
          int sockaddr_len);
          /*
          功能:同远程服务器建立主动连接,成功时返回0,若连接失败返回-1.
          参数说明:
          sockfd:套接字描述符,指明创建连接的套接字
          server_addr:指明远程端点:IP地址和端口号
          sockaddr_len:地址长度
          */
          

  • 实战练习

    • 编写两个程序:一个服务器,一个客户端,让用户能够在客户端不断输入信息并发送到服务器终端上显示.
  • 服务器程序编程

    • 新建serve.cpp文件,在其中写服务器的代码:

      #include<sys/types.h>
      #include<sys/socket.h>
      #include<stdio.h>
      #include<netinet/in.h>
      #include<arpa/inet.h>
      #include<unistd.h>
      #include<string.h>
      #include<stdlib.h>
      #include<fcntl.h>
      #include<sys/shm.h>
      #include<iostream>
      using namespace std;
      
      int main()
      {
          //定义sockfd
          int server_sockfd = socket(AF_INET,SOCK_STREAM,0);
          //定义sockaddr_in
          struct sockaddr_in server_sockaddr;
          server_sockaddr.sin_family = AF_INET;//TCP/IP协议族
          server_sockaddr.sin_port=htons(8023);//端口号
          server_sockaddr.sin_addr.s_addr=inet_addr("127.0.0.1");//ip地址,127.0.0.1
      
          //bind,成功返回0,出错返回-1
          if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr)))
          {
              perror("bind");//输出错误原因
              exit(1);//结束程序
          }
      
          //listen,成功返回0,出差返回-1
          if(listen(server_sockfd,20)==-1)
          {
              perror("listen");//输出错误原因
              exit(1);//结束程序
          }
      
          //客户端套接字
          struct sockaddr_in client_addr;
          socklen_t length=sizeof(client_addr);
      
          //成功返回非负描述字,出错返回-1
          int conn=accept(server_sockfd,(struct sockaddr*)&client_addr,&length);
          if(conn<0)
          {
              perror("connect");//输出错误原因
              exit(1);//结束程序
          }
      
          //接收缓冲区
          char buffer[1000];
      
          //不断接收数据
          while(1)
          {
              memset(buffer,0,sizeof(buffer));
              int len=recv(conn,buffer,sizeof(buffer),0);
              //客户端发送exit或异常结束时,退出
              if(strcmp(buffer,"exit")==0||len<=0)
                  break;
              cout<<"收到客户端信息:"<<buffer<<endl;
          }
          close(conn);
          close(server_sockfd);
          return 0;
      }
      
      
    • client.cpp

      #include<sys/types.h>
      #include<sys/socket.h>
      #include<stdio.h>
      #include<netinet/in.h>
      #include<arpa/inet.h>
      #include<unistd.h>
      #include<string.h>
      #include<stdlib.h>
      #include<fcntl.h>
      #include<sys/shm.h>
      #include<iostream>
      using namespace std;
      
      int main()
      {
          //定义sockfd
          int sock_cli=socket(AF_INET,SOCK_STREAM,0);
      
          //定义sockaddr_in
          struct sockaddr_in servaddr;
          memset(&servaddr,0,sizeof(servaddr));
          servaddr.sin_family=AF_INET;//TCP/IP协议族
          servaddr.sin_port=htons(8023);//服务器端口
          servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");//服务器ip
          //连接服务器,成功返回0,错误返回-1
          if(connect(sock_cli,(struct sockaddr *)&servaddr,sizeof(servaddr)))
          {
              perror("connect");
              exit(1);
          }
          cout<<"连接服务器成功!"<<endl;
      
          char sendbuf[100];
          char recvbuf[100];
          while(1)
          {
              memset(sendbuf,0,sizeof(sendbuf));
              cin>>sendbuf;
              send(sock_cli,sendbuf,strlen(sendbuf),0);//发送
              if(strcmp(sendbuf,"exit")==0)
              {
                  break;
              }
          }
          close(sock_cli);
          return 0;
      }
      
      

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-04-22 19:15:05  更:2022-04-22 19:17:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 3:48:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码