网络学了点socket,写个聊天室,还得改进

news2024/11/27 2:33:47

目录

第一版:

common

服务端:

客户端

第一版问题总结:

第二版

服务端:

客户端:

改进:

Windows客户端

一些小问题

还可以进行的改进


这篇文章我就先不讲网络基础的东西了,我讲讲在我进行制作我这个拉跨聊天室中遇到的问题,并写了三版代码.

第一版:

common

#pragma once
#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "LockGuard.hpp"
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    USAGE_ERROR
};

void Setserver(struct sockaddr_in &server,std::string &serverip,uint16_t &serverport)
{
    bzero(&server, sizeof(server));
    server.sin_port = htons(serverport);
    server.sin_addr.s_addr = inet_addr(serverip.c_str());
    server.sin_family=AF_INET;
}


class InetAddr
{
    void GetAddress(uint16_t *Port, std::string *Ip)
    {
        *Port = ntohs(_Addr.sin_port);
        *Ip = inet_ntoa(_Addr.sin_addr);
    }

public:
    InetAddr(struct sockaddr_in &Addr)
        : _Addr(Addr)
    {
        GetAddress(&_Port, &_Ip);
    }
    uint16_t Port()
    {
        return _Port;
    }
    std::string Ip()
    {
        return _Ip;
    }

private:
    struct sockaddr_in _Addr;
    uint16_t _Port;
    std::string _Ip;
};

服务端:

#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 "Common.hpp"
const static int defaultfd = -1;

class Udpserver
{

public:
    Udpserver(uint16_t port)
        : _socketfd(defaultfd), _prot(port), _isrunning(false)
    {
    }
    void InitServer()
    {
        _socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_socketfd < 0)
        {
            LOG(WARNING, "%s", "sockfd创建失败");
        }
        LOG(INFO, "%s", "sock创建成功");
        // 填充sockaddr_in结构
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;    // 设置家族协议
        local.sin_port = htons(_prot); // 设置端口号 换成网络序列
        // port要经过网络传输给对面,先到网络,_port:主机序列-> 主机序列,转成网络序列
        // a. 字符串风格的点分十进制的IP地址转成 4 字节IP
        // b. 主机序列,转成网络序列
        // in_addr_t inet_addr(const char *cp) -> 同时完成 a & b
        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // "192.168.3.1" -> 字符串风格的点分十进制的IP地址 -> 4字节IP
        local.sin_addr.s_addr = INADDR_ANY; // 随机ip地址 一般不能绑定确定ip地址

        // 开始绑定
        int n = bind(_socketfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "%s", "bind error");
            exit(BIND_ERROR);
        }
        LOG(INFO, "%s", "bind success");
    }

    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            char Inbuffer[1024];
            struct sockaddr_in peer;
            socklen_t socklen = sizeof(peer); // 初始化为sock
            // 让server收取数据 获取客户端socket
            ssize_t n = recvfrom(_socketfd, Inbuffer, sizeof(Inbuffer), 0, (struct sockaddr *)&peer,&socklen); // 接受
            if (n > 0)
            {
                std::cout <<"client say:";
                std::cout << Inbuffer << std::endl; // 成功就打印出来

                // sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);
                LOG(DEBUG, "建立 客户IP:%s 连接端口:%s", netAddr.Ip().c_str(), netAddr.Port());
                // sendto(_socketfd,buffer,sizeof(buffer),0,(struct sockaddr*)&sockaddr_in,socklen);//发送
            }
            //
            ///发/
            //
            InetAddr netAddr(peer);
            std::string message;
            std::cout << "server:";
            getline(std::cin, message);
            sendto(_socketfd, message.c_str(), message.size(), 0, (struct sockaddr *)&peer, socklen); // 发送
        }
        _isrunning = false;
    }

private:
    int _socketfd;
    uint16_t _prot;
    bool _isrunning;
};

客户端

#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 "Common.hpp"

static bool isrunning = false;
static std::string Clientname;
void useagge(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << " serverip serverport\n"
              << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        useagge(argv[0]);
        exit(USAGE_ERROR);
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::atoi(argv[2]);
    std::cout << "你是?" << std::endl;
    std::cin >> Clientname;
    // 创建socket;
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        LOG(WARNING, "%s", "Sock错误创建失败");
    }
    LOG(INFO, "%s", "sock创建成功");
    isrunning = true;
    struct sockaddr_in server;
    Setserver(server, serverip, serverport);
    struct sockaddr_in local;
    bzero(&local, sizeof(local));
    std::cout << "可以进行通信了!" << std::endl;
    
    while (isrunning)
    {
      
        std::cout << "server:";
        std::string message;
        getline(std::cin, message);
        sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, socklen); // 发送
        
        struct sockaddr_in peer;
        socklen_t socklen = sizeof(peer); // 初始化为sock
        char buffer[1024];
        ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &socklen);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "server: " << buffer << std::endl;
        }
    }
    return 0;
}

第一版问题总结:

我一开始是想着这个流程,因为一开始服务端只是接受客户端,服务端不会发消息给客户端,所以我想在原基础上,让两端都可以接受和发送,当时就有想可以多线程实行接受和发的任务,但是觉得上线程太麻烦就决定是服务端发->客户端收->客户端发->服务器收,这一条链路实行,但是问题是,我把收发是写在循环里,而 recvfrom是非阻塞等待的,所以双方实际上永远等不到对方信息

所以实际上仍然是要让多线程实行接受和发的任务

第二版

服务端:

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <thread>
#include <mutex>
#include "log.hpp"
#include "Common.hpp"
const static int defaultfd = -1;
 public:
    Udpserver(uint16_t port)
        : _socketfd(defaultfd), _prot(port), _isrunning(false)
    {
        _socklen = sizeof(_peer);
        InitServer();
    }
    void InitServer()
    {
        _socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_socketfd < 0)
        {
            LOG(WARNING, "%s", "sockfd创建失败");
        }
        LOG(INFO, "%s", "sock创建成功");
        // 填充sockaddr_in结构
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;    // 设置家族协议
        local.sin_port = htons(_prot); // 设置端口号 换成网络序列
        // port要经过网络传输给对面,先到网络,_port:主机序列-> 主机序列,转成网络序列
        // a. 字符串风格的点分十进制的IP地址转成 4 字节IP
        // b. 主机序列,转成网络序列
        // in_addr_t inet_addr(const char *cp) -> 同时完成 a & b
        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // "192.168.3.1" -> 字符串风格的点分十进制的IP地址 -> 4字节IP
        local.sin_addr.s_addr = INADDR_ANY; // 随机ip地址 一般不能绑定确定ip地址

        // 开始绑定
        int n = bind(_socketfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "%s", "bind error");
            exit(BIND_ERROR);
        }
        LOG(INFO, "%s", "bind success");
        _isrunning = true;
    }

    void Start()
    void receive()
    {
        _isrunning = true;
        while (_isrunning)
        {
            
            char Inbuffer[1024] = {0}; // 初始化缓冲区
            struct sockaddr_in tempPeer;
            socklen_t tempSocklen = sizeof(tempPeer);
            ssize_t n = recvfrom(_socketfd, Inbuffer, sizeof(Inbuffer) - 1, 0, (struct sockaddr *)&tempPeer, &tempSocklen);
            if (n > 0)
            {
                std::cout <<"client say:";
                std::cout << Inbuffer << std::endl; // 成功就打印出来

                // sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);
                LOG(DEBUG, "建立 客户IP:%s 连接端口:%s", netAddr.Ip().c_str(), netAddr.Port());
                // sendto(_socketfd,buffer,sizeof(buffer),0,(struct sockaddr*)&sockaddr_in,socklen);//发送
                Inbuffer[n] = 0;
                std::lock_guard<std::mutex> lock(_peerMutex);
                _peer = tempPeer;
                _socklen = tempSocklen;
                std::cout << "client says:" << Inbuffer << std::endl;
            }
            //
            ///发/
            //
            InetAddr netAddr(peer);
            else
            {
                perror("recvfrom error");
            }
        }
    }
    void sent()
    {
        while (_isrunning)
        {
            std::string message;
            std::cout << "server: ";
            std::cin >> message;
            std::lock_guard<std::mutex> lock(_peerMutex);
            ssize_t sent = sendto(_socketfd, message.c_str(), message.size(), 0, (struct sockaddr *)&_peer, _socklen);
            if (sent == -1)
            {
                perror("sendto error");
            }
        }
        _isrunning = false;
    }
    void Start()
    {
        std::thread recvThread(&Udpserver::receive, this);
        std::thread sendThread(&Udpserver::sent, this);

        recvThread.detach();
        sendThread.detach();
        while (_isrunning)
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
        close(_socketfd);
    }

private:
    int _socketfd;
    uint16_t _prot;
    bool _isrunning;
    //
    struct sockaddr_in _peer;
    socklen_t _socklen;

    std::mutex _peerMutex;
};

客户端:

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <memory>
#include <thread>
#include "log.hpp"
#include "Common.hpp"

static bool isrunning = false;
static std::string Clientname;

class Client
{
public:
    Client(const std::string &server_ip, uint16_t server_port)
    {
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd == -1)
        {
            perror("socket creation failed");
            exit(EXIT_FAILURE);
        }

        memset(&_serverAddr, 0, sizeof(_serverAddr));
        _serverAddr.sin_family = AF_INET;
        _serverAddr.sin_port = htons(server_port);
        if (inet_pton(AF_INET, server_ip.c_str(), &_serverAddr.sin_addr) <= 0)
        {
            perror("inet_pton failed");
            close(_sockfd);
            exit(EXIT_FAILURE);
        }

        _isrunning = true;
    }
    void start()
    {
        std::cout << "你是? ";
        std::cin >> _clientName;
        std::cout << "可以进行通信了!" << std::endl;

        std::thread recvThread(&Client::receive, this);
        std::thread sendThread(&Client::send, this);

        recvThread.detach();
        sendThread.detach();

        while (_isrunning)
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }

        close(_sockfd);
    }

private:
    void receive()
    {
        while (_isrunning)
        {
            char buffer[1024] = {0};
            struct sockaddr_in peer;
            socklen_t socklen = sizeof(peer);

            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &socklen);
            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << "server: " << buffer << std::endl;
            }
            else if (n == -1)
            {
                perror("recvfrom error");
            }
        }
    }
    void send()
    {
        while (_isrunning)
        {
            std::cout << _clientName << ": ";
            std::string message;
            std::cin >> message;

            ssize_t sent = sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&_serverAddr, sizeof(_serverAddr));
            if (sent == -1)
            {
                perror("sendto error");
            }
        }
    }

private:
    int _sockfd;
    struct sockaddr_in _serverAddr;
    bool _isrunning;
    std::string _clientName;
};

改进:

这一版上,我添加了多线程和锁,能让客户端服务端进行并发的运行,并收发消息

Windows客户端

由于我想让Windows朋友也能与我建立通信,所以我在客户端上进行了修改成Windows版本

 #define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <thread>
#include <memory>

#pragma comment(lib, "Ws2_32.lib")

static bool isrunning = false;
static std::string Clientname;

class Client
{
public:
    Client(const std::string& server_ip, uint16_t server_port)
    {
        WSADATA wsaData;
        int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
        if (result != 0)
        {
            std::cerr << "WSAStartup failed: " << result << std::endl;
            exit(EXIT_FAILURE);
        }

        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd == INVALID_SOCKET)
        {
            std::cerr << "socket creation failed: " << WSAGetLastError() << std::endl;
            WSACleanup();
            exit(EXIT_FAILURE);
        }

        memset(&_serverAddr, 0, sizeof(_serverAddr));
        _serverAddr.sin_family = AF_INET;
        _serverAddr.sin_port = htons(server_port);
        if (inet_pton(AF_INET, server_ip.c_str(), &_serverAddr.sin_addr) <= 0)
        {
            std::cerr << "inet_pton failed: " << WSAGetLastError() << std::endl;
            closesocket(_sockfd);
            WSACleanup();
            exit(EXIT_FAILURE);
        }

        _isrunning = true;
    }

    void start()
    {
        std::cout << "你是? ";
        std::cin >> _clientName;
        std::cout << "可以进行通信了!" << std::endl;

        std::thread recvThread(&Client::receive, this);
        std::thread sendThread(&Client::send, this);

        recvThread.detach();
        sendThread.detach();

        while (_isrunning)
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }

        closesocket(_sockfd);
        WSACleanup();
    }

private:
    void receive()
    {
        while (_isrunning)
        {
            char buffer[1024] = { 0 };
            struct sockaddr_in peer;
            int peerlen = sizeof(peer);

            int n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &peerlen);
            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << "server: " << buffer << std::endl;
            }
            else if (n == SOCKET_ERROR)
            {
                std::cerr << "recvfrom error: " << WSAGetLastError() << std::endl;
            }
        }
    }

    void send()
    {
        while (_isrunning)
        {
            std::cout << _clientName << ": ";
            std::string message;
            std::cin >> message;

            int sent = sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&_serverAddr, sizeof(_serverAddr));
            if (sent == SOCKET_ERROR)
            {
                std::cerr << "sendto error: " << WSAGetLastError() << std::endl;
            }
        }
    }

private:
    SOCKET _sockfd;
    struct sockaddr_in _serverAddr;
    bool _isrunning;
    std::string _clientName;
};

void useagge(const std::string& proc)
{
    std::cout << "Usage:\n\t" << proc << " serverip serverport\n"
        << std::endl;
}

int main()
{
    std::string serverip;
    std::string portStr;
    std::cout << "输入服务器ip: ";
    std::cin >> serverip;
    std::cout << "输入服务器端口号: ";
    std::cin >> portStr;

    uint16_t serverport;
    try
    {
        serverport = static_cast<uint16_t>(std::stoi(portStr));
    }
    catch (const std::exception& e)
    {
        std::cerr << "无效的端口号: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }

    std::unique_ptr<Client> csvr = std::make_unique<Client>(serverip, serverport); // C++14
    csvr->start();
    return 0;
}

一些小问题

由于我用的云服务器,大部分端口号是默认禁用的,所以端口号要自己进行开放,我用的阿里云,开放端口的地方在这里

还可以进行的改进

1.目前服务端和客户端仍然是1对1的关系,如果有第二个用户上线,就会挤占第一个用户,所以这里可以用一个vector来对用户的ip进行管理,来统一收所有用户消息

2.目前还没有将用户的名字传输给服务端,后续可以加上

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

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

相关文章

使用 Keras 的 Stable Diffusion 实现高性能文生图

前言 在本文中&#xff0c;我们将使用基于 KerasCV 实现的 [Stable Diffusion] 模型进行图像生成&#xff0c;这是由 stable.ai 开发的文本生成图像的多模态模型。 Stable Diffusion 是一种功能强大的开源的文本到图像生成模型。虽然市场上存在多种开源实现可以让用户根据文本…

Linux磁盘分区使用情况查询

一、磁盘分区使用情况查询 1. 查询磁盘整体使用情况使用 df -h进行查询 如图我们可以了解到磁盘的一些大致的使用情况&#xff0c;注意当已用部分有超过80%使用的分区就意味着你需要进行磁盘的清理了。 2.查询指定的磁盘使用情况 使用指令 du -h 当不指定目录时&#xff0c;默…

Vivado时序报告之Datasheet详解

目录 一、前言 二、Datasheet配置选项说明 2.1 Options 2.2 Groups 2.3 Timer Settings 2.4 Common Options 三、Datasheet报告 3.1 General Information 3.2 Input Ports Setup/Hold 3.3 Output Ports Clock-to-out 3.4 Setup between Clocks 3.5 Combinational…

物联网实战--平台篇之(十四)物模型(用户端)

目录 一、底层数据解析 二、物模型后端 三、物模型前端 四、数据下行 本项目的交流QQ群:701889554 物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html 物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html 物联网…

基于Python的AI动物识别技术研究

基于Python的AI动物识别技术研究 开发语言:Python 数据库&#xff1a;MySQL所用到的知识&#xff1a;Django框架工具&#xff1a;pycharm、Navicat、Maven 系统功能实现 系统的登录模块设计 本次设计的AI动物识别系统为了保证用户的数据安全&#xff0c;设计了登录的模块&…

明天15点!如何打好重保预防针:迎战HVV经验分享

在当今数字化时代&#xff0c;网络攻击日益猖獗&#xff0c;各行各业面临的网络安全威胁不断升级。从钓鱼邮件到复杂的APT攻击&#xff0c;网络犯罪分子的手法层出不穷&#xff0c;给各行各业的信息安全带来了前所未有的挑战。 在这样的背景下&#xff0c;"HVV行动"应…

免费,C++蓝桥杯等级考试真题--第7级(含答案解析和代码)

C蓝桥杯等级考试真题--第7级 答案&#xff1a;D 解析&#xff1a;步骤如下&#xff1a; 首先&#xff0c;--a 操作会使 a 的值减1&#xff0c;因此 a 变为 3。判断 a > b 即 3 > 3&#xff0c;此时表达式为假&#xff0c;因为 --a 后 a 并不大于 b。因此&#xff0c;程…

如何远程连接Linux服务器?

远程连接Linux服务器是通过网络连接到位于远程位置的Linux服务器&#xff0c;以进行服务器管理和操作。远程连接使得系统管理员可以方便地远程访问服务器&#xff0c;进行配置、维护和故障排除等操作&#xff0c;而不必亲自在服务器前工作。以下是一些常用的远程连接方法&#…

使用小黄鸟(HttpCanary)、VMOS Pro虚拟机对手机APP进行抓包(附带软件)

老规矩先看&#xff0c;效果图&#xff1a; 文章很详细&#xff0c;希望可以耐心看完&#xff0c;保证可以学会抓包&#xff0c;不再走冤枉路&#xff0c;小编在之前看过太多类似文章&#xff0c;折腾了太久才搞懂的&#xff0c;写这篇文章就是不想希望你们像小编一样再花时间…

qmt量化交易策略小白学习笔记第15期【qmt编程之获取龙虎榜数据】

qmt编程之获取龙虎榜数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 获取龙虎榜数据 获取指定日期区间内的龙虎榜数据 内置python C.get_longhubang(stock_list, startTime, endTime)参…

十八、【源码】二级缓存

源码地址&#xff1a;https://github.com/mybatis/mybatis-3/ 仓库地址&#xff1a;https://gitcode.net/qq_42665745/mybatis/-/tree/18-second-levelcache 二级缓存 二级缓存&#xff0c;namespace级别&#xff0c;默认关闭&#xff0c;需要手动开启&#xff0c;在xml加入…

论文阅读《SELECTIVE DOMAIN-INVARIANT FEATURE FOR GENERALIZABLE DEEPFAKEDETECTION》

作者&#xff1a;Yingxin Lai、 Guoqing Yang1、Yifan He2、Zhiming Luo、Shaozi Li 期刊&#xff1a;ICASSP-2024 目的&#xff1a;解决泛化性的问题&#xff0c;提出了3个模块 论文整体的架构图&#xff1a;&#xff08;挑选域特征不变&#xff0c;减少对图像内容或者风格…

我的编程语言学习记录:一段不断探索的旅程

目录 我的编程语言学习记录&#xff1a;一段不断探索的旅程 1.引言 2.我的编程之旅开始 第一站&#xff1a;Python — 简洁之美 第二站&#xff1a;JavaScript — 网页的魔法 第三站&#xff1a;Java — 企业级的力量 3.学习过程中的挑战与克服 1.理解概念 3.记忆语法…

德克萨斯大学奥斯汀分校自然语言处理硕士课程汉化版(第七周) - 结构化预测

结构化预测 0. 写在大模型前面的话1. 词法分析 1.1. 分词1.2. 词性标注 2.2. 句法分析 2.3. 成分句法分析2.3. 依存句法分析 3. 序列标注 3.1. 使用分类器进行标注 4. 语义分析 0. 写在大模型前面的话 在介绍大语言模型之前&#xff0c;先把自然语言处理中遗漏的结构化预测补…

Word Split Line

Word Split Line 分割线 https://download.csdn.net/download/spencer_tseng/89413772

Verilog实战学习到RiscV - 4 : ICEStick 评估板计数器

这篇是关于always 时序逻辑的。直接上代码。 引脚配置文件 set_io leds[0] 99 set_io leds[1] 98 set_io leds[2] 97 set_io leds[3] 96set_io -pullup yes pmod[0] 78 set_io -pullup yes pmod[1] 79参看icestick的原理图 这里在pmod上使用了内部的上拉电阻。…

两轮自平衡小车资料(L298N 模块原理图及使用说明+c源码)

本文详细介绍了基于STM32微控制器的两轮自平衡小车的设计与实现过程。内容包括小车的硬件选型、电路设计、软件编程以及PID控制算法的应用。通过陀螺仪和加速度计获取小车的姿态信息&#xff0c;利用PID控制算法调整电机输出&#xff0c;实现小车的自主平衡。此外&#xff0c;还…

mac Network: use --host to expose

本地启动无法访问&#xff0c;这个不是权限问题是mac 主机端口安全策略&#xff0c;现在我们只需要开启端口自动检测就可以 npm run dev --host 网络&#xff1a;未暴露 方案一 1、执行 npm run dev -- --host 方案二 1、请在 vite.config.js server: {host: true } 1…

c++之旅第十弹——IO流

大家好啊&#xff0c;这里是c之旅第十弹&#xff0c;跟随我的步伐来开始这一篇的学习吧&#xff01; 如果有知识性错误&#xff0c;欢迎各位指正&#xff01;&#xff01;一起加油&#xff01;&#xff01; 创作不易&#xff0c;希望大家多多支持哦&#xff01; 一.流的概念&…

知乎网站只让知乎用户看文章,普通人看不了

知乎默认不显示全部文章&#xff0c;需要点击展开阅读全文 然而点击后却要登录&#xff0c;这意味着普通人看不了博主写的文章&#xff0c;只有成为知乎用户才有权力查看文章。我想这不是知乎创作者希望的情况&#xff0c;他们写文章肯定是希望所有人都能看到。 这个网站篡改…