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

socket编程——使用UDP实现的一个回显功能

功能如下:

对于服务端

1. 建立套接字(IPV4协议和UDP)

2. 绑定套接字

3. 读取接收到的消息

4. 发送消息

5. 关闭连接

对于客户端

1. 获取服务端的ip和端口号(命令行参数)

2.建立套接字

3. 发送消息

4. 读取服务端发来的消息

5. 关闭连接

客户端不需要bind绑定套接字,操作系统会自动的绑定本机ip和一个随机的端口给sockfd,为了避免端口号冲突

UdpServer.hpp

定义了服务器类,传入端口来绑定套接字,先初始化,后调用。

#pragma once#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>
#include "LockGuard.hpp"
#include "Log.hpp"
#include "nocopy.hpp"
#include "InetAddr.hpp"using namespace log_ns;enum
{SOCKET_ERROR = 1,BIND_ERROR,
};
static const int gsockfd = -1;
static const uint16_t glocalport = 8888;class UdpServer : public nocopy
{
public:UdpServer(uint16_t localport = glocalport) : _localport(localport),_isrunning(false),_sockfd(gsockfd){}~UdpServer(){if (_sockfd >= 0)close(_sockfd);}void InitServer(){// 创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "套接字创建失败");exit(1);}LOG(DEBUG, "套接字创建成功,该套接字为 %d\n", _sockfd);// 绑定端口struct sockaddr_in local;local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons(_localport);int n = bind(_sockfd, (sockaddr *)(&local), sizeof(local));if (n < 0){LOG(FATAL, "绑定失败\n");exit(1);}LOG(DEBUG, "绑定成功");}void Start(){_isrunning = true;char buffer[1024];while (_isrunning){// 获取数据struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)(&peer), &len);if (n < 0){LOG(FATAL, "recvfrom error!\n");}else{LOG(DEBUG, "recvfrom success!\n");InetAddr addr(peer);buffer[n] = '\0';std::cout << "[" << addr.Ip() << ":" << addr.Port() << "]# " << buffer << std::endl;std::string echo_string = "[udp_server echo]# ";echo_string += buffer;ssize_t res = sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr *)&peer, len);if (res < 0){LOG(FATAL, "echo sendto error!\n");}else{LOG(DEBUG, "echo sendto success!\n");}}}_isrunning = false;}private:int _sockfd;         // 套接字uint16_t _localport; // 本地端口bool _isrunning;     // 是否正在运行
};

nococpy.hpp文件

该文件实现了一个不可拷贝构造和赋值拷贝的类,该类的所有派生类都不可以拷贝构造和赋值拷贝

#pragma once#include <iostream>class nocopy{
public:nocopy(){}nocopy(const nocopy&)=delete;nocopy& operator=(const nocopy&)=delete;~nocopy(){}
};

InetAddr.hpp头文件

该文件实现了一个可以将网络序列包括IP和端口号转化成主机序列的类

#pragma once#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddr{
private:void ToHost(const sockaddr_in& addr){_ip = inet_ntoa(addr.sin_addr);_port = ntohs(addr.sin_port);}
public:InetAddr(const sockaddr_in& addr){ToHost(addr);}std::string Ip(){return _ip;}uint16_t Port(){return _port;}
private:std::string _ip;uint16_t _port;
};

Log.hpp头文件

该文件定义了一个日志类,并提供了宏来进行便捷的打印日志

#pragma once
#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>
#include <cstring>
#include <fstream>
#include <sys/types.h>
#include <cstdarg>#include <ctime>
#include "LockGuard.hpp"namespace log_ns
{enum // 日志等级{DEBUG = 1,INFO,WARNING,ERROR,FATAL};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";}}struct logmessage{public:std::string _level;pid_t id;std::string _filename;int _filenumber;std::string _curr_time;std::string _message_info;};#define SCREEN_TYPE 1
#define FILE_TYPE 2const std::string glogfile = "./log.txt";pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;class Log{private:std::string CurrTimeToString(){time_t now = time(nullptr);struct tm *curr_time = localtime(&now);char buff[128];snprintf(buff, sizeof(buff), "%d-%02d-%02d %02d:%02d:%02d", curr_time->tm_year - 1900, curr_time->tm_mon + 1, curr_time->tm_mday, curr_time->tm_hour, curr_time->tm_min, curr_time->tm_sec);return buff;}void FlushLogToScreen(const logmessage &lg) // 将日志信息输出到显示器文件中{printf("[%s][%d][%s][%d][%s]:%s \n", lg._level.c_str(), lg.id, lg._filename.c_str(), lg._filenumber, lg._curr_time.c_str(), lg._message_info.c_str());}void FlushLogToFile(const logmessage &lg) // 将日志信息输出到指定文件中{std::ofstream out(_logfile, std::ios::app);if (!out.is_open()){return;}char buff[2048] = {'\0'};snprintf(buff, sizeof(buff), "[%s][%d][%s][%d][%s]:%s \n", lg._level.c_str(), lg.id, lg._filename.c_str(), lg._filenumber, lg._curr_time.c_str(), lg._message_info.c_str());out.write(buff, sizeof(buff));out.close();}public:Log(const std::string &file = glogfile): _logfile(file), _type(SCREEN_TYPE){}// 打印日志void PrintLog(const logmessage &lg){LockGuard lockguard(&glock);switch (_type){case SCREEN_TYPE:FlushLogToScreen(lg);break;case FILE_TYPE:FlushLogToFile(lg);break;}}// 切换输出方式void Enable(int type){_type = type;}// 构造logmessage对象,并打印void LogMessage(std::string filename, int filenumber, int level, const char *format, ...) //...表示可变参数列表{logmessage lg;lg._filename = filename;lg._filenumber = filenumber;lg._level = LevelToString(level);// std::cout << lg._level << std::endl;lg.id = getpid();lg._curr_time = CurrTimeToString();// lg._message_info = CurrTimeToString();va_list ap;           // 获取所有参数va_start(ap, format); // 取出参数包的部分char log_info[1024];vsnprintf(log_info, sizeof(log_info), format, ap); // 格式转换va_end(ap);lg._message_info = log_info;// 打印这个日志PrintLog(lg);}private:int _type;std::string _logfile;};Log lg;
#define LOG(LeveL, Format, ...)                                          \do                                                                   \{                                                                    \lg.LogMessage(__FILE__, __LINE__, LeveL, Format, ##__VA_ARGS__); \} while (0)#define EnableScreen()          \do                          \{                           \lg.Enable(SCREEN_TYPE); \} while (0)#define EnableFile()          \do                        \{                         \lg.Enable(FILE_TYPE); \} while (0)
};

UdpServerMain.cpp源文件

实现了服务器端的处理逻辑

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

UdpClientMain.cpp源文件

简单模拟了一个客户端,包括处理数据的逻辑

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "InetAddr.hpp"
#include <memory>
using namespace std;
int main(int argc, char *argv[])
{if (argc != 3){cerr << "Usage:" << argv[0] << " local-port" << endl;exit(0);}string ip = argv[1];uint16_t port = stoi(argv[2]);int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){cerr << "client create sockfd error!\n"<< endl;exit(1);}// 客户端一般不用绑定套接字,操作系统会在第一次发送消息时自动绑定本机ip和一个随机的portstruct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(ip.c_str());server.sin_port = htons(port);// 执行向服务端发送数据以及接收服务端响应while (true){string line;cout << "please input: ";getline(cin, line);ssize_t res = sendto(sockfd, line.c_str(), line.size(), 0, (struct sockaddr *)&server, sizeof(server));if (res > 0){struct sockaddr_in temp;memset(&temp, 0, sizeof(temp));char buff[1024];socklen_t len = 0;int n = recvfrom(sockfd, buff, sizeof(buff) - 1, 0, (struct sockaddr *)&temp, &len);if (n > 0){buff[n] = '\0';cout << buff << endl;}else{cerr << "client recvfrom error" << endl;break;}}else{cout << "client sendto error" << endl;break;}}return 0;
}

http://www.dtcms.com/a/588795.html

相关文章:

  • 侠客行・iOS 26 Liquid Glass TabBar 破阵记
  • G882磁力仪方向调整
  • 站长友情链接网上卖货的平台有哪些
  • 弱函数:嵌入式回调的最佳实践
  • 如何在实验室服务器上搭建python虚拟环境?安装conda并配置虚拟环境
  • 【开发者导航】轻量可微调且开源的大语言模型家族:LLaMA
  • 北京网站建立公司创意包装设计网站
  • INSERT INTO … SELECT … 常见问答(含样例)
  • 做图素材的网站有哪些昆明做网站公司有哪些
  • 移动端网站定制搞笑网站模板
  • 网站后台的数据库怎么做工业产品设计要学什么
  • 你去湖北省住房城乡建设厅网站查软件开发好学吗
  • 北京手机网站设计公司益阳建设公司网站
  • 网站建设ip微信小程序 做网站
  • 单位网站建设的必要性网站如何被收录
  • 狗贩子怎么做网站卖狗成都网站建设餐饮
  • 如何开发网站自己做站长哪个网站可以做编程题
  • 深圳制作公司网站wordpress 显示微信二维码
  • 微信网站建设费记什么科目中山网站搭建
  • 有关小城镇建设的网站网站是怎么建设的
  • 国外一直小猫做图标的网站centos lnmp wordpress
  • 网站备案不注销有什么后果网站免费注册域名
  • 建网站的重要性内蒙古互联网公司
  • 网站建设价格一览表wordpress主页显示博客
  • 没有ipc备案的网站重庆网站设计找重庆最佳科技
  • 网站建设方案内容wordpress打开失败
  • 艺术网站源码杭州品牌网站建设公司
  • 做网站有什么语言好大型网站响应式
  • asp网站图片前端程序员需要掌握哪些基本技术
  • 网站推广公司新锐投资网站源码