【网络】2.TCP通信

TCP通信

  • server
    • 1. 创建套接字
    • 2. 填充套接字
    • 3. 将套接字和监听文件描述符绑定
    • 4. 将_listensock设置为监听状态
    • 5. 启动服务器
      • accept()函数
      • read()函数
  • Server启动
  • client
    • 1. 创建套接字
    • 2. 填充套接字
      • connect()函数
    • 3. 通过文件描述符向服务端发送信息
  • client启动

server

server的启动方式是:./udpserver 8080 --> 格式就是./proc port端口

port端口自己指定

1. 创建套接字

_listensock = socket(AF_INET, SOCK_STREAM, 0);

AF_INET:表明域是ipv4
SOCK_STREAM:表示使用TCP连接,面向字节流的
__listensock:socket()函数返回的文件描述符是监听套接字,用于监听客户端的连接请求,并不是用来接收信息的

2. 填充套接字

//2.填充套接字
struct sockaddr_in local;
bzero(&local, sizeof(local));

local.sin_family = AF_INET;
local.sin_port = _port;
local.sin_addr.s_addr = INADDR_ANY;   //INDAAR_ANY:为0.0.0.0,表示任何ip地址

这里的套接字存储的是服务端的地址信息,包含了服务器监听的ip地址信息和端口信息。

3. 将套接字和监听文件描述符绑定

bind(_listensock, (const struct sockaddr*)&local, sizeof(local)

bind 函数将服务器的监听套接字 _listensocklocal 地址绑定,以便在该地址和端口上监听连接。

4. 将_listensock设置为监听状态

listen(_listensock, backlog)

listen() 函数的作用是将一个套接字设置为监听状态,使服务器能够接收客户端的连接请求。它主要用于TCP服务器的套接字初始化过程中。

在这里,就是将_listensock设置为监听状态。
backlog:连接队列的最大长度,也就是未处理的客户端连接请求数的上限。当有多个客户端同时请求连接时,系统会将这些连接请求放入一个队列中,backlog 就是该队列的大小。

backlog不宜设置过大,在本文中的设置如下:

const int backlog = 10;

5. 启动服务器

    void Start()
    {
        while(true)
        {  
            //1.获得client的套接字
            struct sockaddr_in client;
            
            //2.通过accept获取client的值
            //accept用来接收客户端的连接请求,recvform用来接收客户端的数据
            //listensock是用来接收客户端连接请求的,accept的返回值sock是操作系统用来和客户端一对一通信的
            socklen_t len = sizeof(client);
            int sock = accept(_listensock, (struct sockaddr*)&client, &len);

            if (sock < 0)
            {
                cout << "accept 失败" << endl;
                exit(ACCEOPT_ERRPR);
            }

            //启动服务器
            ServerIO(sock);
        }
    }

accept()函数

int sock = accept(_listensock, (struct sockaddr*)&client, &len)

accept() 函数在服务器端的作用是接收客户端的连接请求,并为与该客户端的通信创建一个新的套接字sock。 这个新的套接字sock用于服务器和客户端之间的一对一数据传输。当有客户端发起连接请求时,服务器监听套接字_listensock会检测到,并通过 accept() 函数获取该请求并创建一个用于通信的套接字sock。

1. _listensock和sock的区别是什么?

_listensock:监听套接字

  • 用途:用于监听客户端的连接请求
  • 创建方式:通过 socket() 函数创建,之后使用 bind() 将其绑定到特定的 IP 地址和端口,再通过 listen() 函数使其进入监听状态。
  • 特点_listensock 只负责监听,它不会直接用于数据传输。每当有客户端请求连接时,服务器会通过 accept() 函数从连接队列中取出请求并创建新的通信套接字。
  • 生命周期:通常服务器运行期间一直存在,直到服务器关闭才释放。

sock:通信套接字

  • 用途:用于与特定客户端之间进行数据传输
  • 创建方式:当客户端发起连接请求并被 accept() 函数接受时,系统会创建一个新的套接字(即 sock)来完成该客户端与服务器之间的通信。
  • 特点:每个 sock 都是与一个客户端的唯一连接,负责传输和该客户端的所有数据。服务器可以有多个 sock,分别与不同客户端通信。
  • 生命周期:当客户端断开连接或服务器结束与该客户端的会话时,sock 会被关闭和释放。

2. accept()函数的详细解析?

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • sockfd

    • 这是服务器的监听套接字文件描述符,通常由 socket() 函数和 bind()listen() 函数配合创建并设置好。
    • 它指定了服务器在该套接字上监听客户端连接请求。
  • addr

    • 这是一个指向 sockaddr 结构体的指针,用于存储客户端的地址信息(如IP地址和端口号)。
    • 该地址结构体在调用 accept() 后会被填充,包含客户端的 IP 地址和端口号,用于识别该连接的客户端。
    • 在代码中,(struct sockaddr*)&client 将客户端地址信息存储在 client 结构体中。
  • addrlen

    • 这是一个指向 socklen_t 类型的指针,表示addr结构体的大小
    • 在调用 accept() 时,addrlen 应包含 addr 的大小;accept() 返回时,它会被更新为客户端地址的实际长度。
  • accept() 函数的返回值

    • 成功:返回一个新的套接字文件描述符(sock),用于服务器和客户端之间的通信。
    • 失败:返回 -1,并设置 errno 指定错误码。

6.运行服务器

    void ServerIO(int sock)
    {
        char inbuffer[size];

        while(true)
        {
            //使用read函数,将网络中传输的数据用sock文件描述符读取到inbuffer中,读取的大小是sizeof(inbuffer) - 1
            ssize_t n = read(sock, inbuffer, sizeof(inbuffer) - 1);
            if (n > 0)
            {  
                //在最后一个位置添加0,用于停止
                inbuffer[n] = 0;
                cout << "服务端接收到数据 : " << inbuffer << endl;
                string outbuffer = inbuffer;

                cout << outbuffer << " server[echo]" << endl;
                write(sock, outbuffer.c_str(), sizeof(outbuffer) - 1);
            }
            else if (n == 0)  //这种情况是客户端关闭连接了
            {
                cout << "客户端关闭退出,服务端也退出..." << endl;
                break;
            }
        }
        close(sock);
    }

read()函数

ssize_t read(int fd, void *buf, size_t count);
  • 参数解释

    • fd:文件描述符,这里是通信套接字 socksock 指向服务器与某个客户端的连接。
    • buf:指向缓冲区的指针,用于存储读取到的数据。这里是 inbuffer
    • count:要读取的最大字节数。代码中使用的是 sizeof(inbuffer) - 1
  • 返回值

    • read() 函数返回读取的字节数。
    • 若返回 0,表示对端关闭了连接(即客户端断开连接)。
    • 若返回 -1,表示发生了错误。
      3.为什么 count 使用 sizeof(inbuffer) - 1
  • 保留空间放置字符串终止符

    • inbuffer 是一个 char 数组,实际接收的内容是字符串(也即以 \0 结尾的字符序列)。
    • read() 函数不会自动添加字符串终止符 \0,因此我们需要在数据的末尾手动加上一个 \0
    • count 设置为 sizeof(inbuffer) - 1,让 inbuffer 留出一个位置,以便在读取完数据后,在末尾加上 \0,构成有效的 C 字符串。
  • 避免缓冲区溢出

    • 如果使用 sizeof(inbuffer) 作为 count 的值,读取的数据可能会填满整个缓冲区,而没有为 \0 留出空间,容易导致缓冲区溢出或后续操作产生不确定的行为。
    • sizeof(inbuffer) - 1 确保不会超过缓冲区的大小,从而避免溢出。

Server启动

#include "TcpServer.hpp"

using namespace std;

  
void Usage(string proc)
{
    cout << "服务端使用说明 : " << proc << " port" << endl;
}

//使用方式 --> ./tcpserver 8080
int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 0;
    }
    
    //argc[1]是string类型,serverIp是uint16_t类型,需要用atoi()转换
    uint16_t sercerIp = atoi(argv[1]);
    unique_ptr<TcpServer> tser(new TcpServer(sercerIp));  //使用智能指针
    
    tser->InitTcpServer();
    tser->Start();

    return 0;
}

client

client的启动方式是:./udpclient 127.0.0.0.1 8080 --> 格式就是./proc ip地址 port端口

1. 创建套接字

_sockfd = socket(AF_INET, SOCK_STREAM, 0);

2. 填充套接字

struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = _serverport;
local.sin_addr.s_addr = inet_addr(_serverip.c_str());

这里的local是套接字,里面填充的是服务端的ip地址和端口信息。

connect()函数

connect(_sockfd, (const struct sockaddr*)&local, len)
//模型
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

connect() 函数的作用是让客户端发起一个连接请求,与服务器建立连接。此操作适用于TCP协议,在连接建立成功后,客户端可以通过 _sockfd 向服务器发送和接收数据。

  • sockfd
    • 这是客户端的套接字文件描述符,通常是由 socket() 函数创建的。
    • 在调用 connect() 前,sockfd 并没有与具体的服务器地址关联,而 connect()sockfd 连接到服务器的 addr 地址。
  • addr
    • 这是一个指向 sockaddr 结构体的指针,用于指定服务器的 IP 地址和端口,即客户端希望连接的目标地址。
    • 在代码中,(const struct sockaddr*)&local 表示将 local(服务器的套接字地址信息)转换为 sockaddr 类型。
  • addrlen
    • 这是 addr 结构体的大小,以 socklen_t 类型表示。在这里,sizeof(local) 被传入,以指定服务器地址结构体的长度。
  • connect() 函数的返回值
    • 成功:返回 0,表示连接成功,客户端与服务器之间的通信通道已建立。
    • 失败:返回 -1,表示连接失败,errno 被设置为相应的错误码。代码中如果连接失败会打印 “客户端连接失败…” 并退出。

3. 通过文件描述符向服务端发送信息

string message;

while(true)
{
  cout << "Enter#";
  getline(cin, message);

  //通过sockfd,向服务端写数据,内容是message
  write(_sockfd, message.c_str(), sizeof(message) - 1);
  //可以通过read函数将服务端接收到的数据回显到服务端,这里就不作处理了
 }

client启动

#include "TcpClient.hpp"

using namespace std;

void Usage(string proc)
{
    cout << "客户端使用说明 : " << proc << " ip " << "port" << endl;
}
  
//输入是 : ./proc ip port
int main(int argc, char* argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return 0;
    }
    string ip = argv[1];
    uint16_t port = atoi(argv[2]);
  
    unique_ptr<TcpClient> tclient(new TcpClient(ip, port));

    tclient->InitTcpClient();
    tclient->Start();

    return 0;
}

github地址:
https://github.com/gaogo21/Linux-Learning/tree/master/NetWork/2024-10-29-tcp
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/904552.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ArcGISPro】宣布推出适用于 ArcGIS 的 AI 助手

此次分享了ESRI正在开发新的“AI 助手”来扩展ArcGIS应用程序&#xff0c;并且使用经过专门培训、提示工程和 LLM的“AI 助手”为这些应用程序提供特定技能。并且以视频的方式展示了如何使用生成式 AI 在ArcGIS应用程序中自动化和加速工作流。最后表示这些 “AI 助手”将在 202…

Ansible基本使用

目录 介绍 安装 inventory-主机清单 分组 子组 modules-模块 command shell script file copy systemd yum get_url yum_repository user mount cron 介绍 ansible是基于python开发的自动化运维工具。架构相对比较简单&#xff0c;仅需通过ssh连接客户机执行…

volatile如何保证可见性和禁止指令重排序?

当线程对volatile修饰的变量进行写操作时&#xff0c;JMM会插入一个写屏障&#xff0c;会强制的将本地内存中的数据写到主内存中 当线程对volatile修饰的变量进行读操作时&#xff0c;JMM会插入一个读屏障&#xff0c;会强制的让本地内存中数据失效&#xff0c;重新到主内存中读…

AUTOSAR_EXP_ARAComAPI的6章笔记(4)

☞返回总目录 相关总结&#xff1a;《AUTOSAR 自适应应用中原始数据流传输的使用方法》总结 6.4 原始数据流传输的使用方法 本章描述了原始数据流&#xff08;RawDataStreams&#xff09;在 AUTOSAR 自适应应用程序中的使用方法。 目前&#xff0c;原始数据流传输在单播 / …

input子系统的框架和重要数据结构详解

#1024程序员节 | 征文# 往期内容 I2C子系统专栏&#xff1a; 专栏地址&#xff1a;IIC子系统_憧憬一下的博客-CSDN博客具体芯片的IIC控制器驱动程序分析&#xff1a;i2c-imx.c-CSDN博客 – 末篇&#xff0c;有往期内容观看顺序 总线和设备树专栏&#xff1a; 专栏地址&#…

【Linux:网络基础】

网络协议&#xff1a; 协议实际上可以称为一种“约定”&#xff0c;通过网络通信中的数据约定&#xff0c;不同主机必须遵循相同的网络协议才可以实现通信。 协议即为通信双方都认识的结构化的数据类型 协议分层 协议的本质也是软件&#xff0c;在设计上为了更好的进行模块…

C++刷怪笼(9)继承

目录 1.前言 2.正文 2.1继承的概念和定义 2.1.1继承的概念 2.1.2继承的定义 ​编辑 2.1.3继承类模板 2.2基类和派生类间的转换 2.3继承中的作用域 2.3.1隐藏规则 2.4派⽣类的默认成员函数 2.4.1个常⻅默认成员函数 2.4.2实现⼀个不能被继承的类 2.5继承与友元 2.…

QT中使用图表之QChart概述

在Qt中使用QChart类可以快速绘制一个图表出来&#xff0c;比如折线图、饼图、柱状图等 QChart类用来管理图表中的图形、图例、轴等 QChartView是专门用来显示图表的类&#xff0c;相当于一个QWidget或者窗口&#xff0c;用来显示QChart 即总的步骤就是 1、创建QChartView的…

codeforces _ 补题

C. Ball in Berland 传送门&#xff1a;Problem - C - Codeforces 题意&#xff1a; 思路&#xff1a;容斥原理 考虑 第 i 对情侣组合 &#xff0c;男生为 a &#xff0c;女生为 b &#xff0c;那么考虑与之匹配的情侣 必须没有 a | b &#xff0c;一共有 k 对情侣&#x…

零基础学西班牙语,柯桥专业小语种培训泓畅学校

No te comas el coco, seguro que te ha salido bien la entrevista. Ya te llamarn. 别瞎想了&#xff01;我保证你的面试很顺利。他们会给你打电话的。 这里的椰子是"头"的比喻。在西班牙的口语中&#xff0c;我们也可以听到其他同义表达&#xff0c;比如&#x…

一、开发环境的搭建

环境搭建步骤&#xff1a; 下载软件安装软件运行软件 其他&#xff1a; Visual studio 安装包文件&#xff1a;https://www.alipan.com/s/nd5RgzD4e3b 下载软件 在浏览器中搜索Visual studio&#xff0c;选择如图的选项 点击该区域&#xff0c;进入该页面&#xff0c;【或…

SSH免密钥登录

1: 用 ssh-key-gen 在本地主机上创建公钥和密钥 winr cmd 打开控制台 ssh-keygen -t rsa 一直按enter 2: 用 ssh-copy-id 把公钥复制到远程主机上 user 是用户名 remote_host是远程主机 ssh-copy-id -i ~/.ssh/id_rsa.pub userremote_host 3: 直接登录远程主机&#xf…

对外部供应商依赖带来的战略限制分析

当企业过分依赖单一或少数供应商时&#xff0c;一旦这些供应商出现问题&#xff0c;如生产延误、质量问题或财务困难&#xff0c;企业的运营将受到严重影响。例如&#xff0c;2020年的新冠疫情期间&#xff0c;许多企业因为供应商无法按时交货而面临生产停滞&#xff0c;导致巨…

Windows11家庭版安装Docker Desktop软件教程

下载Windows安装包 我们建议将源代码和其他数据绑定到 Linux 容器中时,将其存储在 Linux 文件系统中,而不是 Windows 文件系统中。 docker官网首页https://www.docker.com/ (需要科学上网)下载Windows版本的Docker Desktop。 或者使用已经下载好的Docker Desktop 安装包…

理解ADC:为什么量化噪声也会产生谐波?附带介绍 Dither(抖动)

前言 今天继续从经典的 ADI 《MT-001》说起&#xff0c;通常情况下量化噪声是白噪声&#xff0c;但如果量化噪声与输入信号之间存在相关性&#xff0c;就不能被当做白噪声对待。 文中举了一个有意思的例子&#xff1a;理想 ADC 的采样频率为 80 MSPS &#xff0c;一种情况输入…

外包干了7天,技术明显退步。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;22年通过校招进入南京某软件公司&#xff0c;干了接近2年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了2年的功能测试&…

vue组件在项目中的常用业务逻辑(1)

若要使用一个组件&#xff08;已有基本结构&#xff09; 1.在api>index.js获取接口数据&#xff08;这里是模拟数据&#xff09; 2.去vuex仓库&#xff08;store>home.js&#xff09;存储数据&#xff08;state mutations actions三连环&#xff09; &#xff08;准备…

Linux网络命令:系统中用于显示和操作 ARP缓存表的命令arp详解

目录 一、概述 二、用法 1、基本语法 2、常用选项 3、获取帮助 三、示例 1. 显示所有 ARP 表项 2. 以数字形式显示 IP 地址 3. 删除指定的 ARP 表项 4. 添加一个静态 ARP 表项 5. 显示详细信息 四、详细说明 1、ARP 缓存表 2、静态 ARP 表项 3、动态 ARP 表项 五、常…

如何在短时间内入门并掌握深度学习?

如何在短时间内快速入门并掌握深度学习&#xff0c;是很多读者的困惑——晦涩难懂的数学 知识、复杂的算法、烦琐的编程……深度学习虽然让无数读者心怀向往&#xff0c;却也让不少人望而生畏&#xff0c;深感沮丧&#xff1a;时间没少花&#xff0c;却收效甚微。 如何才能更好…

图像分割从基础到进阶:阈值化、K-means和Mean-Shift算法的应用

图像分割是计算机视觉中的一项关键技术&#xff0c;用来将图像划分为若干个 有意义 的区域&#xff0c;以便后续的图像处理和分析工作。根据任务的不同&#xff0c;图像分割可以进一步细分为语义分割、实例分割和全景分割&#xff1a; 语义分割 (Semantic Segmentation) 对图像…