【网络】TCP协议的简单使用

news2024/9/23 15:25:38

目录

echo_service

server

单进程单线程

多进程

多线程

线程池

client

echo_service_code


echo_service

还是跟之前UDP一样,我们先通过实际的代码来实现一些小功能,简单的来使用TCP协议进行简单的通信,话不多说,我们先实现一个echo_serve,就是客户端向服务器发消息,服务器把客户端发的消息返回给客户端

server

首先,我们还是先写服务器,先创建socket套接字,只不过与UDP的选项不同,第二个选项我们选SOCK_STREAM,因为TCP是面向字节流的

其次,还是要bind,bind之前要填充服务器相关信息

由于TCP是面向连接的,所以通信前要先建立连接,因为服务器是被连接的一端,所以要将服务器设置为监听状态

这里第二个参数我们暂且先用上,后面会有讲解

至此,我们初始化的工作就做完了

下面就是要开始我们的服务,我们上面说要先建立连接,所以不能直接接收数据,要先获取连接,我们用下面的接口

可以看到,它的返回值是一个文件描述符,可是socket函数的返回值不也是一个文件描述符吗?它们之间有什么区别呢?我么可以认为socket函数的返回值是一个饭店中站在门口拉客的,它拉到了客人就交给饭店内某个人去一对一服务,这个就是accept的返回值

做完了上面的一系列工作就可以进行通信了,由于TCP是面向字节流的,而文件也是面向字节流的,所以可以用read和write进行通信

网络中read的返回值有不同的含义,大于0就是读到的字节的个数,小于0是读取出错,等于0是客户端退出并且关闭连接,我们就可以用返回值去判断客户端的状态

上面就是大体的过程,下面我们要具体的来考虑一下服务器与客户端的交互过程应该如何写:

我们分为单进程单线程、多进程、多线程、线程池

单进程单线程

首先这个方案是绝对不可以的,这样只能处理一个请求,在和一个客户端交互的时候就不可能去accept新连接

多进程

单进程单线程肯定是不行的,我们必须多创建几个执行流来保证聊天和获取连接都要有执行流去执行

此时我们就可以创建子进程让子进程去和客户端进行交互,父进程重新去获取连接,这样就是可以的,但是父进程是要回收子进程的,父进程也不可能去一直等待,那应该如何做呢?我们之前学过一个信号SIG_CHLD,可以把这个信号的处理动作改为SIG_IGN

这样子进程在退出后会自动清理掉,不会产生僵尸,也不会通知父进程。

还有一种方法就是子进程马上创建孙进程,让孙进程去执行任务,之后子进程立马退出,父进程就可以直接wait到子进程,并且孙进程成为了孤儿进程,会由系统领养,我们就不用关心孙进程的回收问题了

并且要注意,因为与客户端的echo服务已经交给了子进程,那么父进程就没有必要保留这个文件描述符了,直接关掉即可,防止文件描述符泄露(只要是不用,但是占着资源的都叫泄露)

子进程是继承父进程的文件描述符表的,继承后各自独立,互不影响

多线程

我们可以使用原生的线程创建的接口来实现多线程的创建,虽然主线程也是要等待新线程的,但是我们可以将新线程设置为分离状态,这样主线程就不用等待了

线程池

当然除了上面的方式,我们自然可以引入线程池,但是其实线程池适合那些短的任务,就是很快做完又可以领新任务,像这种echo的长服务不适合线程池来做,但是我们还是写一下用一下

thread_t 就是线程池中要放的任务的类型

client

客户端跟UDP一样,先创建socket套接字,然后填充服务器相关信息,之后关键的一步是向服务器发起连接,因为服务器正等着人连接呢

收发消息不止可以用read和write,还可以用recv和send,用法也是很简单的

其实客户端就是如此简单

echo_service_code

//TcpServer.hpp
#pragma once
#include <iostream>
#include <string>
#include<functional>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include<sys/wait.h>
#include <unistd.h>
#include<pthread.h>
#include "Log.hpp"
#include "InetAddr.hpp"
#include"Threadpool.hpp"

enum ERRor
{
    USAGE_ERR = 1,
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR,

};
class TcpServer;//声明一下

struct threaddata
{
    threaddata(int fd,InetAddr&addr,TcpServer*tp):_fd(fd),_addr(addr),_self(tp)
    {}
    int _fd;
    InetAddr _addr;
    TcpServer* _self;
};

class TcpServer
{
public:
    TcpServer(uint16_t port) : _port(port), _listensock(-1)
    {
    }
    void InitServer()
    {
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            LOG(FATAL, "socket err");
            exit(SOCKET_ERR);
        }
        LOG(INFO, "socket success");
        struct sockaddr_in server;
        bzero(&server, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_port);
        server.sin_addr.s_addr = INADDR_ANY;
        int n = bind(_listensock, (struct sockaddr *)&server, sizeof(server));
        if (n < 0)
        {
            LOG(FATAL, "bind err");
            exit(BIND_ERR);
        }
        LOG(INFO, "bind success");
        n = listen(_listensock, 16);
        if (n < 0)
        {
            LOG(FATAL, "listen err");
            exit(LISTEN_ERR);
        }
        LOG(INFO, "listen success");
    }
    void service(int fd, InetAddr addr)
    {
        LOG(INFO, "server get a new link,ip is %s,port is %d", addr.Ip().c_str(), addr.Port());
        while (true)
        {
            char buffer[1024];
            int n = read(fd, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = 0;
                LOG(INFO, "[%s:%d]#%s", addr.Ip().c_str(), addr.Port(), buffer);
                std::string message = "server echo#";
                message += buffer;
                write(fd, message.c_str(), message.size());
            }
            else if (n < 0) // 读取出错
            {
                LOG(ERROR, "read error");
                break;
            }
            else // n==0 client退出&&关闭连接
            {
                LOG(INFO, "[%s,%d] quit", addr.Ip().c_str(), addr.Port());
                break;
            }
        }
    }
    static void*newthreaddo(void*arg)
    {
        pthread_detach(pthread_self());
        threaddata*ptd=static_cast<threaddata*>(arg);
        ptd->_self->service(ptd->_fd,ptd->_addr);
        delete ptd;
        return nullptr;
    }
    void StartServer()
    {
        while (true)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int fd = accept(_listensock, (struct sockaddr *)&client, &len);
            if (fd < 0)
            {
                LOG(WARNING, "accept err");
                continue;
            }
            InetAddr addr(client);
            // service(fd, addr);单进程单线程
            // close(fd);

            // int f = fork();//多进程
            // if (f == 0)
            // {
            //     // son
            //     if (fork() == 0)
            //     {// grandson
            //         close(_listensock);
            //         service(fd, addr);
            //         close(fd);
            //     }
            //     exit(0);//son创建完孙子后立马退出,孙子有由系统领养
            // }
            // // father
            // close(fd);
            // waitpid(f,nullptr,0);

            // pthread_t t;//多线程
            // threaddata* ptd=new threaddata(fd,addr,this);
            // pthread_create(&t,nullptr,newthreaddo,ptd);

            thread_t t=std::bind(&TcpServer::service,this,fd,addr);//线程池
            ThreadPool<thread_t>::Getinstance()->Enqueue(t);
        }
    }

    ~TcpServer()
    {
    }

private:
    uint16_t _port;
    int _listensock;
};





//TcpClient.cc
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
using std::cout;
using std::endl;

void Usage(const char *arg)
{
    cout << "USAGE:" << endl;
    cout << "#         please enter:  " << arg << "serverip serverport" << endl;
}
// ./client 127.0.0.1 8888
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    std::string ip(argv[1]);
    uint16_t port = std::stoi(argv[2]);

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        cout << "socket err" << endl;
        exit(1);
    }
    cout << "socket success" << endl;
    struct sockaddr_in server;
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = INADDR_ANY;

    int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
    if (n < 0)
    {
        cout << "connect err" << endl;
        exit(1);
    }
    cout << "connect success" << endl;
    while (true)
    {
        cout << "please enter#";
        std::string what;
        std::getline(std::cin, what);
        send(sockfd, what.c_str(), what.size(), 0);
        char buffer[1024];
        int num = recv(sockfd, buffer, sizeof(buffer), 0);
        if (num > 0)
        {
            buffer[num] = 0;
            cout << buffer << endl;
        }
        else if (num < 0)
        {
            cout << "recv error" << endl;
            continue;
        }
    }
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LabVIEW提高开发效率技巧----合理使用数据流与内存管理

理使用数据流和内存管理是LabVIEW开发中提高性能和稳定性的关键&#xff0c;特别是在处理大数据或高频率信号时&#xff0c;优化可以避免内存消耗过大、程序卡顿甚至崩溃。 1. 使用 Shift Register 进行内存管理 Shift Register&#xff08;移位寄存器&#xff09; 是 LabVIE…

五分钟上手Spring AI Alibaba,轻松打造智能聊天应用

文章目录 快速上手快速体验示例示例开发指南总结 快速上手 Spring AI Alibaba 已经完全适配了阿里云通用模型&#xff0c;接下来&#xff0c;我们将学习如何使用 spring ai alibaba 开发一个基于通用模型服务的智能聊天应用。 快速体验示例 下载项目 运行以下命令下载源码&…

【产品更新】中汇保函-电子保函管理平台

中汇保函 新增 1.手机扫描保函验真二维码直接跳转小程序模块&#xff0c;验真快人一步。 2.新增客户服务服务&#xff0c;可直接联系微信客服。 优化 1.提交申请、登录程序响应速度。 更新内容说明 1.手机扫描保函验真二维码直接跳转小程序模块&#xff0c;验真快人一步。 2.…

从零开始之AI面试小程序

从零开始之AI面试小程序 文章目录 从零开始之AI面试小程序前言一、工具列表二、部署流程1. VMWare安装2. Centos安装3. Centos环境配置3.1. 更改子网IP3.2. 配置静态IP地址 4. Docker和Docker Compose安装5. Docker镜像加速源配置6. 部署中间件6.1. MySQL部署6.2. Redis部署 7.…

华为OD机试 - 出租车计费(Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

飞睿智能实时雷达活体探测传感器模块,智能家居静止检测实时感知人员有无

随着科技的飞速发展&#xff0c;我们的生活正在经历着未有的创新。在这个创新的浪潮中&#xff0c;实时雷达活体探测传感器模块的技术正逐渐崭露头角&#xff0c;以其独特的优势为我们的生活带来安全与便捷。今天&#xff0c;我们就来详细探讨一下这项技术&#xff0c;看看它是…

帮13岁小孩哥2分钟完成开发,这位AI程序员究竟是何方神圣?

通义灵码再升级&#xff0c;真AI程序员来了 随着通义系列基础模型能力的全面提升&#xff0c;各个具体领域的应用模型也随之飞升。这次在云栖大会上迎来重磅升级的&#xff0c;就包括用于代码生成的通义灵码。 一年前的通义灵码还只能完成基础的辅助编程任务&#xff0c;很难…

基于SpringBoot和Vue框架的医保管理系统的设计与实现

文未可获取一份本项目的java源码和数据库参考。 1.研究的主要内容与方法 &#xff08;1&#xff09;主要内容 医保管理系统采用B/S模式进行开发&#xff0c;采用Springboot框架、VUE技术、Idea为环境、MySQL为数据库开发。主要功能有&#xff1a;个人资料管理、投保用户管理、…

上海我店平台 8月新增注册用户89w 两年破百亿销售额!

近年来&#xff0c;网络空间内涌现了一个备受瞩目的新平台——“上海我店”&#xff0c;其公布的业绩数据显示&#xff0c;短短三年内&#xff0c;该平台交易流水已突破百亿大关&#xff0c;上月更是迎来了近百万的新增注册用户&#xff0c;这一消息迅速吸引了众多商家的目光。…

【深度好文】你必须要知道-大模型的上下文窗口(Context Window )

Context Window 上下文窗口&#xff1a;捕捉信息的范围 上下文窗口指的是 AI 模型在生成回答时考虑的 Token 数量。它决定了模型能够捕捉信息的范围。上下文窗口越大&#xff0c;模型能够考虑的信息就越多&#xff0c;生成的回答也就越相关和连贯。 在语言模型中&#xff0c;上…

SysML图例-手电筒

DDD领域驱动设计批评文集>> 《软件方法》强化自测题集>> 《软件方法》各章合集>>

第二证券:股价为什么出现大跌?股价大跌时怎么办?

股票是预期收益率较大的出资之一&#xff0c;但同时股票商场的动摇也会是比较大的&#xff0c;股价大涨大跌都是有或许出现的。股价大涨会让出资者获利更多&#xff0c;而大跌也会加大出资者的损失。下面为我们分析股价为什么会大跌&#xff0c;并介绍股价大跌出资者应该如何应…

【测试】——Selenium API (万字详解)

&#x1f4d6; 前言&#xff1a;本文详细介绍了如何利用Selenium进行Web自动化测试&#xff0c;包括定位元素&#xff08;如cssSelector和xpath&#xff09;、常用操作函数&#xff08;如点击、输入等&#xff09;、窗口管理、键盘鼠标事件和浏览器导航&#xff0c;以及处理弹窗…

linux 的 sed 命令的 使用学习

&#xff08;1&#xff09; sed 概述&#xff1a; &#xff08;2&#xff09; 首先谢谢 b 站这位老师&#xff0c;这位专家的完美讲解 讲解继续&#xff1a; &#xff08;3&#xff09; 关于 sed 里的模式&#xff1a; &#xff08;4&#xff09; sed 支持的常用的对文本编辑的…

LIN总线CAPL函数—— 设置报头同步间隔场长度(linSetBreakLength)

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

爬虫 ----hook

目录 定义&#xff1a; 了解什么是hook? 举例 hook XHR请求 XMLHttpRequest 案例地址&#xff1a; Interceptors-拦截器 HOOK cookie操作 cookie 示范 常见的hook代码总结 1.Hook Cookie 2.Hook Header 3.Hook URL 4.Hook JSON.stringify 5.Hook JSON.parse 6.Ho…

5G Multicast/Broadcast Services(MBS) (四)

这篇是有关MBS RRC相关的一些基本内容,内容不多,但是感觉很关键,主要包括SI,MBS网络侧相关的内容,L2 协议架构,cell reselection prioritity以及MBS接收的一些内容,希望有帮助。 SI 在MBS场景中,SI和常规5G一样分为Minimum SI和Other SI。Minimum SI是MIB和SIB1,Min…

智能创造的幕后推手:AIGC浪潮下看AI训练师如何塑造智能未来

文章目录 一、AIGC时代的算法与模型训练概览二、算法与模型训练的关键环节三、AI训练师的角色与职责四、AI训练师的专业技能与素养五、AIGC算法与模型训练的未来展望《AI训练师手册&#xff1a;算法与模型训练从入门到精通》亮点内容简介作者简介谷建阳 目录 《医学统计学从入门…

Cisco Packet Tracer超详细下载安装教程(附中文版插件)

一、安装包下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1RK8iQ9lJG__vBEGCYVYNSA 提取码&#xff1a;1lvb 压缩包解压密码&#xff1a;66668888&#xff0c;不能正常解压的&#xff0c;推荐使用360压缩解压 二、安装教程&#xff1a; 1.双击启动安装包 2.点击N…

springboot+vue3基于Java的校园二手商品交易系统设计与实现(编号:4259233)

目录 功能和开发技术介绍具体实现截图开发核心技术介绍&#xff1a;技术创新点vue3和vue2的区别&#xff1a;核心代码部分展示非功能需求分析系统开发流程系统运行步骤软件测试源码获取 功能和开发技术介绍 本课题拟采用主流的MVC架构、开发工具idea、java语言编程、MySQL数据…