Linux网络-Socket套接字_Windows与Linux端双平台基于Udp传输协议进行多线程跨平台的服务器与客户端网络通信的简易聊天室实现

news2024/11/19 18:33:18

文章目录

  • 一、Socket套接字
  • 二、socket 常见API
    • 1. int socket(int domain, int type, int protocol);
    • 2. int bind(int socket, const struct sockaddr *address, socklen_t address_len);
      • struct sockaddr
    • 3. ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address,socklen_t *restrict address_len);
    • 4. ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr,socklen_t dest_len);
    • 5. uint16_t htons(uint16_t hostshort);
      • 网络字节序
    • 6. in_addr_t inet_addr(const char *cp);
  • 三、Server端
    • 1.初始化Socket套接字
    • 2.发送和接收
    • Server端全代码
      • const std::string default_ip = "0.0.0.0";
  • Client端
  • Windows Client端
  • 简易聊天室运行图


一、Socket套接字

在互联网中,我们在网络层采用 IP + 端口号 的方式即可在全网中找到唯一进程,IP标识唯一主机,端口号标识该主机端口号绑定的进程。

这就是Socket套接字。


二、socket 常见API

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

创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)

参数 int domin :指定要在其中创建套接字的通信域。这里我们使用AF_INET采用IPV4通信域。

参数 int type : 指定要创建的套接字类型。 Udp协议是采用面向数据流,所以是用SOCK_DGRAM。

参数 int protocol :指定与套接字一起使用的特定协议。

返回值: 如果成功则返回创建的socket 文件描述符, 失败则返回-1.

代码示例

		int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd == -1)
        {
            exit(1);
        }

2. int bind(int socket, const struct sockaddr *address, socklen_t address_len);

绑定端口号 (TCP/UDP, 服务器)
参数 int socket 就是使用socket接口函数所创建的那个socket文件描述符。

struct sockaddr

在这里插入图片描述
由于底层有不同的网络协议,所以它们的地址格式并不相同,所以通常使用struct sockaddr* 作为参数,然后根据前16位地址类型来确定协议内容。

参数 socklen_t address_len, 结构体sockaddr的长度。
typedef unsigned int socklen_t

返回值: 如果绑定成功则返回0, 失败则返回-1.

代码示例

		int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd == -1)
        {
            exit(1);
        }
        struct sockaddr_in Server;
        bzero(&Server, 0);
        Server.sin_family = AF_INET;
        Server.sin_addr.s_addr = inet_addr(_ip.c_str()); //?
        Server.sin_port = htons(_port); //?

        int n = bind( socket_fd, (const struct sockaddr *)&Server, sizeof(Server));
        if (n != 0)
        {
            exit(2);
        }

3. ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address,socklen_t *restrict address_len);

参数 int socket 就是使用socket接口函数所创建的那个socket文件描述符。
参数 void *restrict buffer为缓冲区,可以用来存放接收的字节数据。
参数 size_t length 为读取的字节长度。
参数 struct sockaddr *restrict address,输出型参数,用于获取发送方的socketaddr。
参数 socklen_t *restrict address_len,输出型参数,用于获取发送方的sockaddr的长度。

示例代码

		char inbuffer[1024];
        while (true)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client); // unsigned int
            bzero(&client, 0);
            // 服务器接受数据
            ssize_t n = recvfrom(_socket_fd, inbuffer, sizeof inbuffer - 1, 0, (sockaddr *)&client, &len);
            if (n > 0)
            {
                std::string client_ip = inet_ntoa(client.sin_addr);
                uint16_t client_port = ntohs(client.sin_port);
                userCheck(client_ip, client);
                inbuffer[n] = '\0';
                if(strcmp(inbuffer,"login\0") == 0) continue;

                std::string info = func(inbuffer, client_ip, client_port);
                senMessage(info);

            }
        }

4. ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr,socklen_t dest_len);

参数与 recvfrom类似。不同的是,这里的const struct sockaddr *dest_addr和socklen_t dest_len 变成了输入型参数。 因为你要发送消息,就需要知道对方的socket信息。

5. uint16_t htons(uint16_t hostshort);

这个函数的作用是什么呢? 这就涉及到网络字节序

网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

规定网络数据在内存中必须采用大端存储方式!!

如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可

所以这个函数的作用就是将hostshort转换为网络字节序。

6. in_addr_t inet_addr(const char *cp);

由于我们习惯将IP地址 写成这样的格式xxx.xxx.xxx.xxx(例如192.168.0.1),而在网络层面,我们肯定是采用四字节的int来存储IP,所以有这么一个函数将一个xxx.xxx.xxx.xxx的格式的IP转换为四字节int。

三、Server端

1.初始化Socket套接字

代码如下(示例):

		// 申请套接字
        int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd == -1)
        {
            logMessage(FATAL, "Socket Applied Fault");
            exit(SOCKET_DENIED);
        }
        _socket_fd = socket_fd;
        logMessage(DEBUG, "Socket Applied Success");
        // bind套接字
        struct sockaddr_in Server;
        bzero(&Server, 0);
        Server.sin_family = AF_INET;
        Server.sin_addr.s_addr = inet_addr(_ip.c_str()); // inet_addr可以将ip形式x.x.x.x的字符串转化为大段存储的in_addr_t类型(typedef uint32_t in_addr_t;)
        Server.sin_port = htons(_port);

        int n = bind(_socket_fd, (const struct sockaddr *)&Server, sizeof(Server));
        if (n != 0)
        {
            logMessage(FATAL, "Bind Fault");
            exit(BIND_FAULT);
        }

2.发送和接收

代码如下(示例):


    void userCheck(std::string &client_ip, struct sockaddr_in &user)
    {
        auto it = _user_map.find(client_ip);
        // std::cout << "开始检测" << std::endl; 
        if (it == _user_map.end())
        {
            //没找到
            std::cout << "新用户[" << client_ip << "]上线..." << std::endl;
        }
        _user_map[client_ip] = user;
    }

    void senMessage(const std::string& info)
    {
        for(auto& user:_user_map )
        {
            sendto(_socket_fd, info.c_str(), info.size(), 0, (const struct sockaddr *)&(user.second), sizeof(user.second));
        }
    }
    
	std::string func(const char *res, const std::string& client_ip, const uint16_t client_port)
	{

    	char buffer [1024];
    	snprintf(buffer, sizeof buffer,"[%s:%d]# %s",client_ip.c_str(),client_port,res);
    	std::string mes = buffer;
    	std::cout << mes << std::endl; 
   	 	return mes;
	}
    
    void run(func_t func)
    {
        char inbuffer[1024];
        while (true)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client); // unsigned int
            bzero(&client, 0);
            // 服务器接受数据
            ssize_t n = recvfrom(_socket_fd, inbuffer, sizeof inbuffer - 1, 0, (sockaddr *)&client, &len);
            if (n > 0)
            {
                std::string client_ip = inet_ntoa(client.sin_addr);
                uint16_t client_port = ntohs(client.sin_port);

                userCheck(client_ip, client);
                inbuffer[n] = '\0';
                if(strcmp(inbuffer,"login\0") == 0) continue;

                std::string info = func(inbuffer, client_ip, client_port);
                senMessage(info);

            }
        }
    }

Server端全代码

                  //UdpServer.hpp  
#pragma once
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <cstdlib>
#include <unistd.h>
#include "log.hpp"
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
const std::string default_ip = "0.0.0.0"; //?

typedef std::function<std::string(const char*, const std::string &, const uint16_t)> func_t;

enum
{
    SOCKET_DENIED = 1,
    BIND_FAULT
};

class UdpServer
{
public:
    UdpServer(const std::string &ip = default_ip, const uint16_t port = 8080)
        : _socket_fd(0), _ip(ip), _port(port)
    {
    }

    void init()
    {
        // 申请套接字
        int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd == -1)
        {
            logMessage(FATAL, "Socket Applied Fault");
            exit(SOCKET_DENIED);
        }
        _socket_fd = socket_fd;
        logMessage(DEBUG, "Socket Applied Success");
        // bind套接字
        struct sockaddr_in Server;
        bzero(&Server, 0);
        Server.sin_family = AF_INET;
        Server.sin_addr.s_addr = inet_addr(_ip.c_str()); // inet_addr可以将ip形式x.x.x.x的字符串转化为大段存储的in_addr_t类型(typedef uint32_t in_addr_t;)
        Server.sin_port = htons(_port);

        int n = bind(_socket_fd, (const struct sockaddr *)&Server, sizeof(Server));
        if (n != 0)
        {
            logMessage(FATAL, "Bind Fault");
            exit(BIND_FAULT);
        }
    }

    void userCheck(std::string &client_ip, struct sockaddr_in &user)
    {
        auto it = _user_map.find(client_ip);
        // std::cout << "开始检测" << std::endl; 
        // std::cout << (*it).first << std::endl; 
        if (it == _user_map.end())
        {
            //没找到
            std::cout << "新用户[" << client_ip << "]上线..." << std::endl;
        }
        _user_map[client_ip] = user;
    }

    void senMessage(const std::string& info)
    {
        for(auto& user:_user_map )
        {
            sendto(_socket_fd, info.c_str(), info.size(), 0, (const struct sockaddr *)&(user.second), sizeof(user.second));
            // std::cout << "已发送数据给" << user.first << std::endl; 
        }
    }

    void run(func_t func)
    {
        char inbuffer[1024];
        while (true)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client); // unsigned int
            bzero(&client, 0);
            // 服务器接受数据
            ssize_t n = recvfrom(_socket_fd, inbuffer, sizeof inbuffer - 1, 0, (sockaddr *)&client, &len);
            if (n > 0)
            {
                std::string client_ip = inet_ntoa(client.sin_addr);
                uint16_t client_port = ntohs(client.sin_port);
                // std::cout <<  client_ip << " : " << client_port <<std::endl;
                userCheck(client_ip, client);
                inbuffer[n] = '\0';
                if(strcmp(inbuffer,"login\0") == 0) continue;
                // std::cout << "Server get message# " << inbuffer << std::endl;
                std::string info = func(inbuffer, client_ip, client_port);
                senMessage(info);
                // sendto(_socket_fd, info.c_str(), info.size(), 0, (const struct sockaddr *)&client, len);
                // sendto(_socket_fd, inbuffer, sizeof inbuffer, 0, (const struct sockaddr *)&client, len);
            }
        }
    }

    ~UdpServer()
    {
        if (_socket_fd == 0)
        {
            close(_socket_fd);
        }
    }

private:
    int _socket_fd;
    std::string _ip;
    uint16_t _port;
    std::unordered_map<std::string, struct sockaddr_in> _user_map;
};
                            //main.cc
#include "Udpserver.hpp"

std::string func(const char *res, const std::string& client_ip, const uint16_t client_port)
{
    //std::cout << "[" << client_ip << " : " << client_port << "] # " << res << std::endl;
    char buffer [1024];
    snprintf(buffer, sizeof buffer,"[%s:%d]# %s",client_ip.c_str(),client_port,res);
    std::string mes = buffer;
    std::cout << mes << std::endl; 
    return mes;
}

int main()
{
    UdpServer us;
    us.init();
    us.run(func);
    return 0;
}

const std::string default_ip = “0.0.0.0”;

这样的ip格式是什么意思呢? 绑定这样的默认IP,就可以收到任意IP发来的消息

使用 netstat -ua 可以查看主机所有udp协议套接字。

在这里插入图片描述

Client端

#define terminal "/dev/pts" 
void Usage(std::string proc)
{
    std::cout << "\n\rUsage: " << proc << " serverip serverport\n"
              << std::endl;
}

enum
{
    SOCKET_DENIED = 1,
};

struct Thread_Data
{
    int socket_fd;
    struct sockaddr_in server;
};

void *recv_mes(void *args)
{
    Thread_Data* data = (Thread_Data*)args;
    char buffer[1024];
    bzero(buffer,0);
    while (true)
    {
        struct sockaddr_in tmp;
        socklen_t tmp_len;
        int n = recvfrom(data->socket_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&tmp, &tmp_len);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cerr <<  buffer << std::endl;
        }
        bzero(buffer,0);

    }
}

void *send_mes(void *args)
{
    Thread_Data* data = (Thread_Data*)args;
    std::string message = "login";
    sendto(data->socket_fd, message.c_str(), message.size(), 0, (const struct sockaddr *)&data->server, sizeof(data->server));
    while (true)
    {
        std::cout << "Please Enter Message@ ";
        std::getline(std::cin, message);
        sendto(data->socket_fd, message.c_str(), message.size(), 0, (const struct sockaddr *)&data->server, sizeof(data->server));
    }
}

int main(int args, char *argv[])
{
    if (args != 3)
    {
        Usage("./UdpClient");
    }
    int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (socket_fd == -1)
    {
        logMessage(FATAL, "Socket Applied Fault");
        exit(SOCKET_DENIED);
    }
    struct sockaddr_in server;
    memset(&server, 0, sizeof server);
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(argv[1]);
    server.sin_port = htons(std::atoi(argv[2]));
    pthread_t recv_thread, send_thread;
    Thread_Data data;
    data.server = server;
    data.socket_fd = socket_fd;
    pthread_create(&send_thread, nullptr, send_mes, (void *)&data);
    pthread_create(&recv_thread, nullptr, recv_mes, (void *)&data);

    pthread_join(recv_thread, nullptr);
    pthread_join(send_thread, nullptr);

    return 0;
}

采用了多线程的方式, 这里需要知道,因为socket本就支持同时可读可写,我们可以理解为它就是线程安全的。

Windows Client端

#include<iostream>
#include<string>
#include<WinSock2.h>
#include<Windows.h>
#include<thread>
#include<functional>
#pragma comment(lib, "ws2_32.lib") // 链接库文件
#pragma warning(disable:4996)      //防止VS发出4996号警告

const int server_port = 8080;
const std::string server_ip = ""; //提前写好服务器IP
                                  //warning : 多线程版本仍有一些小问题  
struct Thread_Data
{
    SOCKET socket_fd;
    struct sockaddr_in server;
};

void recv_mes(const Thread_Data& data)
{

    char buffer[1024];
    while (true)
    {
        memset(buffer, 0, sizeof(buffer));
        struct sockaddr_in tmp;
        int tmp_len = sizeof(tmp);
        int n = recvfrom(data.socket_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&tmp, &tmp_len);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer << std::endl;
        }
        //std::cout << n << " errno:" << errno << std::endl;

    }
}

void send_mes(const Thread_Data& data)
{

    std::string message;
    std::cout << "Send A Message To Start A Chat ";
    while (true)
    {
        std::getline(std::cin, message);
        sendto(data.socket_fd, message.c_str(), (int)message.size(), 0, (const struct sockaddr*)&data.server, sizeof(data.server));
    }
}
int main()
{
    //初始化网络环境
	WSADATA wsd;    
	WSAStartup(MAKEWORD(2, 2), &wsd);
    system("chcp 65001");


    SOCKET socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (socket_fd == SOCKET_ERROR)
    {
        perror("Socket Error");
        exit(1);
    }
    struct sockaddr_in server;
    memset(&server, 0, sizeof server);
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());
    server.sin_port = htons(server_port);
    std::thread threads[2];
    Thread_Data data;
    data.server = server;
    data.socket_fd = socket_fd;
    sendto(socket_fd, "login", 5, 0, (const struct sockaddr*)&server, sizeof(server));
    threads[0] = std::thread(recv_mes, std::ref(data));
    threads[1] = std::thread(send_mes, std::ref(data));



    threads[0].join();
    threads[1].join();


    closesocket(socket_fd);
    WSACleanup();   //清理网络环境
	return 0;

}

这里的Windows端也是实现了一个多线程的版本,因为是不同的操作系统,在接口上是有一些差别的,不过大部分都差不多。

简易聊天室运行图

在这里插入图片描述

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

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

相关文章

话术巧妙分隔沟通效果更佳看看这个小技巧

客服回复客户咨询&#xff0c;如果遇到比较复杂的问题&#xff0c;经常会有大段的文字回复&#xff0c;用聊天宝的分段符功能&#xff0c;在需要分段的地方点击右上角的“插入分隔符”&#xff0c;就可以在指定位置分段&#xff0c;实现多段发送的目的。 前言 客服回复客户咨询…

Python-3.12.0文档解读-内置函数map()详细说明+记忆策略+常用场景+巧妙用法+综合技巧

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 详细说明 map(function, iterable, *iterables) 参数 返回值 示例 注意事项 参考…

CHI Read传输——CHI(3)

目录 一、Read操作概览 二、DMT(Direct Memory Transfer) 三、DCT (Direct Cache Transfer) 四、without Direct Data Transfer 五、ReadNoSnp and ReadOnce* structure with DMT 本篇我们来介绍一下CHI传输类型中的Read 一、Read操作概览 read操作有以下几种&#xff1…

自适应星空背景个人导航单页(附带源码)

自适应星空背景个人导航单页 效果图部分源码领取源码下期更新预报 效果图 部分源码 function Star(id, x, y) {this.id id;this.x x;this.y y;this.r Math.floor(Math.random() * 2) 1;var alpha (Math.floor(Math.random() * 10) 1) / 10 / 2;this.color "rgba(…

QColor官网文档简介

颜色可以用不同的模型来进行表示,常见的有RGB,HSV或者CMYK.QColor基于RGB值创建颜色,如果要转接到HSV或者CMYK需要使用转接函数toHsv() toCmyk() 返回期望格式的副本 get...和set...函数具体参见文档用isValid()检查RGB颜色是否合法颜色组成部分可以单独检索QRgb是一种无符号整…

第98天:权限提升-WIN 全平台MSF 自动化CS 插件化EXP 筛选溢出漏洞

目录 思维导图 前置知识 案例一&#xff1a; Web&Win2008-人工手动&全自动msf-筛选&下载&利用 手动 全自动msf 案例二: Web&Win2019-CS 半自动-反弹&插件&利用 思维导图 前置知识 提权方式&#xff0c;这里讲的是溢出漏洞 windows权限 常…

⌈ 传知代码 ⌋ 高速公路车辆速度检测软件

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

C++学习/复习8--STL简介/六大组件/缺陷

一、STL简介 二、六大组件 三、面试题 四、STL缺陷

实现按块复制元素的进阶技巧

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、按块复制元素的重要性 二、使用LED模块创建数组并复制 三、实现按块复制的具体步骤 四…

粤嵌—2024/5/28—最大正方形(✔)

代码实现&#xff1a; 方法一&#xff1a;模拟——超时 int maximalSquare(char **matrix, int matrixSize, int *matrixColSize) {int maxSide 0;if (matrix NULL || matrixColSize NULL || matrixSize < 0 || matrixColSize[0] < 0) {return 0;}for (int i 0; i &l…

Cesium For Unity 在Unity中无法下载的问题

Unity 下载失败&#xff0c;提供百度网盘“com.cesium.unity-1.10.0.tgz”下载链接 链接&#xff1a;https://pan.baidu.com/s/1PybXQ8EvkRofOKD6rSN66g?pwd1234 提取码&#xff1a;1234 导入方法&#xff1a; 1.打开PackageManager;Window-PackageManager 2.在PackageMan…

Golang | Leetcode Golang题解之第104题二叉树的最大深度

题目&#xff1a; 题解&#xff1a; func maxDepth(root *TreeNode) int {if root nil {return 0}queue : []*TreeNode{}queue append(queue, root)ans : 0for len(queue) > 0 {sz : len(queue)for sz > 0 {node : queue[0]queue queue[1:]if node.Left ! nil {queue…

酒店报修进入智能时代:无纸化系统的全面革新

在这个信息爆炸的时代&#xff0c;面对酒店的设备故障你还在用纸质工单来报修吗&#xff1f;那简直像是石器时代的遗风&#xff01;一场酒店服务的革命性变革正在悄然兴起&#xff0c;它将彻底颠覆你对传统报修方式的认知。想象一下&#xff0c;当客人在房间遇到水龙头漏水&…

使用大模型LLM实现销售AI

想象一个场景&#xff0c;客户通过聊天窗口咨询一款产品。销售AI首先使用LLM解析客户的问题&#xff0c;然后通过智能代理查询数据库获取产品详细信息&#xff0c;并以自然而友好的方式回应客户。 在对话过程中&#xff0c;AI可以评估客户的兴趣&#xff0c;并主动提供促销信息…

太极图形学——渲染——光线追踪概念部分

程序动画和渲染 程序动画和渲染有一些类似的地方&#xff0c;都是找到合适的像素并填上颜色&#xff0c;但是要把一个图片渲染的好看是有一些规则来指导的 光线追踪包含哪些呢 果壳中的光线追踪器 实时条件下的光线追踪&#xff1a;需要进行光栅化的处理 离线条件下做cg的话…

Hololens 2 新建自定义按钮

官方链接地址 1、创建Cube 2、添加PressableButton脚本&#xff0c;并点击AddNearin… 3、把Cube拖入到MovingButtonVisuals变量中 4、点击NearInteractionTouchable组件&#xff08;这个组件是添加和上一个脚本绑定的&#xff0c;自动添加上来的&#xff09;上的Fix… 5、…

探索无限乐趣,尽在淘宝扭蛋机小程序

随着科技的快速发展和人们消费习惯的不断改变&#xff0c;小程序已经成为我们日常生活中不可或缺的一部分。为了满足广大用户对新鲜、有趣、互动性强的购物体验的追求&#xff0c;淘宝特别推出了全新的扭蛋机小程序&#xff0c;让您在购物的同时&#xff0c;也能享受到扭蛋带来…

4步成功将三方库——speexdsp移植到OpenHarmony

四步实现三方库移植&#xff1a; 1、在Linux下编译要移植的三仓库&#xff0c;分析编译过程&#xff1b; 2、将要移植的三方库加入编译框架中以及产品引用&#xff1b; 3、增量编译出动态链接库和可执行文件推送到开发板上&#xff1b; 4、在开发板上验证移植是否成功、API接口…

【qt】QTableWidget 表格组件

QTableWidget 表格组件 一.应用场景二.初始化表格1.界面拖放2.设置列数3.表头的处理①&#xff1a;表头装的是啥②&#xff1a;设置为标头③&#xff1a;设置列宽的模式 4.设置行数5.添加每个单元格①&#xff1a;设置单元格信息②&#xff1a;添加单元格③&#xff1a;单元格附…

P148--章节作业1

编辑 编辑 public class Main {public static void main(String args[]){double yxq100000;int cishu0;while(true) {if(yxq > 50000) {yxq yxq - yxq * 0.05;cishucishu1;}else if(yxq > 1000){yxq yxq - 1000;cishucishu1;}else{break;}}System.out.print(cishu);} …