【Socket编程】基于TCP协议实现客户端与服务端的通信

前言

由于TCP是面向连接的,所以在创建套接字之后还需要进入监听状态,监听状态下可以获取客户端的请求。获得请求之后,服务器需要接受连接,之后再处理事务。

实现服务端具体步骤

总的来说,TCP服务端主要实现以下步骤:

  1. 创建一个监听套接字对象,指定使用TCP协议
  2. 绑定套接字到特定的地址和端口号
  3. 调用listen方法,监听连接请求
  4. 接收连接,返回一个新的套接字用于与客户端通信,以及客户端的地址
  5. 处理请求
  6. 发送响应
  7. 本次连接结束,关闭套接字
  8. 服务器结束,关闭监听套接字

为了便于区分,我们将第一个创建的套接字称为监听套接字,监听套接字只获取连接。后创建的套接字用于处理业务。

实现客户端具体步骤

  1. 创建套接字
  2. 使用connect方法连接到服务器
  3. 发送请求
  4. 接收响应
  5. 关闭套接字,结束连接

实现服务端

TcpServer.hpp

该头文件定义了一个服务器类,用于实现上述服务器的各个功能,具体代码如下:

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fstream>
#include <functional>
#include "InetAddr.hpp"
#include "Log.hpp"using namespace log_ns;const static int gport = 8888;
const static int gsockfd = -1;
const static int gblcklog = 8;using task_t = function<void()>;class TcpServer
{
private:
public:TcpServer(uint16_t port = gport): _port(port), _listensockfd(gsockfd), _isrunning(false){}void InitServer(){// 1.创建socket,选择TCP协议_listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(FATAL, "socket create error\n");exit(1);}LOG(FATAL, "listen socket create success,sockfd is %d\n", _listensockfd);// 创建strcut sockaddr_instruct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;// 2. bindint res = bind(_listensockfd, (struct sockaddr *)&local, sizeof(local));if (res < 0){LOG(FATAL, "server listen bind failed\n");exit(1);}LOG(FATAL, "server listen bind success\n");// 3.设置监听状态if (listen(_listensockfd, gblcklog) < 0){LOG(FATAL, "listen error\n");exit(1);}LOG(INFO, "listen suceess\n");}void Loop(){_isrunning = true;while (_isrunning){struct sockaddr_in client;socklen_t len = sizeof(client);// 4.获取新连接int sockfd = accept(_listensockfd, (struct sockaddr *)&client, &len);if (sockfd < 0){LOG(WARNING, "accept error\n");continue;}InetAddr addr(client);LOG(INFO, "get a new link,client info:%s,sockfd is %d\n", addr.AddrStr().c_str(), sockfd);// 处理业务// v0--简单读取消息Service(sockfd, addr);}_isrunning = false;}void Service(int sockfd, InetAddr addr){while (true){// 接收消息char inbuff[1024];ssize_t n = read(sockfd, inbuff, sizeof(inbuff) - 1);if (n > 0){inbuff[n] = 0;LOG(INFO, "get message from client ,message is %s\n", inbuff);// 回响string echo_message = "[server say]# ";echo_message += inbuff;write(sockfd, echo_message.c_str(), echo_message.size());}else if (n == 0){LOG(INFO, "client %s quit\n", addr.AddrStr().c_str());break;}else{LOG(ERROR, "read error: %s\n", addr.AddrStr().c_str());break;}}close(sockfd);}private:uint16_t _port;int _listensockfd;bool _isrunning;
};

TcpServerMain.cpp

该文件定义了服务端对象,执行服务端逻辑

#include "TcpServer.hpp"
#include <iostream>
#include <memory>
int main(int argc, char *argv[])
{if (argc != 2){std::cerr << "Usage: " << argv[0] << " local-port" << std::endl;exit(0);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<TcpServer> tcvr = make_unique<TcpServer>(port);tcvr->InitServer();tcvr->Loop();return 0;
}

实现客户端

TcpClientMain.cpp

该文件实现了客户端发送请求的逻辑

#include <iostream>
#include <cstring>
#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::cerr << "Usage : " << argv[0] << " server-ip  server-port\n"<< std::endl;exit(0);}std::string ip = argv[1];uint16_t port = std::stoi(argv[2]);// 1.创建sockfdint sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){std::cerr << "create socket error" << std::endl;exit(0);}struct sockaddr_in peer;memset(&peer, 0, sizeof(peer));peer.sin_family = AF_INET;inet_pton(AF_INET, ip.c_str(), &peer.sin_addr);peer.sin_port = htons(port);// 2.连接服务器int n = connect(sockfd, (struct sockaddr *)&peer, sizeof(peer));if (n < 0){std::cerr << "connect socket error" << std::endl;exit(2);}// 3.处理业务while (true){std::string message;std::cout << "Enter #";std::getline(std::cin, message);write(sockfd, message.c_str(), message.size());char echo_buff[1024];int res = read(sockfd, echo_buff, sizeof(echo_buff) - 1);if (res > 0){echo_buff[res] = 0;std::cout << echo_buff << std::endl;}else{break;}}close(sockfd);return 0;
}

运行结果在这里插入图片描述

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

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

相关文章

达梦数据库的系统视图v$dmwatcher

达梦数据库的系统视图v$dmwatcher 查询当前登录实例所对应的守护进程信息&#xff0c;注意一个守护进程可以同时守护多个组的实例&#xff0c;因此查询结果中部分字段&#xff08;N_GROUP、SWITCH_COUNT&#xff09;为守护进程的全局信息&#xff0c;并不是当前登录实例自身的…

0401-403组合逻辑电路的分析设计竞争冒险

组合逻辑电路的分析设计&竞争冒险 4.组合逻辑电路1.目录2.教学基本要求3.序 关于组合逻辑电路 4.1组合逻辑电路分析与设计一、组合逻辑电路分析二、组合逻辑电路的分析步骤&#xff1a;三、组合逻辑电路的分析举例例1 奇校验电路例2.对输入的二进制求反码例3.一个工厂发电的…

CSS 的环境变量函数env()

在CSS中&#xff0c;env() 函数并不是传统意义上的“环境变量”函数&#xff0c;如你在编程语言中可能遇到的那样。相反&#xff0c;env() 是CSS中的一个函数&#xff0c;它用于访问由宿主环境&#xff08;如浏览器&#xff09;提供给CSS的自定义属性&#xff08;也称为环境变量…

使用 PVE 自签 CA 证书签发新证书

前言 PVE 安装时会自动创建一个有效期 10 年的 CA 证书, 我们可以利用这个 CA 证书给虚拟机中的 Web 应用签发新的 TLS 证书用于提供 HTTPS 服务. 下面以 PVE 虚拟机中通过 Docker 跑的一个 雷池 应用为例进行演示. PVE 证书位置 官方文档: https://pve.proxmox.com/wiki/Pr…

服务攻防-框架安全(漏洞复现)

关闭靶场 sudo docker-compose down 运行此靶场 sudo docker-compose up -d 查看启动环境 sudo docker ps 运行dockers容器 docker exec -it 64052abd288b /bin/bash thinkphp框架 thinkphp 2 - rce漏洞复现 docker exec -it 731dbae0e0b5 /bin/bash 集成化工具扫描 可以命令…

Qt区分鼠标按下时移动的是哪个多边形

使用不同的鼠标事件处理器&#xff1a;为每个多边形分配不同的事件处理器&#xff0c;或者在同一个处理器中使用逻辑来区分。 检查鼠标点击位置&#xff1a;在鼠标按下事件中&#xff0c;检查鼠标的位置是否在某个多边形的边上或顶点上。 使用图形的标识符&#xff1a;给每个…

十五届蓝桥杯JAVA B组题目详解(持续更新中)

试题 B: 类斐波那契循环数 我发现蓝桥杯的题目现在就是要费时间去理解&#xff0c;所以还是审题很重要&#xff0c;这道题的思路就是&#xff0c;一个n位数的前n个数&#xff0c;都是对应的位数上的值&#xff0c;比如说12345&#xff0c;五位数是吧&#xff0c;那数列S的前五位…

phpstudy下载使用以及搭建本地SQL labs靶场

一&#xff0c;PHP study 小皮面板(phpstudy) - 让天下没有难配的服务器环境&#xff01; (xp.cn) 1&#xff0c;下载。 根据自己电脑系统下载对应的版本。 双击exe文件运行 选择下载目录&#xff08;路径不能有中文名&#xff09;。 2&#xff0c;使用。 启动阿帕奇和MySQ…

AI学习指南机器学习篇-自组织映射(Self-Organizing Maps,SOM)简介

AI学习指南机器学习篇-自组织映射&#xff08;Self-Organizing Maps&#xff0c;SOM&#xff09;简介 自组织映射&#xff08;Self-Organizing Maps&#xff0c;SOM&#xff09;是一种无监督学习算法&#xff0c;经常被用于降维、聚类和可视化高维数据。本篇博客将对SOM算法的…

数据库第二次作业

1.建立数据库 2.插入数据 3.完成查询 &#xff08;1&#xff09;、显示所有职工的基本信息。 &#xff08;2&#xff09;、查询所有职工所属部门的部门号&#xff0c;不显示重复的部门号。 &#xff08;3&#xff09;、求出所有职工的人数。 &#xff08;4&#xff09;、列…

FreeRTOS 信号量

信号量是操作系统中重要的一部分&#xff0c;信号量是任务间同步的一种机制&#xff0c;信号量可以用在多 任务访问同一资源时的资源管理。FreeRTOS 提供了多种信号量&#xff0c;按信号量的功能可分为二值信号量、计数型信号量、互斥信号量和递归互斥信号量。 使用信号量相关…

C#各种锁知识点

先上总结&#xff1a; 锁类型特点适用场景优点缺点自旋锁忙等待实现锁定&#xff0c;适合高并发短时间锁定高并发环境&#xff0c;短时间锁定&#xff0c;仅限单进程多线程同步开销低&#xff0c;避免线程上下文切换忙等待消耗CPU资源&#xff0c;不适合长时间锁定&#xff0…

组队学习——贝叶斯分类器

前言 本次数据继续沿用上一次主题的【组队学习——支持向量机-CSDN博客】 数据处理部分延续【组队学习——支持向量机】主题的处理办法对应划分训练集和验证集 模型选择 本次贝叶斯分类器模型的较多&#xff0c;常用的为高斯朴素贝叶斯分类器、多项式朴素贝叶斯分类器、伯努…

配置文件格式 XML 快速上手

文章目录 1.语法2.实例3.解析参考文献 XML&#xff08;Extensible Markup Language&#xff09;是可扩展标记语言&#xff0c;用来传输和存储数据。因为其允许用户自定义标记名称&#xff0c;具有自我描述性&#xff0c;可灵活地用于存储服务配置信息。 1.语法 XML 文档结构是…

JavaWeb总结

终于结束了JavaWeb的学习&#xff0c;个人感觉其实就是学习客户端与服务端交互的中间件&#xff0c;以及服务端处理的逻辑&#xff0c;来帮助我们构建整个项目的运转逻辑&#xff0c;从客户端到服务器再到客户端&#xff0c;核心是围绕着一系列的请求和响应如何处理&#xff0c…

嵌入式Linux学习: platform 设备驱动实验

在Linux中&#xff0c;Platform&#xff08;平台&#xff09;机制是一个重要的设备驱动管理框架&#xff0c;它主要在Linux 2.6内核及以后的版本中引入。Platform机制的主要目的是提供一种统一的方式来管理那些不直接挂靠在传统物理总线&#xff08;如USB、PCI、I2C、SPI等&…

信息学奥赛一本通 1270:【例9.14】混合背包

【题目描述】 一个旅行者有一个最多能装V公斤的背包&#xff0c;现在有n件物品&#xff0c;它们的重量分别是W1&#xff0c;W2&#xff0c;…,Wn &#xff0c;它们的价值分别为C1,C2,…,Cn。有的物品只可以取一次&#xff08;01背包&#xff09;&#xff0c;有的物品可以取无限…

如何理解ref toRef和toRefs

是什么 ref 生成值类型的响应式数据可用于模板和reactive通过.value修改值 ref也可以像vue2中的ref那样使用 toRef 针对一个响应式对象&#xff08;reactive&#xff09;的prop创建一个ref两者保持引用关系 toRefs 将响应式对象&#xff08;reactive封装&#xff09;转换…

论文阅读:Speculative RAG: Enhancing Retrieval Augmented Generation through Drafting

论文地址&#xff1a;https://arxiv.org/abs/2407.08223 RAG 将 LLM 的生成能力与外部知识源相结合&#xff0c;以提供更准确和最新的响应。最近的 RAG 进展侧重于通过迭代 LLM 完善或通过 LLM 的额外指令调整获得自我批判能力来改进检索结果。在这项工作中&#xff0c;作者介…

编程中的智慧六:单例、原型、建造者

上一篇咱们结合Spring介绍了设计模式中的工厂模式相关方法&#xff0c;其实现在Java开发基本上都是基于Spring框架开发&#xff0c;所以后续我们在开发过程中基本上很少自己重写一个工厂模式&#xff0c;都是直接使用Spring来完成。今天咱们接着看剩下的创建型设计模式&#xf…