LinuxSocket套接字编程

news2025/4/18 11:25:32

1.介绍函数使用

1.创建套接字

int socket(int domain, int type, int protocol);

  • domain:指定协议族,如AF_INET(IPv4)或AF_INET6(IPv6)。

  • type:指定套接字类型,如SOCK_DGRAM(UDP)或SOCK_STREAM(TCP)。

  • protocol:通常设置为0,表示使用默认协议。

返回值:成功时返回一个套接字描述符,失败时返回-1。

2.绑定套接字到一个特定的IP地址和端口号

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  • sockfd:由socket()返回的套接字描述符。

  • addr:指向sockaddr结构的指针,包含要绑定的地址信息。

  • addrlenaddr的长度。

返回值:成功时返回0,失败时返回-1。

3.从UDP套接字接收数据,获取发送方地址

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

  • sockfd:套接字描述符。

  • buf:用于存储接收到的数据的缓冲区。

  • len:缓冲区的大小。

  • flags:通常设置为0。

  • src_addr:指向sockaddr结构的指针,用于存储发送方的地址信息。

  • addrlen:指向socklen_t的指针,用于指定src_addr的长度。

4.向指定的地址发送数据

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

  • sockfd:套接字描述符。

  • buf:要发送的数据的缓冲区。

  • len:要发送的数据的长度。

  • flags:通常设置为0。

  • dest_addr:指向sockaddr结构的指针,包含目标地址信息。

  • addrlendest_addr的长度。

5.设置套接字选项

 int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

  • sockfd:套接字描述符。

  • level:选项级别,如SOL_SOCKET

  • optname:选项名称,如SO_REUSEADDRSO_REUSEPORT

  • optval:指向选项值的指针。

  • optlen:选项值的长度。

返回值:成功时返回0,失败时返回-1。

6.将网络字节序的IP地址转换为点分十进制

char *inet_ntoa(struct in_addr in);

 hostshort:主机字节序的无符号短整数

返回值:网络字节序的无符号短整数。

7.将主机字节序的无符号整数转换为网络字节序

uint16_t htons(uint16_t hostshort);

  • hostshort:主机字节序的无符号短整数。

返回值:网络字节序的无符号短整数。

8.将网络字节序的无符号整数转换为主机字节序

uint16_t ntohs(uint16_t netshort);

 

  • netshort:网络字节序的无符号短整数。

netstate指令查看状态

etstat命令是一个功能强大的网络工具,用于查看和分析系统的网络状态。以下是netstat命令的一些常用选项和示例,帮助你查看网络连接状态:

常用选项

  1. -a:显示所有活动的网络连接,包括监听和非监听状态。

  2. -t:仅显示TCP协议相关的连接。

  3. -u:仅显示UDP协议相关的连接。

  4. -n:以数字形式显示地址和端口号,避免进行DNS解析。

  5. -l:仅显示监听状态的连接。

  6. -p:显示与网络连接相关联的进程ID和程序名称。

  7. -r:显示路由表信息。

  8. -i:显示网络接口统计信息。

类比理解:bind 就像「分配电话号码」​

假设你的程序是一个「电话」,网络通信需要两件事:

  1. 电话号码​(IP地址 + 端口):别人通过这个号码找到你。
  2. 电话机​(套接字):用来接听和拨打电话的工具。

bind 的作用
把你的「电话机」绑定到一个「电话号码」上。这样,别人(其他程序)才能通过这个号码联系到你。

2.实现socket通信

 1.服务端构造函数

要接收一个端口号,以及一个处理信息的函数,然后isrunning是一个判断执行状态,true表示运行中,就可以执行主体代码。

   UdpServer(uint16_t port,func_t func)
        :_sockfd(defaultfd),
        _port(port),
        _isrunning(false),
        _func(func)
        {}

 2.服务端初始化函数

创建端口号,设定为UDP模式(虽然第三参数为0,但是前两个参数就决定了模式是UDP的),setsockopt函数用来这个原因是,当服务器主动关闭连接的时候,套接字会进入TIME_WAIT状态,这个原因是,当服务器主动关闭连接的时候,套接字会进入TIME_WAIT状态,可以设置一下地址端口重用,不然就会打印出日志bind error信息。bero函数是吧给定的空间进行初始化为0,接着就是初始化协议族为四字节,然后端口号要转成网络字节序,然后设置套接字绑定的ip地址为INADDR_ANY,表示该套接字将监听所有可用的网络接口上的链接请求,INADDR_ANY是一个常量,,表示任何可用的网络接口,这样子设置是为了把所有的客户端信息接收了,客户端可能是各种网络的,所以设置这个可用全部接收,接着就是绑定套接字了,因为是在栈区上创建的,是在用户层面上,需要到内核态里,就需要bind函数来进行绑定,绑定后的套接字才是有用的。

 void Init()
    {
        _sockfd=socket(AF_INET,SOCK_DGRAM,0);
        if(_sockfd<0)
        {
            LOG(LogLevel::FATAL)<<"socket error";
            exit(1);
        }
        int opt = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        LOG(LogLevel::INFO)<<"socket success, sockfd:"<<_sockfd;

        struct sockaddr_in local;
        bzero(&local,sizeof(local));
        local.sin_family=AF_INET;
        local.sin_port=htons(_port);
        local.sin_addr.s_addr=INADDR_ANY;

        int n=bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
        if(n<0)
        {
            LOG(LogLevel::FATAL)<<"bind error";
            exit(2);
        }
        LOG(LogLevel::INFO)<<"bind success,sockfd:"<<_sockfd;
    }

3.服务端开始函数

 进入这个函数就可以把运行状态设置为true,然后进入循环,设置buffer用来接收消息,创建sockaddr_in类型peer表示客户端的消息(如端口号和IP地址),因为recvfrom函数需要这个参数,这个函数是在UDP套接字接收数据,peer就是一个输出型参数,执行完后,可以从peer里知道是谁发的,就像信封,上面有发件人的信息,需要注意的是参数需要强转成sockaddr类型的,接收成功就进入判断,从peer结构体里可以得到客户端信息(访问成员变量),然后用inet_ntoa将网络字节序转成点分十进制,调用回调函数,把buffer传过去,得到的信息处理再返回,最后执行sendto函数把信息进行发送回去,告诉客户端已经接收到了这个信息。

在使用recvfrom函数时,需要将sockaddr_in强制转换为sockaddr类型,原因如下:

兼容性

  • sockaddr是一个通用的套接字地址结构体,用于支持多种协议族。sockaddr_in是internet环境下套接字的地址形式,专门用于IPv4地址。

  • 套接字函数如bindconnectrecvfrom等设计为使用通用的sockaddr结构体作为参数,以便能够支持不同的地址族。通过将sockaddr_in强制转换为sockaddr,可以确保这些函数能够接受IPv4地址信息,同时保持接口的一致性。

灵活性

  • 强制转换允许程序员在需要时传递特定的地址结构体,同时保持函数接口的通用性。这意味着,如果将来需要支持其他类型的地址(如IPv6地址sockaddr_in6),只需更改地址结构体的类型和相应的转换,而无需修改函数调用的代码。

统一性

  • sockaddrsockaddr_in的长度都是16个字节,因此可以互相转换。这种统一的内存布局使得在不同地址类型之间进行转换成为可能,而不会导致数据损坏或访问错误的内存区域。

void Start()
    {
        _isrunning=true;
        while(_isrunning)
        {
            char buffer[1024];
            struct sockaddr_in peer;
            socklen_t len=sizeof(peer);
            
            std::cout << "服务器开始接收数据: " << std::endl;
            ssize_t s=recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
            if(s>0)
            {
                std::cout<<"进入判断"<<std::endl;
                int peer_port=ntohs(peer.sin_port);
                std::string peer_ip=inet_ntoa(peer.sin_addr);
                buffer[s]=0;
                std::cout << "buffer: " << buffer << std::endl;
                std::string result=_func(buffer);
                std::cout << "result: "  <<result << std::endl;
                sendto(_sockfd,result.c_str(),result.size(),0,(struct sockaddr*)&peer,len);

            }

        }

    }



std::string defaulthandler(const std::string& message)
{
    std::cout<<"打印"<<std::endl;
    std::string hello="hello, ";
    hello+=message;
    return hello;
}

4.UdpServer.hpp文件总代码

#pragma once

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

using namespace LogModule;

using func_t =std::function<std::string(const std::string&)>;

const int defaultfd=-1;


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

    void Init()
    {
        _sockfd=socket(AF_INET,SOCK_DGRAM,0);
        if(_sockfd<0)
        {
            LOG(LogLevel::FATAL)<<"socket error";
            exit(1);
        }
        int opt = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        LOG(LogLevel::INFO)<<"socket success, sockfd:"<<_sockfd;

        struct sockaddr_in local;
        bzero(&local,sizeof(local));
        local.sin_family=AF_INET;
        local.sin_port=htons(_port);
        local.sin_addr.s_addr=INADDR_ANY;

        int n=bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
        if(n<0)
        {
            LOG(LogLevel::FATAL)<<"bind error";
            exit(2);
        }
        LOG(LogLevel::INFO)<<"bind success,sockfd:"<<_sockfd;
    }

    void Start()
    {
        _isrunning=true;
        while(_isrunning)
        {
            char buffer[1024];
            struct sockaddr_in peer;
            socklen_t len=sizeof(peer);
            
            std::cout << "服务器开始接收数据: " << std::endl;
            ssize_t s=recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
            if(s>0)
            {
                std::cout<<"进入判断"<<std::endl;
                int peer_port=ntohs(peer.sin_port);
                std::string peer_ip=inet_ntoa(peer.sin_addr);
                buffer[s]=0;
                std::cout << "buffer: " << buffer << std::endl;
                std::string result=_func(buffer);
                std::cout << "result: "  <<result << std::endl;
                sendto(_sockfd,result.c_str(),result.size(),0,(struct sockaddr*)&peer,len);

            }

        }

    }
    ~UdpServer()
    {

    }
private:
    int _sockfd;
    uint16_t _port;
    bool _isrunning;
   
    func_t _func;

};

5.实例实现

main要写命令行参数,要接收端口号,不用IP地址是因为前面设置0地址为IP地址,就不需要接收这个参数了,创建一个port接收传入的端口号,这里的uint_16是一个重定义的变量,设置日志类型向控状态打印信息,std::unique_ptr是一个智能指针,用于管理动态分布的对象,它所管理的对象只能有一个所有者,std::make_unique是C++14引入的一个工厂函数,用于简化unique_ptr的创建过程,会调用new操作符来分配空间,返回一个unique_ptr对象。创建对象执行,这里不是.而是->是因为这是一个智能指针,所以需要箭头调用函数。

#include <iostream>
#include <memory>
#include "UdpServer.hpp"
#include <functional> 
std::string defaulthandler(const std::string& message)
{
    std::cout<<"打印"<<std::endl;
    std::string hello="hello, ";
    hello+=message;
    return hello;
}

int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        std::cerr<<"Usage:"<<argv[0]<<" port"<<std::endl;
        return 1;
    }

    uint16_t port=std::stoi(argv[1]);
    Enable_Console_Log_Strategy();

    std::unique_ptr<UdpServer> usvr=std::make_unique<UdpServer>(port, defaulthandler);
    usvr->Init();
    usvr->Start();
    return 0;
}

6.客户端实现

客服端的话需要传入地址和端口号,创建套接字,以及sockaddr_in类型变量,初始化创建的sockaddr_in结构体的值,进入循环创建string类型的input,要发送的信息,调用getline函数从cin输入流中获取信息放到input中,调用sendto函数向服务端发送消息,套接字,以及发送内容以及大小,还有向谁发送信息,以及信息大小这些参数,然后就是创建buffer来接收消息,向服务端发送消息,服务端反馈后,客服端接收,接收到就打印出接收的消息。

为什么需要传递sizeof(server)

  • 指定地址结构体的大小addrlen参数用于告诉sendto函数目标地址结构体的大小。这对于底层网络库正确解析地址信息至关重要。

  • 兼容性sockaddr是一个通用的地址结构体,而sockaddr_in是专门用于IPv4的地址结构体。通过传递sizeof(server),可以确保sendto函数知道它正在处理的是sockaddr_in结构体的大小。

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

int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        std::cerr<<"Usage:"<<argv[0]<<"server_ip server_port"<<std::endl;
        return 1;
    }

    std::string server_ip=argv[1];
    uint16_t server_port=std::stoi(argv[2]);

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

    struct sockaddr_in server;
    memset(&server,0,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_port=htons(server_port);
    server.sin_addr.s_addr=inet_addr(server_ip.c_str());
    while(true)
    {
        std::string input;
        std::cout<<"Please Enter#";
        std::getline(std::cin,input);

        std::cout << "客户端发送数据: " << input.c_str() << std::endl;
        ssize_t n=sendto(sockfd,input.c_str(),input.size(),0,(struct sockaddr*)&server,sizeof(server));
        (void)n;

        char buffer[1024];
        struct sockaddr_in peer;
        socklen_t len=sizeof(peer);
        ssize_t m=recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
        if(m>0)
        {
            buffer[m]=0;
            std::cout<<buffer<<std::endl;
        }
    }

    return 0;
}

Makefile文件

.PHONY:all
all:udpclient udpserver

udpclient:UdpClient.cc
	g++ -o $@ $^ -std=c++17 
udpserver:UdpServer.cc
	g++ -o $@ $^ -std=c++17

.PHONY:clean
clean:
	rm -f udpclient udpserver

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

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

相关文章

动态科技感html导航网站源码

源码介绍 动态科技感html导航网站源码&#xff0c;这个设计完美呈现了科幻电影中的未来科技界面效果&#xff0c;适合展示技术类项目或作为个人作品集的入口页面&#xff0c;自适应手机。 修改卡片中的链接指向你实际的HTML文件可以根据需要调整卡片内容、图标和颜色要添加更…

YOLOv11训练中精准率召回率与mAP@0.5的动态变化分析

目标检测模型的训练过程涉及多个关键性能指标和损失函数的变化&#xff0c;这些数据能够直观反映模型的收敛速度、最终精度以及改进效果。本文旨在通过绘制YOLOv11模型在训练过程中的精准率&#xff08;Precision&#xff09;、召回率&#xff08;Recall&#xff09;、mAP0.5 、…

Java常用工具算法-6--秘钥托管云服务AWS KMS

前言&#xff1a; 之前我们介绍了一些常用的加密算法&#xff08;如&#xff1a;对称加密AES&#xff0c;非对称加密RSA&#xff0c;ECC等&#xff09;&#xff0c;不论是哪一种都需要涉及到秘钥的管理。通常的做法都是把秘钥放到配置文件中进行配置&#xff0c;但是对于一些高…

11. Langchain输出解析(Output Parsers):从自由文本到结构化数据

引言&#xff1a;从"自由发挥"到"规整输出" 2025年某金融机构的合同分析系统升级前&#xff0c;AI生成的合同摘要需人工二次处理达47分钟/份。引入LangChain结构化解析后&#xff0c;处理时间缩短至3分钟。本文将详解如何用LangChain的解析器&#xff0c;…

python reportlab模块----操作PDF文件

reportlab模块----操作PDF文件 一. 安装模块二. reportlab相关介绍三. 扩展canvas类四. 水平写入完整代码五. 垂直写入完整代码 一. 安装模块 pip install reportlab二. reportlab相关介绍 # 1. letter 生成A4纸张尺寸 from reportlab.lib.pagesizes import letter print(let…

解锁基因密码之重测序(从测序到分析)

在生命科学的奇妙世界中&#xff0c;基因恰似一本记录着生命奥秘的“天书”&#xff0c;它承载着生物体生长、发育、衰老乃至疾病等一切生命现象的关键信息。而重测序技术&#xff0c;则是开启基因“天书”奥秘的一把神奇钥匙。 试想&#xff0c;你手中有一本经典书籍的通用版…

TQTT_KU5P开发板教程---QSFP25G光口回环测试

文档实现功能介绍 本文档通过一个叫做ibert的IP&#xff0c;实现25G光口回环测试例子。工程新建方法请参考文档《流水灯》&#xff0c;其中只是将文件名进行修改。 Vivado 起始页&#xff08;或 file-->Project-->New 创建新工程(Create New Project) 向导起始页面 点…

JVM虚拟机篇(七):JVM垃圾回收器全面解析与G1深度探秘及四种引用详解

JVM垃圾回收器全面解析与G1深度探秘及四种引用详解 JVM虚拟机&#xff08;七&#xff09;&#xff1a;JVM垃圾回收器全面解析与G1深度探秘及四种引用详解一、JVM有哪些垃圾回收器1. Serial回收器2. ParNew回收器3. Parallel Scavenge回收器4. Serial Old回收器5. Parallel Old回…

柑橘病虫害图像分类数据集OrangeFruitDaatset-8600

文章目录 1. 前言2. 数据类别介绍3. 数据集地址 1. 前言 柑橘&#xff0c;作为水果界的 “宠儿”&#xff0c;不仅以其酸甜可口的味道深受大众喜爱&#xff0c;更是在全球水果产业中占据着举足轻重的地位。无论是早餐中的一杯橙汁&#xff0c;还是下午茶里的柑橘甜点&#xff…

深度学习总结(4)

张量积 张量积&#xff08;tensor product&#xff09;或点积&#xff08;dot product&#xff09;是最常见且最有用的张量运算之一。注意&#xff0c;不要将其与逐元素乘积&#xff08;*运算符&#xff09;弄混。在NumPy中&#xff0c;使用np.dot函数来实现张量积&#xff0c…

利用CST Microwave Studio设计贴片天线

利用CST Microwave Studio设计贴片天线的步骤如下&#xff0c;分为几个关键阶段&#xff1a; --- ### **1. 初始设置** - **新建项目**&#xff1a;打开CST&#xff0c;创建新项目&#xff08;File > New&#xff09;&#xff0c;选择“Antenna (Planar)”或“Microwave &…

STM32之SG90舵机控制(附视频讲解)

目录 前言&#xff1a; 一、硬件准备与接线 1.1 硬件清单 1.2 接线 二、 SG90舵机简介 1.1 外观 1.2 基本参数 1.3 引脚说明 1.4 控制原理 1.5 特点 1.6 常见问题 三、 单片机简介 四、 程序设计 4.1 定时器配置 4.2 角度控制函数 4.3 主函数调用 五、 总结 …

(1)英特尔 RealSense T265(三)

文章目录 前言 4.4 地面测试 4.5 飞行测试 4.6 室内外实验 4.7 数据闪存记录 4.8 启动时自动运行 4.9 使用 OpticalFlow 进行 EKF3 光源转换 前言 Realsense T265 通过 librealsense 支持 Windows 和 Linux 系统。不同系统的安装过程差异很大&#xff0c;因此请参阅 gi…

多模态大语言模型arxiv论文略读(七)

MLLM-DataEngine: An Iterative Refinement Approach for MLLM ➡️ 论文标题&#xff1a;MLLM-DataEngine: An Iterative Refinement Approach for MLLM ➡️ 论文作者&#xff1a;Zhiyuan Zhao, Linke Ouyang, Bin Wang, Siyuan Huang, Pan Zhang, Xiaoyi Dong, Jiaqi Wang,…

SQL ③-基本语法

SQL基本语法 表操作 创建表 CREATE TABLE table_name (column1 datatype constraint,column2 datatype constraint,column3 datatype constraint,... );删除表 DROP [TEMPORARY] TABLE [IF EXISTS] table_name [, table_name...];TEMPORARY&#xff1a;表示临时表&#xff…

esp32cam -> 服务器 | 手机 -> 服务器 直接服务器传输图片

服务器先下载python &#xff1a; 一、Python环境搭建&#xff08;CentOS/Ubuntu通用&#xff09; 一条一条执行 安装基础依赖 # CentOS sudo yum install gcc openssl-devel bzip2-devel libffi-devel zlib-devel # Ubuntu sudo apt update && sudo apt install b…

豆浆机语音提示芯片方案:基于可远程在线更换语音的WT2003H-16S芯片

随着智能家居概念的普及&#xff0c;消费者对家电产品的智能化、便捷性提出了更高要求。豆浆机作为厨房常用电器&#xff0c;其操作便捷性和用户体验直接影响市场竞争力。传统豆浆机多依赖指示灯或简单蜂鸣器提示用户操作状态&#xff0c;信息传递单一且无法满足个性化需求。 在…

解密工业控制柜:认识关键硬件(PLC)

前言 作为一名视觉开发工程师&#xff0c;我们不仅要做到做好自己的工作&#xff0c;我们更需要在工业现场学习更多知识&#xff0c;最近网上流传很多&#xff0c;“教会徒弟&#xff0c;饿死师傅”&#xff1b;在自动化行业中&#xff0c;在项目下来很忙的时候&#xff0c;我们…

【嵌入式系统设计师】知识点:第11 章 嵌入式系统设计案例分析

提示:“软考通关秘籍” 专栏围绕软考展开,全面涵盖了如嵌入式系统设计师、数据库系统工程师、信息系统管理工程师等多个软考方向的知识点。从计算机体系结构、存储系统等基础知识,到程序语言概述、算法、数据库技术(包括关系数据库、非关系型数据库、SQL 语言、数据仓库等)…

【深度解析】SkyWalking 10.2.0版本安全优化与性能提升实战指南

前言 Apache SkyWalking 作为云原生可观测性领域的佼佼者&#xff0c;在微服务架构监控中扮演着至关重要的角色。然而&#xff0c;官方版本在安全性、镜像体积和功能扩展方面仍有优化空间。本文将分享一套完整的 SkyWalking 10.2.0 版本优化方案&#xff0c;从安全漏洞修复到镜…