基于Socket简单的TCP网络程序

小白苦学IT的博客主页

初学者必看:Linux操作系统入门

代码仓库:Linux代码仓库

❤关注我一起讨论和学习Linux系统

TCP单例模式的多线程版本的英汉互译服务器

我们先来认识一下与udp服务器实现的不同的接口:

TCP服务器端

socket():创建一个新的套接字,指定使用的协议族(如IPv4)、套接字类型(如SOCK_STREAM表示TCP)和协议(通常为0,表示使用默认协议)。

bind():将套接字绑定到一个特定的地址和端口号上,这样客户端就可以通过这个地址和端口号连接到服务器。

listen():使套接字进入监听状态,等待客户端的连接请求。可以指定最大连接队列长度。

accept():接受一个客户端的连接请求,并返回一个新的套接字,用于与这个客户端进行通信。原始的套接字继续用于监听其他客户端的连接请求。

read() :从已连接的客户端套接字读取数据。TCP是字节流协议,因此你需要按照某种协议或方式来分割和解析接收到的数据。

write():向已连接的客户端套接字发送数据。

TCP客户端

socket():同样创建一个新的套接字。

connect():发起一个到服务器地址和端口号的连接请求。

read() :从服务器套接字读取数据。

write():向服务器套接字发送数据。

封装TcpSocket

TcpServer.hpp

#pragma once#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"
#include <cstring>
#include<pthread.h>
#include"ThreadPool.hpp"
#include "task.hpp"const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 5;enum
{UsageError = 1,SocketError,BindError,ListenError,
};class TcpServer;class ThreadData1
{
public:ThreadData1(int fd,const std::string & ip,const uint16_t & port,TcpServer* t):sockfd(fd),clientip(ip),clientport(port),tsvr(t){}public:int sockfd;std::string clientip;uint16_t clientport;TcpServer* tsvr;
};class TcpServer
{public:TcpServer(const uint16_t port, const std::string &ip = defaultip): _listensock(-1), _port(port), _ip(ip){}void InitServer(){_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){log.LogMessage(FATAL, "create socket error , errno:%d, strerror: %s", errno, strerror(errno));exit(SocketError);}log.LogMessage(INFO, "create socket success ,_listensock:%d", _listensock);struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);inet_aton(_ip.c_str(), &(local.sin_addr));if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0){log.LogMessage(FATAL, "bind error , errno:%d, strerror: %s", errno, strerror(errno));exit(BindError);}log.LogMessage(INFO, "bind socket success ,_listensock:%d", _listensock);// Tcp是面向连接的,所以服务器一般是比较“被动”的,服务器一种处于一种if (listen(_listensock, backlog) < 0){log.LogMessage(FATAL, "listen error , errno:%d, strerror: %s", errno, strerror(errno));exit(ListenError);}log.LogMessage(INFO, "listen success ,_listensock:%d", _listensock);}void Start(){ThreadPool<Task>::GetInstance()->Start();log.LogMessage(INFO, "tcpServer is running ...");for (;;){// 1.获取新链接struct sockaddr_in client;socklen_t len = sizeof(client);int sockfd = accept(_listensock, (struct sockaddr *)&client, &len);if (sockfd < 0){log.LogMessage(WARNING, "accept error , errno:%d, strerror: %s", errno, strerror(errno));continue;}uint16_t clientport = ntohs(client.sin_port);char clientip[32];inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));// 2.根据新连接来进行通信log.LogMessage(INFO, "get a new link ... client ip : %s, client port : %d , sockfd:%d", clientip, clientport, sockfd);//version4 线程池版本Task t(sockfd,clientport,clientip);ThreadPool<Task>::GetInstance()->Push(t);}}~TcpServer() {}private:int _listensock;uint16_t _port;std::string _ip;
};

ThreadPool.hpp

#pragma once#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>struct ThreadInfo
{pthread_t tid;std::string name;
};static const int defalutnum = 10;template <class T>
class ThreadPool
{
public:void Lock(){pthread_mutex_lock(&mutex_);}void Unlock(){pthread_mutex_unlock(&mutex_);}void Wakeup(){pthread_cond_signal(&cond_);}void ThreadSleep(){pthread_cond_wait(&cond_, &mutex_);}bool IsQueueEmpty(){return tasks_.empty();}std::string GetThreadName(pthread_t tid){for (const auto &ti : threads_){if (ti.tid == tid)return ti.name;}return "None";}public:static void *HandlerTask(void *args){ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);std::string name = tp->GetThreadName(pthread_self());while (true){tp->Lock();while (tp->IsQueueEmpty()){tp->ThreadSleep();}T t = tp->Pop();tp->Unlock();t();}}void Start(){int num = threads_.size();for (int i = 0; i < num; i++){threads_[i].name = "thread-" + std::to_string(i + 1);pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);}}T Pop(){T t = tasks_.front();tasks_.pop();return t;}void Push(const T &t){Lock();tasks_.push(t);Wakeup();Unlock();}static ThreadPool<T> *GetInstance(){if (nullptr == tp_) // ???{pthread_mutex_lock(&lock_);if (nullptr == tp_){std::cout << "log: singleton create done first!" << std::endl;tp_ = new ThreadPool<T>();}pthread_mutex_unlock(&lock_);}return tp_;}private:ThreadPool(int num = defalutnum) : threads_(num){pthread_mutex_init(&mutex_, nullptr);pthread_cond_init(&cond_, nullptr);}~ThreadPool(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&cond_);}ThreadPool(const ThreadPool<T> &) = delete;const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // a=b=c
private:std::vector<ThreadInfo> threads_;std::queue<T> tasks_;pthread_mutex_t mutex_;pthread_cond_t cond_;static ThreadPool<T> *tp_;static pthread_mutex_t lock_;
};template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr;template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;

Main.cc

#include"TcpServer.hpp"
#include<memory>
#include<iostream>void Usage(std::string proc)
{std::cout<<"\n\rUsage: "<<proc<<" port[1024+]"<<std::endl;
}int main(int argc,char* argv[])
{if(argc!=2){Usage(argv[0]);exit(UsageError);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<TcpServer> tcp_svr(new TcpServer(port));tcp_svr->InitServer();tcp_svr->Start();return 0;
}

TcpClient.cc

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>
#include<unistd.h>void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " serverip  serverport[1024+]" << std::endl;
}int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){std::cerr << "socket error" << std::endl;return 1;}struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr));server.sin_port = htons(serverport);// tcp要不要bind? 要bind 要不要显示的bind? 不用显示的bind 系统进行bind,随机端口// 客户端发起connect的时候,进行自动随机bind.int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));if (n < 0){std::cerr << "connect error" << std::endl;return 2;}std::string message;std::cout<<"please Enter# ";std::getline(std::cin,message);write(sockfd,message.c_str(),message.size());char inbuffer[4096];int r = read(sockfd,inbuffer,sizeof(inbuffer));if(r>0){inbuffer[r] = 0;std::cout<<inbuffer<<std::endl;}close(sockfd);std::cout<<"Connection closed by foreign host"<<std::endl;return 0;
}

Init.hpp 

#pragma once#include<iostream>
#include<string>
#include<fstream>
#include<unordered_map>
#include "Log.hpp"const std::string dictname = "./translation.txt";
const std::string sep = ":";static bool Split(std::string & s,std::string *part1,std::string *part2)
{auto pos = s.find(sep);if(pos==std::string::npos){return false;}*part1 = s.substr(0,pos);*part2 = s.substr(pos+1);return true;
}class Init
{
public:Init(){std::ifstream in(dictname);if(!in.is_open()){log.LogMessage(FATAL,"ifstream open %s error",dictname.c_str());exit(1);}std::string line;while(std::getline(in,line)){std::string part1,part2;Split(line,&part1,&part2);dict.insert({part1,part2});}in.close();}std::string translation(const std::string &key){auto iter = dict.find(key);if(iter == dict.end()){return "UnKnow";}else return iter->second;}
private:std::unordered_map<std::string,std::string> dict;};

Log.hpp 

#pragma once#include <iostream>
#include <cstdarg>
#include <ctime>
#include <string>
#include <unistd.h>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>enum
{DEBUG = 0,INFO,WARNING,ERROR,FATAL
};enum
{Screen = 10,Onefile,Classfile
};std::string LevelToString(int level)
{switch (level){case DEBUG:return "Debug";case INFO:return "Info";case WARNING:return "Warning";case ERROR:return "Error";case FATAL:return "Fatal";default:return "Unknown";}
}const int defaultstyle = Screen;
const std::string default_filename = "log.";
const std::string logdir="log";class Log
{
public:Log():style(defaultstyle),filename(default_filename){mkdir(logdir.c_str(),0775);}void Enable(int sty){style = sty;}std::string TimestampToLocalTime(){time_t curr = time(nullptr);struct tm *currtime = localtime(&curr);char time_buffer[128];snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",currtime->tm_year + 1900, currtime->tm_mon, currtime->tm_mday, currtime->tm_hour,currtime->tm_min, currtime->tm_sec);return time_buffer;}void WriteLog(const std::string &levelstr, const std::string &message){switch (style){case Screen:std::cout << message<<std::endl;break;case Onefile:WriteLogToOnefile("all", message);break;case Classfile:WriteLogToClassfile(levelstr, message);break;default:break;}}void WriteLogToOnefile(const std::string &logname, const std::string &message){umask(0);int fd = open(logname.c_str(),O_CREAT | O_WRONLY | O_APPEND,0666);if(fd<0)return;write(fd,message.c_str(),message.size());close(fd);// std::ofstream out(logname);// if (!out.is_open())//     return;// out.write(message.c_str(), message.size());// out.close();}void WriteLogToClassfile(const std::string &levelstr, const std::string &message){std::string logname = logdir;logname+="/";logname+=filename;logname += levelstr;WriteLogToOnefile(logname, message);}void LogMessage(int level, const char *format, ...) // 类c的日志接口{char rightbuffer[1024];va_list args;va_start(args, format);vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);va_end(args);char leftbuffer[1024];std::string curtime = TimestampToLocalTime();std::string levelstr = LevelToString(level);std::string idstr = std::to_string(getpid());snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s]",levelstr.c_str(), curtime.c_str(), idstr.c_str());std::string logInfo = leftbuffer;logInfo += rightbuffer;WriteLog(levelstr, logInfo);}~Log() {}private:int style;std::string filename;
};Log log;class Conf
{
public:Conf(){log.Enable(Screen);}~Conf(){}
};Conf conf;

task.hpp

#pragma once
#include<string>
#include<iostream>
#include"Log.hpp"
#include"Init.hpp"Init init;class Task
{
public:Task(int sockfd, const uint16_t &clientport, const std::string &clientip):clientip_(clientip),clientport_(clientport),sockfd_(sockfd){}void Run(){char buffer[4096];// 测试代码ssize_t n = read(sockfd_, buffer, sizeof(buffer));if (n > 0){buffer[n] = 0;std::cout << "client key# " << buffer << std::endl;std::string echo_string = init.translation(buffer);write(sockfd_, echo_string.c_str(), echo_string.size());}else if (n == 0){log.LogMessage(INFO, "%s:%d quit,server close sockfd:%d", clientip_.c_str(), clientport_, sockfd_);}else{log.LogMessage(WARNING, "read error,sockfd:%d,clientip:%s ,clientport:%d ", sockfd_, clientip_.c_str(), clientport_);}close(sockfd_);}void operator()()//运算符重载实现仿函数{Run();}~Task() {}
private:int sockfd_;std::string clientip_;uint16_t clientport_;
};

translation.txt

# 英汉互译键值对  
# English-Chinese Key-Value Pairs  hello: 你好  
world: 世界  
goodbye: 再见  
thank you: 谢谢  
please: 请  
welcome: 欢迎  
happy: 快乐的  
sad: 悲伤的  
angry: 生气的  
excited: 兴奋的  apple: 苹果  
banana: 香蕉  
orange: 橙子  
grape: 葡萄  
peach: 桃子  
watermelon: 西瓜  
strawberry: 草莓  
cherry: 樱桃  morning: 早上  
afternoon: 下午  
evening: 晚上  
night: 夜晚  
weekday: 工作日  
weekend: 周末  
January: 一月  
February: 二月  
March: 三月  
April: 四月

只是写入了部分单词,比较简单,想要实现的更完整可以自行加入一些单词与中文意思及其解释等内容可以让该词典内容更丰富。

 

运行结果:

服务器一直在运行,客户端访问一次就退出了。

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

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

相关文章

设置你的第一个React应用

目录 一、React入门 1.1 你好React 1.2 创建React 1.3 应用结构 二、总结 2.1 定义组件 2.2 组件源码 三、组件详解 注意事项 3.1 组件三部曲 3.2 组件通信 —— props 3.3 对象数组迭代 —— map() 3.4 事件处理 3.5 钩子函数 —— useState() 初次学习最终效果…

wireshark数据流分析学习日记day3-从 Pcap 导出对象

从 HTTP 流量导出文件 过滤http请求 发现get请求上传了两个文件 保存即可 也可以保存网页 点击保存 改文件名为html结尾以便于访问 请谨慎使用此方法。如果从 pcap 中提取恶意 HTML 代码并在 Web 浏览器中查看它&#xff0c;则 HTML 可能会调用恶意域&#xff0c;这就是为什么…

HTML——5.表单、框架、颜色

一、表单 HTML 表单用于在网页中收集用户输入的数据&#xff0c;例如登录信息、搜索查询等。HTML 提供了一系列的表单元素&#xff0c;允许用户输入文本、选择选项、提交数据等。 <!DOCTYPE html> <html lang"en"> <head> <meta charset&q…

01.IDEA中出现Cannot resolve symbol ‘SpringApplication异常

试了很多次&#xff0c;看了这篇文章终于发现了问题。IDEA解决springboot工程中Cannot resolve symbol SpringApplication异常-CSDN博客 我存在的问题在于Maven home path有误&#xff0c;改正之后就没有问题&#xff0c;不标红了。

C语言实现快速排序算法

1. 什么是快速排序算法 快速排序的核心思想是通过分治法&#xff08;Divide and Conquer&#xff09;来实现排序。 算法的基本步骤是: 1. 选择一个基准值&#xff08;通常是数组中的某个元素&#xff09;&#xff0c;将数组分成两部分&#xff0c;使得左边的部分所有元素都小于…

RabbitMQ3.13.x之六_RabbitMQ使用场景

RabbitMQ3.13.x之六_RabbitMQ使用场景 文章目录 RabbitMQ3.13.x之六_RabbitMQ使用场景1. 为什么选择 RabbitMQ&#xff1f;1. 可互操作2. 灵活3. 可靠 2. 常见用户案例1. 服务解耦2. 远程过程调用3. 流处理4. 物联网 1. 为什么选择 RabbitMQ&#xff1f; RabbitMQ 是一个可靠且…

C语言 | Leetcode C语言题解之第8题字符串转换整数atoi

题目&#xff1a; 题解&#xff1a; int myAtoi(char * s){int i0;int out0;int pol1;int lenstrlen(s);if(len0) return 0;while(s[i] ) i; //删除空格if(s[i]-){ //判断正负pol-1;i;}else if(s[i]){pol1;i;}else{pol1;}while(s[i]!\0){if(s[i]<0||s[i]>9){ /…

【智能算法】跳蛛优化算法(AOA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2021年&#xff0c;H Peraza-Vzquez等人受到跳蛛狩猎行为启发&#xff0c;提出了跳蛛优化算法&#xff08;Jumping Spider Optimization Algorithm&#xff0c;JSOA&#xff09;。 2.算法原理 2.1…

【Kaggle】练习赛《鲍鱼年龄预测》(上)

前言 上一篇文章&#xff0c;讲解了《肥胖风险的多类别预测》机器学习方面的文章&#xff0c;主要是多分类算法的运用&#xff0c;本文是一个回归的算法&#xff0c;本期是2024年4月份的题目《Regression with an Abalone Dataset》即《鲍鱼年龄预测》&#xff0c;在此分享高手…

C++数据结构与算法——回溯算法组合问题

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

特征增强自蒸馏卷积神经网络

目录 1.1 模型总体架构 1.2 特征增强金字塔模块 1.3 辅助分类器 1.1 模型总体架构 与自然图像相比&#xff0c;遥感场景图像地物较为复杂&#xff0c;具有类间相似度高和类内差异大的特点&#xff0c;这导致常用的网络模型难以有效学习遥感场景图像的表征特征。此外&#xf…

springboot之mybatisPlus多表查询及分页查询

文章目录 一、多表查询二、mybatis-plus条件查询三、分页查询 一、多表查询 可能会用到的注解 这里的场景是&#xff0c;查询每个用户及其所有的订单。就是查询你的id号的同时&#xff0c;把你所有的历史订单信息都拉出来。 表结构这样 CREATE TABLE User ( id INT PRIMARY…

【可靠性】陷阱电荷对TDDB影响的多尺度模拟

【From Accelerated to Operating Conditions: How Trapped Charge Impacts on TDDB in SiO2 and HfO2 Stacks】 文章总结&#xff1a; 本研究深入探讨了在SiO2和HfO2介质堆叠中&#xff0c;陷阱电荷对时间依赖介电击穿&#xff08;TDDB&#xff09;现象的影响。通过引入载流子…

位运算-191. 位1的个数- 136. 只出现一次的数字

位1的个数 已解答 简单 相关标签 相关企业 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中 设置位 的个数&#xff08;也被称为汉明重量&#xff09;。 示例 1&#xff1a; 输入&#xff1a;n 11 输…

Git 术语及中英文对照

完毕&#xff01;&#xff01;感谢您的收看 ----------★★历史博文集合★★---------- 我的零基础Python教程&#xff0c;Python入门篇 进阶篇 视频教程 Py安装py项目 Python模块 Python爬虫 Json Xpath 正则表达式 Selenium Etree CssGui程序开发 Tkinter Pyqt5 列表元组字…

C++从入门到精通——类的定义及类的访问限定符和封装

类的定义及类的访问限定符和封装 前言一、类的定义类的两种定义方式成员变量命名规则的建议示例 二、类的访问限定符和封装访问限定符访问限定符说明C为什么要出现访问限定符例题 封装例题 前言 类的定义是面向对象编程中的基本概念&#xff0c;它描述了一类具有相同属性和方法…

MyBatis-Plus的学习笔记

MyBatis-Plus 一、MyBatis-Plus快速入门 1.1 简介 课程版本&#xff1a;3.5.3.1 https://baomidou.com/ MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window) 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&…

基于Vision Transformer的迁移学习在乳腺X光图像分类中的应用

乳房X线摄影(MG)在乳腺癌的早期发现中起着重要作用。MG可以在早期阶段发现乳腺癌&#xff0c;即使是感觉不到肿块的小肿瘤。基于卷积神经网络(CNN)的DL最近吸引了MG的大量关注&#xff0c;因为它有助于克服CAD系统的限制(假阳性、不必要的辐射暴露、无意义的活组织检查、高回调…

51单片机入门_江协科技_21.1_开发板USB口连接建议

1. 目前我自己用的普中A2版本的开发板&#xff0c;操作失误导致在开发板连接电脑并通电的情况下误将跳线帽触碰到开发板的3.3V与GND&#xff0c;导致USB口浪涌&#xff0c;2个电脑上面的USB口烧毁&#xff0c;开发板暂时没有任何问题&#xff0c;电脑USB口现在只是接通后有电&a…

8_springboot_shiro_jwt_多端认证鉴权_多Reaml管理

1. 目标 前面一直讨论的是只有一个Reaml的场景&#xff0c;Shiro是可以管理多个Realm的。那么什么场景下&#xff0c;我们需要定义多个Realm&#xff0c;以及Shiro框架是如何管理多个Realm的&#xff0c;他们是如何工作的。本章将会解释上面的问题&#xff0c;最后会配置前面章…