【Linux】网络基础_3

news2024/11/15 9:40:11

文章目录

  • 十、网络基础
    • 5. socket编程
      • socket 常见API
      • sockaddr结构
      • 简单的UDP网络程序
  • 未完待续


十、网络基础

5. socket编程

socket 常见API

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
 
// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address,
 socklen_t address_len);
 
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
 
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
 socklen_t* address_len);
 
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
 socklen_t addrlen);

sockaddr结构

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX DomainSocket. 然而, 各种网络协议的地址格式并不相同。
在这里插入图片描述
IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。
我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型,端口号,IP地址。

简单的UDP网络程序

我们需要我们之前写的日志程序:
Log.hpp

#pragma once

#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include "LockGuard.hpp"

// 宏定义,用于定义日志格式
#define LOG(level, format, ...) do{LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__);}while (0)
// 将日志输入到文件
#define EnableFile() do{gIsSave = true;}while (0)
// 将日志输出到显示器
#define EnableScreen() do{gIsSave = false;}while (0)

bool gIsSave = false;
// 日志文件名
const std::string logname = "log.txt";

// 枚举日志级别
enum Level
{
    DEBUG = 0,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

// 保存日志到文件
void SaveFile(const std::string &filename, const std::string &message)
{
    std::ofstream out(filename, std::ios::app);
    if (!out.is_open())
    {
        return;
    }
    out << message;
    out.close();
}

// 日志级别转字符串
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";
    }
}

// 获取当前时间字符串
std::string GetTimeString()
{
    time_t curr_time = time(nullptr);
    struct tm *format_time = localtime(&curr_time);
    if (format_time == nullptr)
        return "None";
    char time_buffer[1024];
    snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
             format_time->tm_year + 1900,
             format_time->tm_mon + 1,
             format_time->tm_mday,
             format_time->tm_hour,
             format_time->tm_min,
             format_time->tm_sec);
    return time_buffer;
}

// 日志锁,同一时刻只能写一个日志
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

// 日志信息
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{
    // 日志级别
    std::string levelstr = LevelToString(level);
    // 时间
    std::string timestr = GetTimeString();
    // 进程id
    pid_t selfid = getpid();

    // 日志内容
    char buffer[1024];
    va_list arg;
    va_start(arg, format);
    vsnprintf(buffer, sizeof(buffer), format, arg);
    va_end(arg);

    // 日志格式化
    std::string message = "[" + timestr + "]" + "[" + levelstr + "]" +
                          "[" + std::to_string(selfid) + "]" +
                          "[" + filename + "]" + "[" + std::to_string(line) + "] " + buffer;
    LockGuard lockguard(&lock);

    // 输出日志
    if (!issave)
    {
        std::cout << message;
    }
    else
    {
        SaveFile(logname, message);
    }
}

封装的RAII模式的锁:
LockGuard.hpp

#ifndef __LOCK_GUARD_HPP__
#define __LOCK_GUARD_HPP__

#include <iostream>
#include <pthread.h>

class LockGuard
{
public:
    // 构造函数加锁
    LockGuard(pthread_mutex_t *mutex)
        :_mutex(mutex)
    {
        pthread_mutex_lock(_mutex);
    }

    // 析构函数解锁
    ~LockGuard()
    {
        pthread_mutex_unlock(_mutex);
    }
private:
    pthread_mutex_t *_mutex;
};

#endif

服务端代码:
UdpServer.hpp

#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"
#include "InetAddr.hpp"

enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    USAGE_ERROR
};

const static int defaultfd = -1;

class UdpServer
{
public:
    UdpServer(uint16_t port)
        :_sockfd(defaultfd)
        ,_port(port)
        ,_isrunning(false)
    {}

    void InitServer()
    {
        // 创建 UDP socket 套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            LOG(FATAL, "socket error, %s, %d\n", strerror(errno), errno);
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "socket create success, sockfd: %d\n", _sockfd);

        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        // 端口号要经过网络传输,需要转换成网络字节序
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = 0;

        // 绑定本地地址
        int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "bind error, %s, %d\n", strerror(errno), errno);
            exit(BIND_ERROR);
        }
        LOG(INFO, "socket bind success\n");
    }

    void Start()
    {
        // 启动服务器
        _isrunning = true;
        while (true)
        {
            // 接收数据
            char buffer[1024];
            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)
            {
                // 收到数据,打印
                buffer[n] = '\0';
                InetAddr addr(peer);
                LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.Ip().c_str(), addr.Port(), buffer);
                sendto(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&peer, len);
            }
            // sleep(1);
            // LOG(DEBUG, "server is running...\n");
        }
        _isrunning = false;
    }

    ~UdpServer()
    {}
private:
    int _sockfd;
    // std::string _ip;
    // 服务器所用端口号
    uint16_t _port;
    bool _isrunning;
};

客户端代码:
UdpClient.hpp

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>

void Usage(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << " local_ip local_prot\n" << std::endl;
}

int main(int argc, char* argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    // 获取本地IP地址和端口号
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        std::cerr << "socket error" << std::endl;
    }

    // 构建目标主机的socket信息
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    server.sin_addr.s_addr = inet_addr(serverip.c_str());
    std::string message;
    // 循环发送消息
    while (true)
    {
        // 输入消息
        std::cout << "Please Enter# ";
        std::getline(std::cin, message);
        sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));

        // 接收消息
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        char buffer[1024];
        ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
        if (n > 0)
        {
            std::cout << "server echo# " << buffer <<std::endl;
        }
    }
    
    return 0;
}

发送者数据:
InetAddr.hpp

#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

class InetAddr
{
private:
    void GetAddress(std::string *ip, uint16_t *port)
    {
        *port = ntohs(_addr.sin_port);
        *ip = inet_ntoa(_addr.sin_addr);
    }

public:
    InetAddr(const struct sockaddr_in &addr) : _addr(addr)
    {
        GetAddress(&_ip, &_port);
    }

    std::string Ip()
    {
        return _ip;
    }

    uint16_t Port()
    {
        return _port;
    }

    ~InetAddr()
    {}
private:
    struct sockaddr_in _addr;
    std::string _ip;
    uint16_t _port;
};

Main.cc

#include <iostream>
#include <memory>
#include "UdpServer.hpp"

void Usage(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << " local_prot\n" << std::endl;
}

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERROR);
    }
    EnableScreen();
    // std::string local_ip = argv[1];
    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<UdpServer> usvr(new UdpServer(port));
    // 初始化服务器
    usvr->InitServer();
    // 启动服务器
    usvr->Start();
    return 0;
}

Makefile

.PHONY:all
all:udpserver udpclient

udpserver:Main.cc
	g++ -o $@ $^ -std=c++11
udpclient:UdpClient.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f udpserver udpclient

结果:
服务端输入:./udpserver 端口号 即可
客户端输入:./udpclient 服务器的ip地址(若是本地互联,输入0即可) 端口号 即可
在这里插入图片描述
成功通信!


未完待续

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

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

相关文章

Ubuntu窗口如何自适应VM虚拟机

修改VM的显示设置 1&#xff1a;右击ubuntu计算机&#xff0c;点击设置按钮&#xff0c;进入到设置界面。 2&#xff1a;点击拉伸模式&#xff0c;选择自由拉伸。 以上操作结束之后打开ubuntu虚拟机。 ubuntu屏幕显示设置 进入到ubuntu系统之后&#xff0c;在桌面右击&…

网关与auth微服务缓存打通

文章目录 &#x1f31e; Sun Frame&#xff1a;SpringBoot 的轻量级开发框架&#xff08;个人开源项目推荐&#xff09;&#x1f31f; 亮点功能&#x1f4e6; spring cloud模块概览常用工具 &#x1f517; 更多信息1.缓存一致性问题1、更新了数据库&#xff0c;再更新缓存2、更…

大数据-68 Kafka 高级特性 物理存储 日志存储概述

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

文献翻译软件哪个好?6个工具让你轻松看懂外语文献

在医学研究领域&#xff0c;文献翻译软件扮演着至关重要的角色。 医学论文、研究报告和临床指南等&#xff0c;往往包含了大量的专业术语和复杂的句子结构&#xff0c;对于非母语者来说&#xff0c;理解这些内容无疑是一项挑战。 幸运的是&#xff0c;随着技术的发展&#xf…

巴黎奥运会8K转播科技为国产品牌自主研发设计

这个夏天&#xff0c;顶流是属于巴黎奥运会中国队的。 20枚金牌、15枚银牌、12枚铜牌......这个数字正随着赛事推进而不停在增加。赛场之上&#xff0c;中国健儿奋力拼搏、捷报频传&#xff0c;令人热血沸腾&#xff1b;赛场之外&#xff0c;另一支来自中国企业的“奥运选手”…

Linux OS:基于阻塞队列的生产者消费者模型

Linux OS&#xff1a;基于阻塞队列的生产者消费者模型 前言一、阻塞队列的大致框架二、生产者向阻塞队列中生产数据三、消费者获取阻塞队列中数据四、总体生产和消费思路及测试代码4.1 单生产单消费4.2 多生产多消费 五、所以代码 前言 阻塞队列是一种常用于实现生产者消费者模…

大模型微调技术PEFT

1. 横向对比 总体概览&#xff1a; 方法Transformer 中如何应用特点Prompt Tuning在输入到Transformer的input文本中添加Prompthard 模式prompt CO-STARP-tuning在输入到Transformer的embedding 层添加Promptsoft 模式 promptAdapter Tuning在 Transformer 中间层插入 Adapt…

MATLAB代码|蚁群算法|计算二元函数最大值

总述 蚁群方法求解二元函数的最大值。蚂蚁群先随机分布在定义域内&#xff0c;如下&#xff1a; 计算结束后&#xff0c;得到如下的分布&#xff0c;再计算分布均值&#xff0c;得到此时的自变量取值&#xff0c;因为是二元函数&#xff0c;所以有两个自变量&#xff0c;带入…

Qt 实战(9)窗体 | 9.1、QWidget

文章目录 一、QWidget1、定义与概念2、继承体系3、常用特性3.1、事件处理3.2、布局管理3.3、子控件与父子关系3.4、右键菜单 4、注意事项5、总结 前言&#xff1a; 在Qt这一强大的跨平台C图形用户界面应用程序开发框架中&#xff0c;QWidget扮演着至关重要的角色。作为所有用户…

【日记】为啥家族原发性高血压的人还喜欢喝酒啊……(442 字)

正文 今天跟人吵了一下午架&#xff0c;因为有一张报表换了新表&#xff0c;所有人都不知道怎么报。上级行一个想法&#xff0c;我一个想法。吵完都发现对方说得有道理&#xff0c;于是决定明天问省分行。难绷。草台班子。 鱼儿说他最近喜欢上了喝酒。我们劝他的同时&#xff0…

太阳光度计CE-318数据处理

太阳光度计CE-318数据处理 备注&#xff1a;处理公式 在我国近海&#xff0c;α的值在0到3之间&#xff0c;所以他们相对误差最大不超过25%&#xff0c;而通过查阅相关资料&#xff0c;北京地区α的值可以近似的取1.665。 大气是不断运动的&#xff0c;气溶胶在短时间内也可…

【网络安全】https协议的加密方案避免中间人攻击(MITM攻击)导致的数据泄露风险

目录 引言 概念准备 中间人 加密 数据摘要 && 数据指纹 数字签名 密钥加密 中间人攻击 CA证书 https加密的解决方案 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 引言 http在应用层协议中是明文传输协议&#xff0c;它是通信双方传输数据时的一种约定。【…

超声波便携式气象站:精准监测,随身携带

在追求高效与便捷的今天&#xff0c;超声波便携式气象站以其独特魅力脱颖而出&#xff0c;成为众多行业及户外爱好者的理想选择。超声波便携式气象站采用多采集装置一体式设计&#xff0c;不仅实现了体积的小型化与重量的轻量化&#xff0c;更便于用户随身携带&#xff0c;随时…

期权杠杆与期货杠杆的区别是什么?

期权与股指期货在杠杆性上展现出截然不同的特性&#xff0c;这些特性对投资者的策略选择具有深远影响。首先&#xff0c;股指期货采用保证金制度&#xff0c;其杠杆比例是恒定的&#xff0c;无论市场如何波动&#xff0c;投资者在月初设定的十倍杠杆到月尾仍保持不变。相比之下…

STM32常见的下载方式有三种

经过对比&#xff0c;推荐使用 SWD下载&#xff0c;只需要一个仿真器&#xff08;如jLINK、ST LINK、 CMSIS DAP 等&#xff09;&#xff0c;比较方便。 不推荐使用串口下载&#xff08;速度慢、无法仿真和调试&#xff09;和 JTAG 下载&#xff08;占用 IO 多&#xff09;。

Java语言程序设计基础篇_编程练习题*16.7 (设置时钟的时间)

*16.7 (设置时钟的时间) 编写一个程序&#xff0c;显示一个时钟&#xff0c;并通过在三个文本域中输入小时、分钟和秒 钟来设置时钟的时间&#xff0c;如图16-38b 所示。使用程序清单14-21的ClockPane改变时钟大小使其居于面板中央 习题思路 实例化一个ClockPane(在程序清单1…

【网络安全渗透测试零基础入门必知必会】之什么是文件包含漏洞分类(非常详细)零基础入门到精通,收藏这一篇就够了

一、前言 这是大白给粉丝盆友们整理的网络安全渗透测试入门阶段文件包含渗透与防御第1篇。 本文主要讲解什么是文件包含漏洞、本地文件包含漏洞 喜欢的朋友们&#xff0c;记得给大白点赞支持和收藏一下&#xff0c;关注我&#xff0c;学习黑客技术。 一、什么是文件包含漏洞…

the request was rejected because no multipart boundary was found

文章目录 1. 需求描述2. 报错信息3. 探索过程1. 使用postman 排除后端错误2. 搜索网上的解决方法3. 解决方法 1. 需求描述 想要在前端上传一个PDF 发票&#xff0c;经过后端解析PDF之后&#xff0c;将想要的值自动回填到对应的输入框中 2. 报错信息 org.apache.tomcat.util.…

VSCode远程调试Linux程序

VS 安装CodeRunner插件 菜单→添加配置→lunch.json中设置如下&#xff1a; program填入要调试的文件 {"version": "0.2.0","configurations": [{"name": "gdb renderPng","type": "cppdbg","re…

为什么某央企可以抓到红队攻击,而你不行?

国家HVV行动从2016年到2024年已经是第9年了&#xff0c;HVV行动目的就是保卫国家关键基础设施的网络安全行动&#xff0c;更是一场实战化的网络攻击与防御实战&#xff0c;这些年来红队攻击手段层出不穷&#xff0c;最为典型的就是 0/N Day、弱口令、社工钓鱼等&#xff0c;也极…