当前位置: 首页 > article >正文

TCP socket

TCP的socket和UDP大同小异,基本的代码结构都是相同的。一些相同的接口本文就不赘述了,例如,socket,bind,有需要看这篇文章UDP socket

服务端server

两步:初始化服务端,运行服务端

初始化服务端

创建socket,bind IP和端口号,listen监听

socket的第二个参数使用SOCK_STREAM,因为与UDP不同的是TCP面向字节流。其余的与UDP无异。

listen接口

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 

    void init()
    {
        // socket
        sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd_ < 0)
        {
            exit(1);
        }
        // bind
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        inet_pton(AF_INET, ip_.c_str(), &local.sin_addr);
        local.sin_port = htons(port_);
        int n = bind(sockfd_, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            exit(2);
        }
        // listen
        if (listen(sockfd_, 10) < 0)
        {
            exit(3);
        }
    }

运行服务端

四步:accept接收请求,接收数据,处理数据,最后发送数据

accept接口

 初始化socket得到的文件描述符是用来listen的,而accept获得的文件描述符才是用来传输数据的,read和write接口就是用这个描述符的。

read和write很简单,第一个参数是文件描述符,第二个参数是读取/发送的数据地址,第三个参数是数据的大小

void run()
    {
        while (1)
        {
            // accept
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int lis_fd = accept(sockfd_, (struct sockaddr *)&client, &len);
            // 接收数据
            char buf[1024] = {0};
            int n = read(lis_fd, buf, sizeof(buf));
            if (n < 0)
            {
                break;
            }
            else if (n == 0)
            {
                cout << "quit" << endl;
                break;
            }
            else
            {
                buf[n] = 0;
                std::cout << "server get msg:" << buf << std::endl;
                // 处理数据
                std::string msg = "server say: I have get msg >_";
                msg += buf;
                write(lis_fd, msg.c_str(), msg.size());
            }
        }
    }

客户端client

五步:创建socket(同服务端)->bind(由OS完成)->connect请求连接->发送数据write->接收数据read

connect接口

第一个参数是socket获得的文件描述符,第二个是要连接的服务端的属性结构体(记得类型转换),TCP的结构体数据类型是struct sockaddr_in。第三个是结构体大小

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cout << argv[0] << "[ip]" << "[port]" << std::endl;
        return -1;
    }
    // 获取命令行参数
    uint16_t port = stoi(argv[2]);
    string ip = argv[1];
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ip.c_str());

    

        
    while (1)
    {
        // socket
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
        {
            std::cout << "client create fail" << endl;
            exit(1);
        }
        // bind由os随机分配端口完成
        
        // connect
        int n = connect(sockfd, (struct sockaddr *)&server, (socklen_t)sizeof(server));
        if (n < 0)
        {
            std::cout << "client connect fail" << std::endl;
            exit(2);
        }

        std::string msg;
        std::cout << "Please Enter>_";
        getline(std::cin, msg);
        // 发送数据
        n = write(sockfd, msg.c_str(), msg.size());
        if (n < 0)
        {
            std::cout << "write fail" << std::endl;
            continue;
        }

        char buf[1024] = {0};
        // 接收数据
        n = read(sockfd, buf, sizeof(buf));
        if (n < 0)
        {
            std::cout << "read fail" << std::endl;
            continue;
        }
        buf[n] = 0;
        cout << buf << endl;
    }
}

序列化和反序列化

序列化是将数据结构或对象转换为一种可以存储或传输的格式,反序列化是序列化的逆过程,它将序列化后的数据重新还原为原始的对象或数据结构。

其实这本质和协议是一样的,都是一种约定。服务端和客户端都按照一个相同的规则将收发的数据做处理,以便于数据的传输。

序列化和反序列化的应用场景

  1. 网络编程

    客户端与服务器之间传输数据时,往往通过序列化和反序列化的方式来进行。例如,在你写的 TCP 客户端/服务器代码中,客户端需要将数据(如数学表达式)序列化后发送给服务器,服务器再反序列化处理并返回结果。
  2. 数据持久化

    将内存中的数据或对象序列化后保存到数据库或文件中,以便稍后可以重新加载数据并继续使用。
  3. 跨进程通信

    当两个不同的进程需要交换数据时,序列化可以将复杂数据结构转换为字节流,方便在进程之间传递。

源码

//tcpserver.hpp
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

enum
{
    SocketFail = 1,
    BindFail,
    ListenError
};
class tcpServer
{
public:
    tcpServer(std::string ip = "0.0.0.0", uint16_t port = 8888)
        : ip_(ip), port_(port)
    {
    }
    ~tcpServer()
    {
    }

    void init()
    {
        // socket
        sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd_ < 0)
        {
            exit(SocketFail);
        }
        // bind
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        inet_pton(AF_INET, ip_.c_str(), &local.sin_addr);
        local.sin_port = htons(port_);
        int n = bind(sockfd_, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            exit(BindFail);
        }
        // listen
        if (listen(sockfd_, 10) < 0)
        {
            exit(ListenError);
        }
    }

    void run()
    {
        while (1)
        {
            // accept
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int lis_fd = accept(sockfd_, (struct sockaddr *)&client, &len);
            // 接收数据
            char buf[1024] = {0};
            int n = read(lis_fd, buf, sizeof(buf));
            if (n < 0)
            {
                break;
            }
            else if (n == 0)
            {
                cout << "quit" << endl;
                break;
            }
            else
            {
                buf[n] = 0;
                std::cout << "server get msg:" << buf << std::endl;
                // 处理数据
                std::string msg = "server say: I have get msg >_";
                msg += buf;
                write(lis_fd, msg.c_str(), msg.size());
            }
        }
    }

private:
    int sockfd_;
    std::string ip_;
    uint16_t port_;
};
//tcpclient.cpp
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cout << argv[0] << "[ip]" << "[port]" << std::endl;
        return -1;
    }
    // 获取命令行参数
    uint16_t port = stoi(argv[2]);
    string ip = argv[1];
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ip.c_str());

    

        
    while (1)
    {
        // socket
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
        {
            std::cout << "client create fail" << endl;
            exit(1);
        }
        // bind由os随机分配端口完成
        
        // connect
        int n = connect(sockfd, (struct sockaddr *)&server, (socklen_t)sizeof(server));
        if (n < 0)
        {
            std::cout << "client connect fail" << std::endl;
            exit(2);
        }

        std::string msg;
        std::cout << "Please Enter>_" << std::endl;
        getline(std::cin, msg);
        // 发送数据
        n = write(sockfd, msg.c_str(), msg.size());
        if (n < 0)
        {
            std::cout << "write fail" << std::endl;
            continue;
        }

        char buf[1024] = {0};
        // 接收数据
        n = read(sockfd, buf, sizeof(buf));
        if (n < 0)
        {
            std::cout << "read fail" << std::endl;
            continue;
        }
        buf[n] = 0;
        cout << buf << endl;
    }
}
//main.cpp
#include "tcpServer.hpp"
#include <memory>

int main()
{
    std::unique_ptr<tcpServer> server(new tcpServer);
    server->init();
    server->run();
    return 0;
}


http://www.kler.cn/a/310370.html

相关文章:

  • 设计模式之工厂模式,但是宝可梦
  • OCR识别铁路电子客票
  • Autosar CP 基于CAN的时间同步规范导读
  • C++,STL 054(24.11.13)
  • Java设计模式面试题及参考答案
  • 图论基本术语
  • Android 进程间通信
  • 使用llama.cpp 在推理MiniCPM-1.2B模型
  • 24年蓝桥杯及攻防世界赛题-MISC-3
  • 【Redis】Redis 典型应用 - 分布式锁原理与实现
  • 计算机毕业设计 基于SpringBoot框架的网上蛋糕销售系统的设计与实现 Java实战项目 附源码+文档+视频讲解
  • Python编程 - 协程
  • [PICO VR眼镜]眼动追踪串流Unity开发与使用方法,眼动追踪打包报错问题解决(Eye Tracking/手势跟踪)
  • FFmpeg源码:skip_bits、skip_bits1、show_bits函数分析
  • centos远程桌面连接windows
  • iPhone 16系列:熟悉的味道,全新的体验
  • 浅谈Tair缓存的三种存储引擎MDB、LDB、RDB
  • 使用Addressables+SpriteAtlas打包产生冗余
  • Python知识点:详细讲解Python字节码与反编译
  • Elasticsearch 开放推理 API 新增阿里云 AI 搜索支持
  • react 高阶组件
  • 优化数据的抓取规则:减少无效请求
  • 【数学建模】典型相关分析
  • 【RabbitMQ 项目】服务端:数据管理模块之消息管理
  • 大语言模型超参数调优:开启 AI 潜能的钥匙
  • Linux下rpm方式部署mysql(国产化生产环境无联网服务器部署实操)