基于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() 初次学习最终效果…

最长上升子序列2

最长上升子序列2 时间限制&#xff1a;1秒 内存限制&#xff1a;64M 题目描述 给定一个长度为 N 的数列&#xff0c;求数值严格单调递增的子序列的长度最长是多少。 输入描述 第一行包含整数 N。 第二行包含 N 个整数&#xff0c;表示完整序列。 输出描述 输出一…

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){ /…

算法学习(十七)规律

规律 1. 概念 模拟题目过程探索问题规律找出隐含条件 2. 解题技巧&#xff08;我的总结&#xff09; 1> 将 问题中的抽象条件 找出实际意思 题目说明实现649. Dota2 参议院题中每个议员最好表现 禁止后面最近的对方议员 或 禁止前面最远的对方议员我的提交1702. 修改后的…

Python基础教程:从入门到实践

Python&#xff0c;一种解释型、交互式、面向对象的编程语言&#xff0c;因其简洁易懂的语法和强大的功能库而广受欢迎。无论是数据分析、机器学习还是Web开发&#xff0c;Python都发挥着重要作用。本文将带你走进Python的世界&#xff0c;从基础语法开始&#xff0c;逐步深入实…

【智能算法】跳蛛优化算法(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;不定时更…

C++ 引用和指针

C++ 中的引用(Reference)和指针(Pointer)是两种不同的概念,它们在语法上和语义上有很多不同之处,下面是它们的区别: 语法: 引用使用 & 符号声明,并且必须在初始化时绑定到一个对象上。例如:int& ref = obj;指针使用 * 符号声明,并且可以在任何时候指向一个对…

Java入门第6课

1.数据类型的自动转化 java的数据转化是容量小的部分向大的方向转&#xff0c;这个部分很简单&#xff0c;byte->short->int->long&#xff0c;char->int->long;int->double 有一些数据转化虽然是自动的&#xff0c;但是存在数据精度丢失&#xff0c;比如i…

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

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

【ARM 嵌入式 C 入门及渐进 21 -- 内存分配相关函数malloc 与 realloc】

请阅读【嵌入式开发学习必备专栏 】 文章目录 malloc 与 reallocmallocrealloc主要差异点 malloc 与 realloc malloc 和 realloc 都是 C 语言标准库中的函数&#xff0c;用于动态内存分配。它们在使用上有明显的区别&#xff1a; malloc malloc&#xff08;Memory Allocatio…

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 输…