Linux——socket套接字与udp通信

目录

一、理解IP地址与端口

二、socket套接字

三、TCP与UDP的关系

四、网络字节序

五、socket编程

1.socket()创建套接字

2.填充sockaddr_in 结构体

3.bind() 绑定信息

4.recvfrom()接收消息

5.sendto()发送消息

六、UdpServer代码


一、理解IP地址与端口

IP地址是Internet Protocol(互联网协议)的缩写,是网络上用于标识和定位设备的唯一地址

他分为公网IP与内网IP,我们通常使用并且查询到的都是内网IP,如下,查询到的就是内网ip。

公网IP地址和内网IP地址共同构成了一个设备的所有IP地址,公网IP地址用于设备与互联网上的其他设备通信,而内网IP地址用于设备在局域网内部通信。

而在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址,直到的从哪里来到哪里去,我们才能将消息发到对方设备上。

但是仅仅只知道IP地址还不够,比如你聊QQ的时候,消息不仅仅要到你的电脑中,还得在QQ中显示出来,因此消息还得认识端口号。

端口号(port)是传输层协议的内容

  • 端口号是一个2字节16位的整数;
  • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
  • IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
  • 一个端口号只能被一个进程占用.

由此可以看出,网络通信的本质都是进程间通信,只不过这两个进程不一定在同一台电脑上。ip地址用来表示互联网中唯一的一台主机,端口号用来指定该机器中唯一的进程。 

二、socket套接字

这种IP地址和端口号的组合通常被称为套接字(socket)。

  • 一般情况,一个端口号和一个进程相关联。
  • 特殊情况,一个进程可以绑定多个端口号,但是一个端口号不能被多个进程绑定。

比如A进程绑定了很多端口号,那么往这些端口号发送的数据都会来到A这里。但是B端口号绑定了很多进程,那往B端口号发送数据,到底给哪一个进程呢?如果全部都给岂不是数据会很冗余,因此一端口号不能被多个进程绑定。

三、TCP与UDP的关系

TCP(传输控制协议)和UDP(用户数据报协议)是两种常用的网络传输协议。

TCP的特点

  1. 传输层协议
  2. 有连接
  3. 可靠传输
  4. 面向字节流

UDP的特点

  1. 传输层协议
  2. 无连接
  3. 不可靠传输
  4. 面向数据报

目前,我们只需要知道TCP有连接,并且可靠,UDP无连接,并且不可靠,字节流与数据包后续再讨论。

由于TCP有连接并可靠,因此需要付出更多的代价去完成TPC通信,适合如支付、发送机密文件重要场景。

UDP无连接并不可靠,因此要简单一些,适合视频通话、打游戏(低延迟)等场景

四、网络字节序

按数据在计算机存储中的排列方式的分为大端机与小端机。

比如在内存中的数据为 0x11223344 

如果是大端机就是低位字节处存高地址,高位字节处存低地址。

而小端机就是低位字节处存低地址,高位字节处存高地址。

既然无法保证所有的电脑都是小端机或者大端机,那么在网络通信的时候,需要考虑数据的字节序问题,通常需要进行字节序的转换。

所以网络规定:所有到达网络的数据,必须是大端的,因此所有从网络收到数据的机器,都知道数据是大端的。

所有数据具在发送到网络时,都必须要做小端到大端的转化,你是大端,直接发给网络就可以,你是小段,先转化完再发给网络。数据在到达机器时,也要做相应的判断,进行合适的转化。

如下接口可以进行相应的转化。

这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。

  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回
  • 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。 

五、socket编程

如下是socket编程的重要接口

// 创建 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);

其中有一个struct sockaddr* address。 

在网络编程的时候,socket分了很多类别如域间socket、网络socket,原始socket等等每一个socket都有自己独特的一些东西,但他们的本质都是socket。因此我们可以使用struct sockaddr* 这个指针来指向所有的socket。实现了C风格的多态。

今天我们着重学习一下网络socket。

1.socket()创建套接字

使用函数socket(int domain,int type,int protocol)接口创建socket。

网络socket的domain为AF_INIT,使用UDP数据包 type 为 SOCK_DGRAM,protocol默认填0。

Linux一切皆文件,这样我们就可以得到一个文件描述符,后面就可以通过该文件描述符在网络上进行通信。

2.填充sockaddr_in 结构体

有了文件描述符,还需要填充sockaddr_in结构体里面的字段,sockaddr 是一个通用的套接字地址结构体,而sockaddr_in则专门为网络socket设计的,他是用于IPv4套接字的地址结构体。

里面有

  • sin_family,代表使用哪个地址族,一般填 AF_INET 代表IPv4
  • sin_addr.s_addr,代表那个ip地址
  • sin_port,代表那个端口号

3.bind() 绑定信息

现在有了网络套接字并填充了sockaddr_in字段,但是由于现在这些内容还是栈上面变量,因此需要通过bind将该套接字绑定到指定的网络地址上,后面就可以通信了。

4.recvfrom()接收消息

recvfrom可以接受消息,接受消息放到buf里,预期接受len个字节的消息,flags默认为0,代表的阻塞模式,src_addr与addrlen为输入输出参数,输出发送放相关sokect消息。(因为有其他网络中的线程进行发送,你来接受,你输出的是你发送方的套接字地址结构体)

5.sendto()发送消息

 接口与recevfrom类似,位于区别是adrlen不需要传地址。

 通过这些操作与接口,就可以进行网络通信了,话不多说,我们直接用来看看。 

六、UdpServer代码

Comm.hpp(错误码头文件)

#pragma once//错误码
enum{Usage_Err = 1,Socket_Err,Bind_Err,
};

 InetAddr.hpp(网络套接字地址结构体封装)

#pragma once
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;
class InetAddr
{
public:InetAddr(struct sockaddr_in& peer):_addr(peer){_port = ntohs(peer.sin_port); // ntohs网络转主机short_ip = inet_ntoa(peer.sin_addr); // inet_ntoa sin_addr转点分十进制字符串ip}string GetIp(){return _ip;}uint16_t GetPort(){return _port;}string PrintDebug(){string info = _ip;info+=":";info+=to_string(_port);return info;}~InetAddr(){}private:string _ip;uint16_t _port;struct sockaddr_in _addr;
};

Log.hpp(日志文件)

#pragma once#include<iostream>
#include<cstdarg>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
using namespace std;
enum{Debug = 0,Info,Warnig,Error,Fatal
};enum{Screen = 10,OneFile,ClassFile
};string LevelToString(int level)
{switch (level){case Debug:return "Debug";case Info:return "Info";case Warnig:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "Unkonw"; }
}const int default_style = Screen;
const string default_filename = "Log.";
const string logdir = "log";class Log  
{
public:Log(int style = default_style,string filename = default_filename):_style(style),_filename(filename){if(_style != Screen)mkdir(logdir.c_str(),0775);}//更改打印方式void Enable(int style){_style = style;}//时间戳转化为年月日时分秒string GetTime(){time_t currtime = time(nullptr);struct tm* curr = localtime(&currtime);char time_buffer[128];snprintf(time_buffer,sizeof(time_buffer),"%d-%d-%d %d:%d:%d",curr->tm_year+1900,curr->tm_mon+1,curr->tm_mday,curr->tm_hour,curr->tm_min,curr->tm_sec);return time_buffer;}//写入到文件中void WriteLogToOneFile(const string& logname,const string& message){FILE* fp = fopen(logname.c_str(),"a");if(fp==nullptr){perror("fopen filed");exit(-1);}fprintf(fp, "%s\n", message.c_str());fclose(fp);}//打印日志void WriteLogToClassFile(const string& levelstr,const string& message){string logname = logdir;logname+="/";logname+=_filename;logname+=levelstr;WriteLogToOneFile(logname,message);}void WriteLog(const string& levelstr,const string& message){switch (_style) {case Screen:cout<<message<<endl;//打印到屏幕中break;case OneFile:WriteLogToClassFile("all",message);//给定all,直接写到all里break;case ClassFile:WriteLogToClassFile(levelstr,message);//写入levelstr里break;default:break;}}//打印日志void LogMessage(int level,const char* format,...){char rightbuffer[1024];//处理消息va_list args;   //va_list 是指针va_start(args,format);//初始化va_list对象,format是最后一个确定的参数//现在args指向了可变参数部分vsnprintf(rightbuffer,sizeof(rightbuffer),format,args);//写入到leftbuffer中va_end(args);char leftbuffer[1024];//处理日志等级、pid、时间string levelstr = LevelToString(level);string currtime = GetTime();string idstr = to_string(getpid());snprintf(leftbuffer,sizeof(leftbuffer),"[%s][%s][%s]",levelstr.c_str(),currtime.c_str(),idstr.c_str());string loginfo = leftbuffer;loginfo+=rightbuffer;WriteLog(levelstr,loginfo);}//提供接口给运算符重载使用void _LogMessage(int level,char* rightbuffer){char leftbuffer[1024];string levelstr = LevelToString(level);string currtime = GetTime();string idstr = to_string(getpid());snprintf(leftbuffer,sizeof(leftbuffer),"[%s][%s][%s]",levelstr.c_str(),currtime.c_str(),idstr.c_str());string messages = leftbuffer;messages+=rightbuffer;WriteLog(levelstr,messages);}//运算符重载void operator()(int level,const char* format,...){char rightbuffer[1024];va_list args;   //va_list 是指针va_start(args,format);//初始化va_list对象,format是最后一个确定的参数vsnprintf(rightbuffer,sizeof(rightbuffer),format,args);//写入到leftbuffer中va_end(args);_LogMessage(level,rightbuffer);}~Log() {}
private:int _style;string _filename;
};Log lg;class Conf
{
public:Conf(){lg.Enable(Screen); }~Conf(){}
};Conf conf;

 nocopy.hpp(服务端继承该文件,进栈拷贝构造与赋值拷贝构造)

#pragma onceclass nocopy
{
public:nocopy(){}nocopy(const nocopy& n) = delete;nocopy& operator=(const nocopy& n) = delete;~nocopy(){}
}; 

 UdpServer.hpp(服务器封装的类)

#pragma once#include <iostream>
#include <string>
#include <cstring>#include <sys/types.h>          //网络常用4小只
#include <sys/socket.h>         //网络常用4小只
#include <netinet/in.h>         //网络常用4小只
#include <arpa/inet.h>          //网络常用4小只#include <cerrno>
#include "nocopy.hpp"
#include "Log.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"using namespace std;const static string defaultip = "0.0.0.0";
const static uint16_t defaultport = 8888;
const static int defaultfd = -1;class UdpServer :public nocopy
{
public://固定绑定ip才需要 _ip// UdpServer(const string& ip = defaultip,uint16_t port = defaultport)//     :_ip(ip),_port(port),_sockfd(defaultfd)// {// }UdpServer(uint16_t port = defaultport):_port(port),_sockfd(defaultfd){}void Init(){//1.创建套接字  AF_INET表示网络套接字 SOCK_DGRAM 表示 UDP 协议的数据报套接字_sockfd = socket(AF_INET,SOCK_DGRAM,0);if(_sockfd<0){lg.LogMessage(Fatal,"socket error, %d : %s",errno,strerror(errno));//打印日志exit(Socket_Err);}lg.LogMessage(Info,"socket success, sockfd : %d", _sockfd);//2.填充 sockaddr_in 结构体 指定网络信息struct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_family = AF_INET;         //表示要使用的是 IPv4 地址// local.sin_addr.s_addr = inet_addr(_ip.c_str()); //inet_addr将字符串转32位二进制整数(固定绑定)local.sin_addr.s_addr = INADDR_ANY;//IP动态绑定(不固定)local.sin_port = htons(_port);  //_port主机转网络//绑定,让设备知道该结构体中的网络与端口int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n!=0){//绑定失败lg.LogMessage(Fatal,"bind error, %d : %s",errno,strerror(errno));exit(Bind_Err);}}void Start(){//服务器一直要接受消息,永不退出char buff[1024];for(;;){struct sockaddr_in peer;socklen_t len = sizeof(peer);//接受消息放到buff里,预期接受sizeof(buff)-1个字节的消息,peer为输入输出参数,输出clien的sokect消息ssize_t n = recvfrom(_sockfd,buff,sizeof(buff)-1,0,(struct sockaddr*)&peer,&len);if(n>0){InetAddr addr(peer);buff[n] = 0;cout<<"["<<addr.PrintDebug()<<"]# " <<buff<<endl;//sendto 发送消息给peerssize_t m = sendto(_sockfd,buff,strlen(buff),0,(struct sockaddr*)&peer,len);}}}~UdpServer(){}private:// string _ip;(固定绑定服务器ip才需要,我们选择不固定,动态绑定uint16_t _port;int _sockfd;
};

 UdpClient.cc (客户端)

#include<iostream>
#include <cerrno>
#include <cstring>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include "Comm.hpp"using namespace std;void Usage(string proc)
{cout<<"Usage: \n\t" <<proc<<"local_ip local_port\n"<<endl;
}int main(int argc,char* argv[])
{if(argc!=3){Usage(argv[0]);return Usage_Err;}string serverip = argv[1];uint16_t serverport = stoi(argv[2]);//创建套接字int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd<0){cerr<<"socket error: "<<strerror(errno)<<endl; exit(Socket_Err);}cout<<"create socket success, sockfd:"<<sockfd<<endl;//客户端需要bind,但是不需要显示bind(手动发生数据自动bind)//因为服务器的端口号只有一个,是总所周知并且固定的,客户端会很多并不一定在线//填充server信息struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverip.c_str());server.sin_port = htons(serverport);socklen_t len = sizeof(server);while(true){string inbuffer; cout<<"Please Enter#";getline(cin,inbuffer);//给server发送消息ssize_t n = sendto(sockfd,inbuffer.c_str(),inbuffer.size(),0,(struct sockaddr*)&server,len);if(n > 0){char buff[1024];//发送成功,并且接收消息struct sockaddr_in tmp;socklen_t tmp_len = sizeof(tmp);ssize_t m = recvfrom(sockfd,buff,sizeof(buff)-1,0,(struct sockaddr*)&tmp,&len);if(m>0){//接受成功buff[m] = 0;cout<<"server echo# "<<buff<<endl;}}}return 0;
}

 Main.cc (服务器的主函数)

#include "UdpServer.hpp"
#include"Comm.hpp"
#include<memory>//固定绑定ip
// void Usage(string proc)
// {
//     cout<<"Usage: \n\t" <<proc<<"local_ip local_port\n"<<endl;
// }// int main(int argc,char* argv[])
// {
//     if(argc!=3)
//     {
//         Usage(argv[0]);
//         return Usage_Err;
//     }//     string ip = argv[1];
//     uint16_t port = stoi(argv[2]);//     unique_ptr<UdpServer> usvr (new UdpServer(ip,port));
//     usvr->Init();
//     usvr->Start();
//     return 0;
// }//动态绑定ip
void Usage(string proc)
{cout<<"Usage: \n\t" <<proc<<"local_port\n"<<endl;
}
int main(int argc,char* argv[])
{if(argc!=2){Usage(argv[0]);return Usage_Err;}uint16_t port = stoi(argv[1]);unique_ptr<UdpServer> usvr (new UdpServer(port));usvr->Init();usvr->Start();return 0;
}

 Makefile(进行一键构造)

.PHONY:all
all:udp_server udp_client
udp_server:Main.ccg++ -o $@ $^ -std=c++11
udp_client:UdpClient.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f udp_server udp_client

运行结果如下,客户端可以给服务器发消息,服务器将客户端的发来消息做回应。

代码地址 

1.test为本文内容。

2.udp_server_excute为远程发送bash指令

3.udp_server_chat为多人聊天室。

谢谢大家观看!!!

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

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

相关文章

【C++】详解string类

目录 简介 框架 构造 全缺省构造函数 ​编辑 传对象构造函数 拷贝构造 析构函数 容量 size() capacity&#xff08;&#xff09; empty() clear() reserve() ​编辑 resize() 遍历 检引用符号"[ ]"的重载 迭代器 begin() end() rbegin() rend(…

【触摸案例-控件不能响应的情况 Objective-C语言】

一、接下来,我们来说这个“控件不能响应的情况”, 1.素材里边,有一个“不接受用户交互的情况”,这么一个代码,把它打开, 把这个项目啊,复制过来,改一个名字,叫做“04-控件不能响应的情况”, 打开之后,command + R,运行一下, 在storyboard上,你也可以看得出来,我…

智慧农业设备——虫情监测系统

随着科技的不断进步和农业生产的日益现代化&#xff0c;智慧农业成为了新时代农业发展的重要方向。其中&#xff0c;虫情监测系统作为智慧农业的重要组成部分&#xff0c;正逐渐受到广大农户和农业专家的关注。 虫情监测系统是一种基于现代传感技术、图像识别技术和大数据分析技…

链表-----返回倒数第K个节点回文结构的判断相交链表

目录 1.返回倒数第K个节点 2.回文结构的判断 3.相交链表的判断&#xff0c;返回交点 1.返回倒数第K个节点 &#xff08;1&#xff09;返回链表的第k个节点&#xff0c;我们这里的做法是定义两个指针&#xff0c;这两个指针之间相差的是k这个长度&#xff1b;这个过程的实现就…

Android手势识别面试问题及回答

问题 1: 如何在Android中实现基本的手势识别&#xff1f; 答案: 在Android中&#xff0c;可以通过使用GestureDetector类来实现基本的手势识别。首先需要创建一个GestureDetector的实例&#xff0c;并实现GestureDetector.OnGestureListener接口来响应各种手势事件&#xff0c…

创建SpringBoot和RabbitMQ的整合项目

文章目录 创建SpringBoot和RabbitMQ的整合项目首先快速创建一个maven项目引入SpringBoot整合rabbitMQ的依赖在src/main目录下创建resources目录并引入配置文件写消息发送者MessageSender写消息接收者MessageReceiver写RabbitMQConfig配置类写SpringBoot启动主类CommandLineRunn…

小剧场短剧影视小程序源码_后端PHP

项目运行截图 源码贡献 https://githubs.xyz/boot?app42 部署说明 linux/win任选 PHP版本&#xff1a;7.3/7.2&#xff08;测试时我用的7.2要安装sg扩展 &#xff09; 批量替换域名http://video.owoii.com更换为你的 批量替换域名http://120.79.77.163:1更换为你的 这两个…

代码随想录算法训练营第60天|84.柱状图中最大的矩形

84. 柱状图中最大的矩形 题目链接&#xff1a;柱状图中最大的矩形 题目描述&#xff1a;给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 解题思路&#…

24 JavaScript学习:this

this在对象方法中 在 JavaScript 中&#xff0c;this 的值取决于函数被调用的方式。在对象方法中&#xff0c;this 引用的是调用该方法的对象。 让我们看一个简单的例子&#xff1a; const person {firstName: John,lastName: Doe,fullName: function() {return this.firstN…

【webrtc】MessageHandler 3: 基于线程的消息处理:以sctp测试为例

消息处理可以用于模拟发包处理G:\CDN\rtcCli\m98\src\net\dcsctp\socket\dcsctp_socket_network_test.cc 这个实现中,onMessage还是仅对了一种消息进行处理,就是接收则模式下,打印带宽。当然,可能程序有多个消息,分别在不同的onmessage中执行?SctpActor:以一个恒定的速率…

C语言贪吃蛇项目

今天给大家带来一款简单的贪吃蛇游戏&#xff0c;一起随我来看看吧 游戏效果&#xff1a; 实现基本的功能&#xff1a; • 贪吃蛇地图绘制 • 蛇吃⻝物的功能&#xff1a;&#xff08;上、下、左、右⽅向键控制蛇的动作&#xff09; • 蛇撞墙死亡 • 蛇撞⾃⾝死亡 • 计算得分…

Flink 实时数仓(一)【实时数仓离线数仓对比】

前言 昨天技术面的时候&#xff0c;面试官说人家公司现在用的都是最新的技术&#xff0c;比如 Doris 等一些最新的工具&#xff0c;确实这些课是学校永远不会开设的&#xff0c;好在他说去了会带着我做一做。可是 ...... 学院这边确实不允许放人&#xff0c;唉&#xff0c;可惜…

Kubernetes 弃用Docker后 Kubelet切换到Containerd

containerd 是一个高级容器运行时&#xff0c;又名 容器管理器。简单来说&#xff0c;它是一个守护进程&#xff0c;在单个主机上管理完整的容器生命周期&#xff1a;创建、启动、停止容器、拉取和存储镜像、配置挂载、网络等。 containerd 旨在轻松嵌入到更大的系统中。Docke…

python项目入门新手攻略

最近工作需要接手了代码量比较大的python开发的项目&#xff0c;平时写python不多&#xff0c;记录一下如何熟悉项目。 分析调用流程-pycallgraph 因为代码量比较大&#xff0c;所以希望通过工具生成代码调用流程&#xff0c;因此用到了pycallgraph。 pycallgraph&#xff0…

windows Jenkins运行python+selenium打开浏览器一直无响应,运行中,还没有打开浏览器

一开始解决办法是把打开服务把Jenkins给禁用了 但是没有用&#xff0c;然后找到安装目录 C:\Program Files\Jenkins 在这个路径下&#xff0c;在地址栏输入cmd打开命令窗口运行Jenkins启动命令 java -jar jenkins.war --httpPort8080 打开浏览器进入链接 http://localhost:…

【Unity学习笔记】第十四 Prefab 概念解惑

目录 1 prefab、prefab变体、prefab覆盖和prefab 嵌套2 connect 与unpack3 prefab到底是什么&#xff0c;它和gameobject又有什么区别&#xff1f;4 为什么要用prefab&#xff1f;5 代码动态加载prefab6 为什么我unity PrefabUtility.InstantiatePrefab() 得到的是null7 Prefab…

Redis基本命令

目录 一、包含String、Set数据类型的基本命令 1、添加一个键值对 2、获取key所关联的字符串值 3、同时设置多个key-value 4、获取多个key对应的值 运行结果 5、将给定的value追加到原值的末尾 追加后效果 6、删除单个key 7、同时删除多个key 8、查询包含某个字符的k…

ubuntu入门

基础命令 cd 切换命令 ls 查看当前目录下所有的文件 cp a.c b.c 拷贝a.c 到 b.c touch a.c 创建a.c文件 mkdir file 创建文件夹file rm file 删除文件 rmdir 删除test文件夹 rmdir test/ mv 移动文件 mv a.c b.c 把a.c 替换成b.c ifconfig 查看电脑网络信息 rm xx 删…

Mybatis进阶(动态SQL)

文章目录 1.动态SQL1.基本介绍1.为什么需要动态SQL2.基本说明3.动态SQL常用标签 2.环境搭建1.新建子模块2.删除不必要的两个文件夹3.创建基本结构4.父模块的pom.xml5.jdbc.properties6.mybatis-config.xml7.MyBatisUtils.java8.MonsterMapper.java9.MonsterMapper.xml10.测试Mo…

工业互联网通讯协议—欧姆龙(Fins tcp)

一、场景 近期公司要对欧姆龙CP系列设备的数据采集&#xff0c;于是就研究了下欧姆龙的Fins Tcp协议。 二、Fins Tcp 组成字节说明固定头446494E53 FINS对应的ASCII码的十六进制长度4后面剩余指令的长度命令4 握手固定为&#xff1a;00000000 读写固定为&#xff1a;0000000…