【lesson62】网络通信UdpSocket版

文章目录

  • UdpSocket
  • UdpServer.hpp
    • UdpServer类
      • 成员变量解释
      • 成员函数解释
    • UdpServer的实现
    • ServerIinit的实现
      • socket
      • bind
      • htons
      • inet_addr
      • 具体实现
    • ServerStart的实现
      • recvfrom
      • sendto
      • ntohs
      • inet_ntoa
      • 具体实现
    • ~UdpServer函数实现
    • UdpServer.hpp整体完整代码
  • UdpServer.cc
  • UdpClient.cc
    • Thread.hpp
    • UdpClient.cc实现
  • Common.h
  • Log.hpp

UdpSocket

我们要用udp协议来实现网络通信。
我们要用udp协议实现两样:
UdpServer(服务器)
UdpClient(客户端)

客服端可以向服务器发送一些数据或者请求,服务器对其进行响应

UdpServer.hpp

UdpServer类

class UdpServer
{
public:UdpServer(uint16_t port, std::string ip = ""): _port(port),_ip(ip){}bool ServerIinit(){}void ServerStart(){}~UdpServer(){}private:uint16_t _port;std::string _ip;int _sock;std::unordered_map<std::string, struct sockaddr_in> _users;
};

成员变量解释

uint16_t _port;
std::string _ip;
int _sock;
std::unordered_map<std::string, struct sockaddr_in> _users;

_port:服务器进程要绑定的端口号
_ip:指定连接该服务器的客户端IP,可以设为INADDR_ANY支持任意客户端连接服务器
_sock:套接字
_users:存储所有连接该服务器的端口号和sockaddr_in

成员函数解释

UdpServer(uint16_t port, std::string ip = ""): _port(port),_ip(ip)
{}bool ServerIinit()
{}void ServerStart()
{}~UdpServer()
{}

UdpServer:构造函数
参数解释:
port:服务器要绑定的端口号
ip:服务器要绑定的IP地址,如果不传默认为缺省值“”,如果为缺省值我们可以设为INADDR_ANY支持任意IP绑定

ServerIinit:初始化服务器

ServerStart:启动服务器

~UdpServer:析构函数,释放一些资源

UdpServer的实现

UdpServer(uint16_t port, std::string ip = ""): _port(port),_ip(ip)
{}

ServerIinit的实现

需要用到的接口介绍

socket

在这里插入图片描述
参数解释:
domain:
在这里插入图片描述
type:
在这里插入图片描述
protocol:
在这里插入图片描述
return value:
在这里插入图片描述
在这里插入图片描述

bind

在这里插入图片描述
在这里插入图片描述
参数介绍:
socetfd:创建套接字文件的文件描述符
addr:表示struct sockaddr的地址,用于设定要绑定的ip和端口
addr_len:struct sockaddr结构体的大小

struct sockaddr {sa_family_t sa_family;char        sa_data[14];
}

sa_family 用于指定AF_***表示使用什么协议族的ip
sa_data 存放ip和端口
这里有一个问题,直接向sa_data中写入ip和端口号有点麻烦,内核提供struct sockaddr_in结构体进行写入,通过/usr/include/linux/in.h可以看到结构体原型
使用该结构体时需要包含<netinet/in.h>头文件,且sockaddr_in结构体是专门为tcp/ip协议族使用,其他协议族需要使用其对应的转换结构体,比如**“域通信协议族**” 使用的是sockaddr_un结构体

struct sockaddr_in {__kernel_sa_family_t  sin_family;     /* Address family               */__be16                sin_port;       /* Port number                  */struct in_addr        sin_addr;       /* Internet address             *//* Pad to size of `struct sockaddr'. 设置IP端口号这个成员暂时用不到 */unsigned char         __pad[__SOCK_SIZE__ - sizeof(short int) -sizeof(unsigned short int) - sizeof(struct in_addr)];
};/* Internet address.填补相比于struct sockaddr所缺的字节数,保障强制转换不要出错 */
struct in_addr {__be32  s_addr; // __be32是32位的unsigned int ,因为ipv4是无符号32位整型 
};

sockaddr_in结构体中存放的端口和ip是分开的,使用struct sockaddr_in设置后,让后将其强制转换struct sockaddr类型,然后传递给bind函数即可

htons

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

inet_addr

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

具体实现

bool ServerIinit()
{//1.创建套接字//AF_INET 表示是网络编程//SOCK_DGRAM 表示使用的是udp协议_sock = socket(AF_INET, SOCK_DGRAM, 0);//如果返回值小于0,说明创建套接字失败if (_sock < 0){//打出创建的日志信息LogMessage(FATAL, "socket %s", strerror(errno));exit(2);}//打出创建的日志信息LogMessage(NORMAL, "udp socket success");//定义sockaddr_in 对象struct sockaddr_in local;//初始化sockaddr_in ,初始化为0bzero(&local, sizeof(local));//向sockaddr_in对象 local填入一些数据//local.sin_port server要绑定的端口号//local.sin_family server使用的协议家族,指明要使用什么协议//local.sin_addr.s_addr server要绑定的IP地址//htons()将端口号转化为大端local.sin_port = htons(_port);local.sin_family = AF_INET;//判断要绑定的IP地址是否为空,如果为空填入INADDR_ANY表示绑定任意IP地址//如果不为空填入该IP//inet_addr()做了两个工作//1.将字符串IP转化为32位整数 2.将32位整数转化为大端local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());//2.开始绑定int n = bind(_sock, (struct sockaddr *)&local, sizeof(local));//如果绑定失败n bind会返回-1if (n == -1){//打印出绑定的日志信息LogMessage(FATAL, "bind %s", strerror(errno));exit(3);}//打印出绑定的日志信息LogMessage(NORMAL, "udp bind success");//到最后成功打印出初始化成功的日志信息LogMessage(NORMAL, "init udp server done ... %s", strerror(errno));return true;
}

ServerStart的实现

需要用到的接口介绍

recvfrom

在这里插入图片描述
在这里插入图片描述
参数解释:
sockfd:创建套接字文件的文件描述符
buffer:输出型参数,数据缓冲区数组,会把从网络中获取到的数据放入其中
len:buffer的大小
flages
在这里插入图片描述
src_addr:指向一个sockaddr结构体,用于存储发送方的地址信息。
addrlen:src_addr结构体的大小
return value:
在这里插入图片描述
在这里插入图片描述

sendto

在这里插入图片描述

在这里插入图片描述
参数解释:
sockfd:创建套接字文件的文件描述符
buffer:要输入数据的数组
len:输入数据的长度
dest_addr:指向一个sockaddr结构体,用于存储接受方的地址信息。
addrlen:dest_addr结构体的大小
flags:
在这里插入图片描述
return value:
在这里插入图片描述
在这里插入图片描述

ntohs

在这里插入图片描述

inet_ntoa

在这里插入图片描述
在这里插入图片描述

具体实现

void ServerStart()
{//数据缓冲区char buffer[BUFFER_SIZE];//客户端的IP和port的临时存放区char key[64];//服务器肯定是一直循环执行的while (true){//定义peer结构体,peer是输出型参数,可以获取客户端的IP和portstruct sockaddr_in peer;//计算peer结构体的大小socklen_t len = sizeof(peer);//初始化peer结构体,初始化为0memset(&peer, 0, len);//recvfrom从_sock文件中获取数据int s = recvfrom(_sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer, &len);//s > 0 代表成功获取数据,我们要对该数据进行处理if (s > 0){buffer[s] = '\0';//因为端口是从网络中来的,所以是大端序列//ntohs将大端的port转化为小端uint16_t src_port = ntohs(peer.sin_port);//inet_ntoa做了两个工作//1.将网络中获取到的32位整数IP地址转化为小端序列//2.将IP地址转化为字符串std::string src_ip = inet_ntoa(peer.sin_addr);//处理获取到的数据printf("[%s:%d]# %s\n", src_ip.c_str(), src_port, buffer);//将网络中的IP地址和端口号存入key中snprintf(key, sizeof(key), "%s-%u", src_ip.c_str(), src_port);LogMessage(NORMAL, "key: %s", key);//在_users中查找key,如果存在说明这个客户端之前有连接果不用在添加进_user中auto ret = _users.find(key);//如果不存在如果存在说明这个客户端第一次连接服务器,将其添加进_user中if (ret == _users.end()){LogMessage(NORMAL, "add new user : %s", key);_users.insert(std::make_pair(key, peer));}}//将收到的消息广播给所有主机for (auto &iter : _users){std::string send_message(key);send_message += "# ";send_message += buffer;LogMessage(NORMAL, "push message to %s", iter.first.c_str());//std::cout << send_message << std::endl;//sendto发送数据到_sock文件中,并指明要发送主机的IP地址和端口号//主机的IP地址和端口号在(struct sockaddr *)&iter.secondsendto(_sock, send_message.c_str(), send_message.size(), 0,(struct sockaddr *)&iter.second, len);}}
}

~UdpServer函数实现

~UdpServer(){if (_sock >= 0){close(_sock);}}

UdpServer.hpp整体完整代码

#include "Common.h"
#include "Log.hpp"class UdpServer
{
public:UdpServer(uint16_t port, std::string ip = ""): _port(port),_ip(ip){}bool ServerIinit(){_sock = socket(AF_INET, SOCK_DGRAM, 0);if (_sock < 0){LogMessage(FATAL, "socket %s", strerror(errno));exit(2);}LogMessage(NORMAL, "udp socket success");struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_port = htons(_port);local.sin_family = AF_INET;local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());int n = bind(_sock, (struct sockaddr *)&local, sizeof(local));if (n == -1){LogMessage(FATAL, "bind %s", strerror(errno));exit(3);}LogMessage(NORMAL, "udp bind success");LogMessage(NORMAL, "init udp server done ... %s", strerror(errno));return true;}void ServerStart(){char buffer[BUFFER_SIZE];char key[64];while (true){struct sockaddr_in peer;socklen_t len = sizeof(peer);memset(&peer, 0, len);int s = recvfrom(_sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer, &len);if (s > 0){buffer[s] = '\0';uint16_t src_port = ntohs(peer.sin_port);std::string src_ip = inet_ntoa(peer.sin_addr);printf("[%s:%d]# %s\n", src_ip.c_str(), src_port, buffer);snprintf(key, sizeof(key), "%s-%u", src_ip.c_str(), src_port);LogMessage(NORMAL, "key: %s", key);auto ret = _users.find(key);if (ret == _users.end()){LogMessage(NORMAL, "add new user : %s", key);_users.insert(std::make_pair(key, peer));}}for (auto &iter : _users){std::string send_message(key);send_message += "# ";send_message += buffer;LogMessage(NORMAL, "push message to %s", iter.first.c_str());//std::cout << send_message << std::endl;sendto(_sock, send_message.c_str(), send_message.size(), 0,(struct sockaddr *)&iter.second, len);}}}~UdpServer(){if (_sock >= 0){close(_sock);}}private:uint16_t _port;std::string _ip;int _sock;std::unordered_map<std::string, struct sockaddr_in> _users;
};

UdpServer.cc

#include "udp_server.hpp"static void Usage(std::string proc)
{std::cout << "Usage:\t\n " << proc << " port\n" << std::endl;
}int main(int argc,char* argv[])
{//使用规则必须是 ./server portif(argc != 2){Usage(argv[0]);exit(1);}uint16_t port = atoi(argv[1]);//使用智能指针管理UdpServerstd::unique_ptr<UdpServer> svr(new UdpServer(port));svr->ServerIinit();svr->ServerStart();return 0;
}

UdpClient.cc

Thread.hpp

#include "Common.h"
#include "Log.hpp"typedef void *(*fun_t)(void *);class ThreadData
{
public:std::string _name;void *_args;
};class Thread
{
public:Thread(std::string name, fun_t routine, void *args){_thread_data._name = name;_func = routine;_thread_data._args = args;}void Start(){pthread_create(&_tid, nullptr, _func, (void*)&_thread_data);//std::cout <<  _thread_data._name << " create success" << std::endl;LogMessage(NORMAL,"%s creadte success",_thread_data._name.c_str());}void Join(){pthread_join(_tid, nullptr);//std::cout <<  _thread_data._name << " join success" << std::endl;LogMessage(NORMAL,"%s join success",_thread_data._name.c_str());}std::string GetName(){return _thread_data._name;}private:ThreadData _thread_data;pthread_t _tid;fun_t _func;
};

UdpClient.cc实现

#include "Common.h"
#include "Log.hpp"
#include "Thread.hpp"std::string serverip;
uint16_t serverport = 0;static void Usage(std::string proc)
{std::cout << "Usage:\t\n " << proc << " serverip serverport\n"<< std::endl;
}//发送数据的线程
void *udpSend(void *args)
{int sock = *((int *)((ThreadData *)args)->_args);//std::cout << sock << std::endl;struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_port = htons(serverport);//std::cout << serverport << std::endl;;//std::cout << serverip << std::endl;server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverip.c_str());std::string message;while (true){std::cerr << "Please Entery your message: ";std::getline(std::cin, message);if (message.c_str() == "quit"){LogMessage(NORMAL, "client quit");break;}sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));}
}//接收数据的线程
void *udpRecv(void *args)
{int sock = *((int *)((ThreadData *)args)->_args);//std::cout << sock << std::endl;char buffer[BUFFER_SIZE];while (true){struct sockaddr_in temp;socklen_t len = sizeof(temp);int s = recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr*)&temp,&len);//std::cout << buffer;if(s > 0){buffer[s] = '\0';std::cout << buffer << std::endl;}}
}int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}int sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock < 0){LogMessage(FATAL, "socket %s", strerror(errno));exit(1);}LogMessage(NORMAL, "socket %s", strerror(errno));serverip = argv[1];serverport = atoi(argv[2]);//多线程版std::unique_ptr<Thread> sender(new Thread("send_thread", udpSend, (void *)&sock));std::unique_ptr<Thread> recver(new Thread("recv_thread", udpRecv, (void *)&sock));sender->Start();recver->Start();sender->Join();recver->Join();// LogMessage(NORMAL, "%s%d",serverip.c_str(),serverport);//单线程版 弊端:一直卡在发送数据处,当别人发消息时,无法及时接收/* struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_port = htons(serverport);server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverip.c_str());std::string message;char buffer[BUFFER_SIZE];while (true){std::cout << "Please Entery your message: ";std::getline(std::cin, message);if (message.c_str() == "quit"){LogMessage(NORMAL, "client quit");break;}//std::cout << message;sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));struct sockaddr_in temp;socklen_t len = sizeof(temp);int s = recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr*)&temp,&len);//std::cout << buffer;if(s > 0){buffer[s] = '\0';std::cout << buffer << std::endl;}} */return 0;
}

Common.h

共用的头文件

#pragma once#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdarg.h>
#include <pthread.h>#define BUFFER_SIZE 1024

Log.hpp

打印日志的函数

#pragma once
#include "Common.h"// 日志是有日志级别的
#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4#define STANDARD_NUM 1024
#define CUSTOM_NUM 1024const char *gLevelMap[] = {"DEBUG","NORMAL","WARNING","ERROR","FATAL"};void LogMessage(int level, const char *format, ...)
{#ifndef DEBUG_SHOWif(level== DEBUG) return;
#endif// 标准部分char standard_buffer[STANDARD_NUM];snprintf(standard_buffer, sizeof(standard_buffer), "[%s][%ld]", gLevelMap[level], time(nullptr));// 自定义部分char custom_buffer[CUSTOM_NUM];va_list args;va_start(args, format);vsnprintf(custom_buffer, sizeof(custom_buffer), format, args);va_end(args);printf("%s%s\n", standard_buffer, custom_buffer);
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络防火墙综合实验

备注&#xff1a;电信网段15.1.1.0 移动网段14.1.1.0 办公区 11.1.1.0 生产区 10.1.1.0 服务区 13.1.1.0 公网 1.1.1.1 和 2.2.2.2 需求&#xff1a; 1、办公区设备可以通过电信链路和移动链路上网&#xff08;多对多nat&#xff0c;并且需要保留一个公网ip&#xff09; 2、…

图形渲染基础学习

原文链接&#xff1a;游戏开发入门&#xff08;三&#xff09;图形渲染_如果一个面只有三个像素进行渲染可以理解为是定点渲染吗?-CSDN博客 游戏开发入门&#xff08;三&#xff09;图形渲染笔记&#xff1a; 渲染一般分为离线渲染与实时渲染&#xff0c;游戏中我们用的都是…

使用阿里云发送短信

使用阿里云短信服务有两种方式 API 发送和 控制台发送&#xff0c;控制台发送到话有太多限制&#xff0c;这里我们使用API 通过 调用服务端代码进行发送。 整体结构如下&#xff1a; 导入依赖 <!--阿里云短信发送--><dependency><groupId>com.aliyun<…

1、电源管理入门之关机重启详解

目录 1. 关机重启软件流程框图 1.1 用户层 1.2 Linux内核层 1.3 ATF层 1.4 SCP层 2. Busybox中的关机重启命令 3. Linux内核中的处理 3.1 系统调用实现 3.2 内核关机函数分析 3.3 关闭所有设备处理 3.4 多CPU调度相关处理 3.5 内核核心关闭 3.6 硬件平台的关闭 3…

用户空间与内核通信(一)

在Linux中&#xff0c;内核空间与用户空间是操作系统中的两个主要部分&#xff0c;它们有着明显的区别和不同的功能。 内核空间&#xff1a; 内核空间是操作系统内核运行的区域&#xff0c;它包括了操作系统内核代码、数据结构和设备驱动程序等。内核空间位于虚拟地址空间的最…

GptSoVits音频教程

这个号称5秒克隆&#xff0c;或者用1分钟音频训练10分钟就能达到原声效果。 5秒的号称&#xff0c;只要是&#xff0c;什么几秒的&#xff0c;大家可以完全不要想了&#xff0c;什么知更鸟&#xff0c;什么火山&#xff0c;包括本次的GptSoVits的效果肯定是不行的&#xff0c;…

gRPC 备查

简介 HTTP/2 HTTP/2 的三个概念 架构 使用流程 gRPC 的接口类型 1.单一RPC 2.服务器流式RPC 3.客户端式流式RPC 4.双向流式RPC

WordPress主题YIA在广告位添加图片广告时下方有空白怎么办?

YIA主题设置中默认有4个广告位&#xff0c;而侧边栏的广告位由站长自行添加。boke112百科在这些广告位添加图片广告后发现图片下方有空白&#xff0c;导致下方的两个角没有变圆角&#xff0c;看起来也有点不好看。具体如下图所示&#xff1a; 其实&#xff0c;这个问题就是典型…

【Python】【VS Code】VS Code中python.json和setting.json文件配置说明

目录 1. python.json配置 2. setting.json配置 3. 解决中文乱码 4. 实现效果 1. python.json配置 python.json 获取步骤&#xff1a;文件 -> 首选项 -> 配置用户代码片段 -> python 此为VS Code的头文件设置&#xff0c;复制以下内容到 python.json {"HEADER…

基于ant的图片上传组件封装(复制即可使用)

/*** 上传图片组件* param imgSize 图片大小限制* param data 上传数据* param disabled 是否禁用*/import React, { useState,useEffect } from react; import { Upload, Icon, message} from antd; const UploadImage ({imgSize 50,data { Directory: Image },disabled f…

Eclipse - Reset Perspective

Eclipse - Reset Perspective 1. Window -> Perspective -> Reset Perspective2. Reset Perspective -> YesReferences 1. Window -> Perspective -> Reset Perspective 2. Reset Perspective -> Yes ​​​ References [1] Yongqiang Cheng, https://yo…

AtCoder Beginner Contest 341 D - Only one of two (Java)

AtCoder Beginner Contest 341 D - Only one of two (Java) 比赛链接&#xff1a;AtCoder Beginner Contest 341 D题传送门AtCoder&#xff1a;D - Only one of two D题传送门洛谷&#xff1a;[ABC341D] Only one of two 题目&#xff1a;[ABC341D】 Only one of two 题目…

LeetCode 0103.二叉树的锯齿形层序遍历:层序遍历 + 适时翻转

【LetMeFly】103.二叉树的锯齿形层序遍历&#xff1a;层序遍历 适时翻转 力扣题目链接&#xff1a;https://leetcode.cn/problems/binary-tree-zigzag-level-order-traversal/ 给你二叉树的根节点 root &#xff0c;返回其节点值的 锯齿形层序遍历 。&#xff08;即先从左往…

Stable Diffusion WebUI 界面介绍

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 大家好&#xff0c;我是水滴~~ 本文主要对 Stable Diffusion WebUI 的界面进行简单的介绍&#xff0c;让你对该 WebUI 有个大致的了解&#xff0c;为后面的深入学习打下一个基础。主要内容包…

Vscode vim 插件使用Ctrl+C和V进行复制粘贴到剪切板

Vscode vim 插件使用CtrlC和V进行复制粘贴到剪切板 使用这一个插件的时候复制粘贴和其他软件互动的时候体验不好, 并且不可以用Ctrl c, Ctrl v很不爽 "vim.commandLineModeKeyBindings": [{"before" : ["Ctrl", "c"],"after&q…

LabVIEW高速信号测量与存储

LabVIEW高速信号测量与存储 介绍了LabVIEW开发的高速信号测量与存储系统&#xff0c;解决实验研究中信号捕获的速度和准确性问题。通过高效的数据处理和存储解决方案&#xff0c;本系统为用户提供了一种快速、可靠的信号测量方法。 项目背景 在科学研究和工业应用中&#xf…

爱上JVM——常见问题(一):JVM组成

1 JVM组成 1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序的运行环境&#xff08;java二进制字节码的运行环境&#xff09; 好处&#xff1a; 一次编写&…

【4.1计算机网络】TCP-IP协议簇

目录 1.OSI七层模型2.常见协议及默认端口3.TCP与UDP的区别 1.OSI七层模型 osi七层模型&#xff1a; 1.应用层 2.表示层 3.会话层 4.传输层&#xff1a;TCP为可靠的传输层协议。 5.网络层 6.数据链路层 7.物理层 2.常见协议及默认端口 3.TCP与UDP的区别 例题1. 解析&#xff1…

XR行业首家|李未可科技通过深度合成服务算法备案

2月18日&#xff0c;国家网信办发布第四批深度合成服务算法备案。 根据《互联网信息服务深度合成管理规定》第十九条规定&#xff0c;具有舆论属性或者社会动员能力的深度合成服务提供者&#xff0c;应当按照《互联网信息服务算法推荐管理规定》履行备案和变更、注销备案手续。…

2.19作业

1.使用fread,fwrite完成两个文件之间的拷贝 程序代码&#xff1a; #include <myhead.h>int main(int argc, const char *argv[]) {if(argc!3){printf("input file error\n");printf("usage:./a.out srcfile destfile\n");return -1;}FILE *fpNULL;…