Modbus协议控制动态链接库

应用场景

基于各门语言都有各自的modbus协议库,且良莠不齐,而且在具体的框架下可能存在版本依赖问题,
而且对modbus协议存在比较多的细节处理,可以查看modbus slave、或者modbus poll中相关的配置可知,
数据类型对应读写寄存器个数、大小端的处理等等细节,所以将以常用的场景下的配置编写动态链接库供其他语言调用。

编程语言

因为我不会写C语言,这里使用Go语言编写代码,以C函数分享库的模式编译成动态链接库

将以Python与Node.js调用的示例演示

辅助工具下载地址

Modbus Poll: 激活码(仅供学习 5A5742575C5D10) -> 模拟主站

链接: https://pan.baidu.com/s/1Sk2m6HWTU0-hE82-BxPvKA 提取码: 1fwn

Modbus Slave: 激活码(仅供学习 5455415451475662) -> 模拟从站

链接: https://pan.baidu.com/s/137w1-2gGQ3bETvbyBefNQg 提取码: 9433

编写Go代码示例 使用Cgo引入C函数

  • Modbus TCP示例
package main

/*
#include <stdlib.h>
*/
import (
"C"
"bytes"
"encoding/binary"
"fmt"
"log"
"math"
"time" "github.com/goburrow/modbus"
)
import "strings" var handler *modbus.TCPClientHandler //export Connect
func Connect(ip *C.char, port C.int) C.int {
log.Printf("Connect ...")
handler = modbus.NewTCPClientHandler(fmt.Sprintf("%s:%d", C.GoString(ip), int(port)))
handler.Timeout = 250 * time.Millisecond
err := handler.Connect()
if err != nil {
log.Printf("Connect error: %v", err)
return -1
}
return 0
} //export Close
func Close() C.int {
log.Printf("Close ...")
err := handler.Close()
if err != nil {
log.Printf("Close error: %v", err)
return -1
}
return 0
} //ReadRegister 一般错误返回 -1 连接失败返回 -3
//export ReadRegister
func ReadRegister(slaveId C.int, address C.int, dataType *C.char, data *C.double) C.int {
var results []byte
var err error handler.SlaveId = byte(slaveId) client := modbus.NewClient(handler) dt := C.GoString(dataType) switch dt {
case "int16":
results, err = client.ReadInputRegisters(uint16(address), 1)
case "int32", "float":
results, err = client.ReadInputRegisters(uint16(address), 2)
case "int64", "double":
results, err = client.ReadInputRegisters(uint16(address), 4)
} if err != nil {
switch {
case strings.Contains(err.Error(), "connection"):
log.Println("Connection error:", err)
return -3
case strings.Contains(err.Error(), "timeout"):
log.Println("Timeout error:", err)
return -1
default:
log.Println("Other error:", err)
return -1
}
} var value float64
switch dt {
case "int16":
var intValue int16
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
value = float64(intValue)
case "int32":
var intValue int32
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
value = float64(intValue)
case "float":
var floatValue float32
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &floatValue)
value = float64(floatValue)
case "int64":
var intValue int64
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
value = float64(intValue)
case "double":
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &value)
} if err != nil {
log.Println(err)
return -1
} *data = C.double(value)
return 0
} //ReadHoldingRegister 一般错误返回 -1 连接失败返回 -3
//export ReadHoldingRegister
func ReadHoldingRegister(slaveId C.int, address C.int, dataType *C.char, data *C.double) C.int {
var results []byte
var err error handler.SlaveId = byte(slaveId) client := modbus.NewClient(handler) dt := C.GoString(dataType)
// startTime := time.Now()
switch dt {
case "int16":
results, err = client.ReadHoldingRegisters(uint16(address), 1)
case "int32", "float":
results, err = client.ReadHoldingRegisters(uint16(address), 2)
case "int64", "double":
results, err = client.ReadHoldingRegisters(uint16(address), 4)
}
// endTime := time.Now() // // 计算时间差
// duration := endTime.Sub(startTime)
// fmt.Printf("Takes: %v\n", duration)
if err != nil {
switch {
case strings.Contains(err.Error(), "connection"):
log.Println("Connection error:", err)
return -3
case strings.Contains(err.Error(), "timeout"):
log.Println("Timeout error:", err)
return -1
default:
log.Println("Other error:", err)
return -1
}
} var value float64
switch dt {
case "int16":
var intValue int16
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
value = float64(intValue)
case "int32":
var intValue int32
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
value = float64(intValue)
case "float":
var floatValue float32
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &floatValue)
value = float64(floatValue)
case "int64":
var intValue int64
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
value = float64(intValue)
case "double":
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &value)
} if err != nil {
log.Println(err)
return -1
} *data = C.double(value)
return 0
} //export WriteRegister
func WriteRegister(slaveId C.int, address C.int, dataType *C.char, value C.double) C.int {
var err error handler.SlaveId = byte(slaveId)
client := modbus.NewClient(handler) dt := C.GoString(dataType) switch dt {
case "int16":
var intValue int16 = int16(value)
_, err = client.WriteSingleRegister(uint16(address), uint16(intValue))
case "int32":
var intValue int32 = int32(value)
results := make([]byte, 4)
binary.BigEndian.PutUint32(results, uint32(intValue))
_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
case "float":
var floatValue float32 = float32(value)
results := make([]byte, 4)
binary.BigEndian.PutUint32(results, math.Float32bits(floatValue))
_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
case "int64":
var intValue int64 = int64(value)
results := make([]byte, 8)
binary.BigEndian.PutUint64(results, uint64(intValue))
_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
case "double":
var doubleValue float64 = float64(value)
results := make([]byte, 8)
binary.BigEndian.PutUint64(results, math.Float64bits(doubleValue))
_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
default:
return -1
} if err != nil {
switch {
case strings.Contains(err.Error(), "connection"):
log.Println("Connection error:", err)
return -3
case strings.Contains(err.Error(), "timeout"):
log.Println("Timeout error:", err)
return -1
default:
log.Println("Other error:", err)
return -1
}
}
return 0
} func main() {}
  • Modbus RTU示例
package main

/*
#include <stdlib.h>
*/
import (
"C"
"bytes"
"encoding/binary"
"log"
"math"
"time" "github.com/goburrow/modbus"
)
import "strings" var handler *modbus.RTUClientHandler //export Connect
func Connect(port *C.char, baudrate C.int) C.int {
log.Printf("Connect ...")
handler = modbus.NewRTUClientHandler(C.GoString(port))
handler.BaudRate = int(baudrate)
handler.DataBits = 8
handler.Parity = "N"
handler.StopBits = 1
handler.Timeout = 250 * time.Millisecond
err := handler.Connect()
if err != nil {
log.Printf("Connect error: %v", err)
return -1
}
return 0
} //export Close
func Close() C.int {
log.Printf("Close ...")
err := handler.Close()
if err != nil {
log.Printf("Close error: %v", err)
return -1
}
return 0
} //ReadRegister 一般错误返回 -1 连接失败返回 -3
//export ReadRegister
func ReadRegister(slaveId C.int, address C.int, dataType *C.char, data *C.double) C.int {
var results []byte
var err error handler.SlaveId = byte(slaveId) client := modbus.NewClient(handler) dt := C.GoString(dataType) switch dt {
case "int16":
results, err = client.ReadInputRegisters(uint16(address), 1)
case "int32", "float":
results, err = client.ReadInputRegisters(uint16(address), 2)
case "int64", "double":
results, err = client.ReadInputRegisters(uint16(address), 4)
} if err != nil {
switch {
case strings.Contains(err.Error(), "connection"):
log.Println("Connection error:", err)
return -3
case strings.Contains(err.Error(), "timeout"):
log.Println("Timeout error:", err)
return -1
default:
log.Println("Other error:", err)
return -1
}
} var value float64
switch dt {
case "int16":
var intValue int16
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
value = float64(intValue)
case "int32":
var intValue int32
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
value = float64(intValue)
case "float":
var floatValue float32
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &floatValue)
value = float64(floatValue)
case "int64":
var intValue int64
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
value = float64(intValue)
case "double":
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &value)
} if err != nil {
log.Println(err)
return -1
} *data = C.double(value)
return 0
} //ReadHoldingRegister 一般错误返回 -1 连接失败返回 -3
//export ReadHoldingRegister
func ReadHoldingRegister(slaveId C.int, address C.int, dataType *C.char, data *C.double) C.int {
var results []byte
var err error handler.SlaveId = byte(slaveId) client := modbus.NewClient(handler) dt := C.GoString(dataType)
// startTime := time.Now()
switch dt {
case "int16":
results, err = client.ReadHoldingRegisters(uint16(address), 1)
case "int32", "float":
results, err = client.ReadHoldingRegisters(uint16(address), 2)
case "int64", "double":
results, err = client.ReadHoldingRegisters(uint16(address), 4)
}
// endTime := time.Now() // // 计算时间差
// duration := endTime.Sub(startTime)
// fmt.Printf("Takes: %v\n", duration)
if err != nil {
switch {
case strings.Contains(err.Error(), "connection"):
log.Println("Connection error:", err)
return -3
case strings.Contains(err.Error(), "timeout"):
log.Println("Timeout error:", err)
return -1
default:
log.Println("Other error:", err)
return -1
}
} var value float64
switch dt {
case "int16":
var intValue int16
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
value = float64(intValue)
case "int32":
var intValue int32
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
value = float64(intValue)
case "float":
var floatValue float32
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &floatValue)
value = float64(floatValue)
case "int64":
var intValue int64
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
value = float64(intValue)
case "double":
err = binary.Read(bytes.NewReader(results), binary.BigEndian, &value)
} if err != nil {
log.Println(err)
return -1
} *data = C.double(value)
return 0
} //export WriteRegister
func WriteRegister(slaveId C.int, address C.int, dataType *C.char, value C.double) C.int {
var err error handler.SlaveId = byte(slaveId)
client := modbus.NewClient(handler) dt := C.GoString(dataType) switch dt {
case "int16":
var intValue int16 = int16(value)
_, err = client.WriteSingleRegister(uint16(address), uint16(intValue))
case "int32":
var intValue int32 = int32(value)
results := make([]byte, 4)
binary.BigEndian.PutUint32(results, uint32(intValue))
_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
case "float":
var floatValue float32 = float32(value)
results := make([]byte, 4)
binary.BigEndian.PutUint32(results, math.Float32bits(floatValue))
_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
case "int64":
var intValue int64 = int64(value)
results := make([]byte, 8)
binary.BigEndian.PutUint64(results, uint64(intValue))
_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
case "double":
var doubleValue float64 = float64(value)
results := make([]byte, 8)
binary.BigEndian.PutUint64(results, math.Float64bits(doubleValue))
_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
default:
return -1
} if err != nil {
switch {
case strings.Contains(err.Error(), "connection"):
log.Println("Connection error:", err)
return -3
case strings.Contains(err.Error(), "timeout"):
log.Println("Timeout error:", err)
return -1
default:
log.Println("Other error:", err)
return -1
}
}
return 0
} func main() {}

编译动态链接库

Go语言支持交叉编译成各个平台的二进制运行程序,但是Cgo不支持,可以去寻找第三方库去处理这个问题,这里选择用Docker容器

  • 编译命令

windows下是modbus.dll,Linux下是 modbus.so,Macos下是 modbus.dylib

go build -o modbus.so -buildmode=c-shared main.go
  • 其他平台编译,比如树莓派,选择使用docker容器
这里找一个 arm32v7 的docker容器,可以通过 docker search arm32v7的方式去搜索,我是试了几个选了个node版本的容器,
不同版本里面的 GLIBC 可能不一样,需要先查看你的树莓派上 GLIBC版本,通过命令 strings /lib/*/libc.so.* | grep GLIBC,
根据版本我选择了node v16的版本下载镜像
  • 下载镜像、创建容器

docker run -itd --name raspberry arm32v7/node:16

  • 进入容器、安装go环境
wget -e http_proxy=127.0.0.1:10809  https://studygolang.com/dl/golang/go1.17.linux-armv6l.tar.gz
tar -zxvf go1.17.linux-armv6l.tar.gz go/
mv go /usr/local/go
# 安装vim
apt-get update
apt-get install vim -y
# 加入系统环境
vim /etc/profile # 加入内容到最后一行 export PATH=$PATH:/usr/local/go/bin
source /etc/profile # 每次进入容器都要执行一次,要是没有找到go命令的话
  • 将Go代码文件拷贝至容器
docker cp modbus_dir raspberry:/  # 将modbus_dir拷贝至容器raspberry的 / 目录下
  • 进入容器编译动态链接库
docker exec -it raspberry /bin/bash
cd modbus_dir
go build -o modbus.so -buildmode=c-shared main.go
  • 将编译后的动态链接库拷贝到宿主机
exit # 退出容器
docker cp raspberry:/modbus_dir/modbus.so ./ # 拷贝至当前目录

应用示例

  • Python

modbus tcp示例

import ctypes
import platform # 加载共享库
print(platform.system())
if platform.system() == "Windows":
modbus = ctypes.CDLL('./modbus.dll')
elif platform.system() == "Darwin":
modbus = ctypes.CDLL('./modbus.dylib')
else: # 树莓派
modbus = ctypes.CDLL('./modbus.so') # 定义函数参数类型和返回类型
modbus.ReadRegister.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_double)]
modbus.ReadRegister.restype = ctypes.c_int modbus.WriteRegister.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_double]
modbus.WriteRegister.restype = ctypes.c_int modbus.ReadHoldingRegister.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_double)]
modbus.ReadHoldingRegister.restype = ctypes.c_int modbus.Connect.argtypes = [ctypes.c_char_p, ctypes.c_int]
modbus.Connect.restype = ctypes.c_int modbus.Close.argtypes = []
modbus.Close.restype = ctypes.c_int # 调用ReadRegister函数
modbus.Connect(b'192.168.1.138', 502) # 写控制寄存器
for i in range(50):
data = ctypes.c_double()
result = modbus.WriteRegister(1, 30, b'float', 123.45 + i)
if result == -3:
print('Connect error.')
if result == 0:
print('Write success.') # 读控制寄存器 | 读输入寄存器时改成 ReadRegister
for i in range(50):
data = ctypes.c_double()
result = modbus.ReadHoldingRegister(1, 30, b'int16', ctypes.byref(data))
print('Read Result:', result, data.value)
modbus.Close()

modbus rtu示例

import ctypes
import platform # 加载共享库
print(platform.system())
if platform.system() == "Windows":
modbus = ctypes.CDLL('./modbus-rtu.dll')
elif platform.system() == "Darwin":
modbus = ctypes.CDLL('./modbus-rtu.dylib')
else: # 树莓派
modbus = ctypes.CDLL('./modbus-rtu.so') # 定义函数参数类型和返回类型
modbus.ReadRegister.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_double)]
modbus.ReadRegister.restype = ctypes.c_int modbus.WriteRegister.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_double]
modbus.WriteRegister.restype = ctypes.c_int modbus.ReadHoldingRegister.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_double)]
modbus.ReadHoldingRegister.restype = ctypes.c_int modbus.Connect.argtypes = [ctypes.c_char_p, ctypes.c_int]
modbus.Connect.restype = ctypes.c_int modbus.Close.argtypes = []
modbus.Close.restype = ctypes.c_int # 调用ReadRegister函数
modbus.Connect(b'COM2', 9600) # 写控制寄存器
for i in range(50):
data = ctypes.c_double()
result = modbus.WriteRegister(1, 30, b'float', 123.45 + i)
if result == -3:
print('Connect error.')
if result == 0:
print('Write success.') # 读控制寄存器 | 读输入寄存器时改成 ReadRegister
for i in range(50):
data = ctypes.c_double()
result = modbus.ReadHoldingRegister(1, 30, b'int16', ctypes.byref(data))
print('Read Result:', result, data.value)
modbus.Close()
  • Node.js

这是我在electron进行软件开发过程中封装的函数

调用顺序: DefineModbus -> connectModbus -> writeRegisterAsync/readRegisterAsync/readHoldingRegisterAsync -> connectModbus

const ffi = require('ffi-napi');
const ref = require('ref-napi');
const double = ref.types.double;
const doublePtr = ref.refType(double);
const path = require('path');
import {checkPort} from './net'; let dllPath: string; console.log(`当前平台: ${process.platform}`) // darwin let dllName = 'modbus.dll';
if (process.platform === 'darwin') {
dllName = 'modbus.dylib';
} if (process.env.MODE === 'development') {
dllPath = path.resolve(`extraResources/dlls/${dllName}`);
} else {
dllPath = path.join(process.resourcesPath, 'extraResources', 'dlls', dllName);
} let modbus: any; const closeModbus = () => {
return new Promise((resolve, reject) => {
modbus.Close.async(
(err: any, result: any) => {
if (err) {
reject(err);
} else {
resolve(result);
}
},
);
});
}; const connectModbus = (ip: string, port: number) => {
return new Promise((resolve, reject) => {
modbus.Connect.async(
ip,
port,
(err: any, result: any) => {
if (err) {
reject(err);
} else {
resolve(result);
}
},
);
});
}; const writeRegisterAsync = (
slaveId: number,
startAddress: number,
dataType: string,
val: number,
) => {
return new Promise((resolve, reject) => {
modbus.WriteRegister.async(
slaveId,
startAddress,
dataType,
val,
(err: any, result: any) => {
if (err) {
reject(err);
} else {
resolve(result);
}
},
);
});
}; const readRegisterAsync = (
slaveId: number,
startAddress: number,
dataType: string,
data: any,
) => {
return new Promise((resolve, reject) => {
modbus.ReadRegister.async(
slaveId,
startAddress,
dataType,
data,
(err: any, result: any) => {
if (err) {
reject(err);
} else {
resolve(result);
}
},
);
});
}; const readHoldingRegisterAsync = (
slaveId: number,
startAddress: number,
dataType: string,
data: any,
) => {
return new Promise((resolve, reject) => {
modbus.ReadHoldingRegister.async(
slaveId,
startAddress,
dataType,
data,
(err: any, result: any) => {
if (err) {
reject(err);
} else {
resolve(result);
}
},
);
});
}; /**
* modbus-serial库的使用
*/
export const ConnectModbus = async (ip: string, port: number) => {
try {
const result = await connectModbus(ip, port);
return [result, 0];
} catch (e) {
return [-1, 0];
}
} export const CloseModbus = async () => {
try {
const result = await closeModbus();
return [result, 0];
} catch(e) {
return [-1, 0];
}
} export const ReadByModbusTcp = async (
slaveId: number,
startAddress: number,
dataType: string,
method: string = 'Input',
) => {
const data = ref.alloc(double);
try {
let result: any;
if (method === 'Input') {
result = await readRegisterAsync(slaveId, startAddress, dataType, data);
} else {
result = await readHoldingRegisterAsync(slaveId, startAddress, dataType, data);
}
return [result, data.deref() as number];
} catch (e) {
console.log(`e :${e}`);
return [0, 0];
}
} export const WriteByModbusTcp = async (
slaveId: number,
startAddress: number,
dataType: string,
val: number,
) => {
try {
const result: any = await writeRegisterAsync(
slaveId,
startAddress,
dataType,
val,
);
return [result, val];
} catch (e) {
return [0, 0];
}
} export const DefineModbus = async () => {
try {
modbus = ffi.Library(dllPath, {
ReadRegister: ['int', ['int', 'int', 'string', doublePtr]],
ReadHoldingRegister: ['int', ['int', 'int', 'string', doublePtr]],
WriteRegister: ['int', ['int', 'int', 'string', 'double']],
Connect: ['int', ['string', 'int']],
Close: ['int', []],
});
console.log('Define Modbus Success.');
} catch (e) {
console.log(`defineModbus error: ${e}`);
}
return dllPath;
} export const CheckConnected = async (ip: string, port: number) => {
try {
const result = await checkPort(ip, port);
return true;
} catch (e) {
return false;
}
}

最后

这里只写了 读写控制寄存器、读输入寄存器,支持数据类型 int16/int32/int64/float/double

可以根据需要加入自己的代码,比如线圈的读写控制设备启停等

动态链接库、源码地址

链接: https://pan.baidu.com/s/1q40gHnftqzGJtUgfws_sdw 提取码: 72o2

Modbus动态链接库供多语言使用 | Go的更多相关文章

  1. golang生成c-shared so供c语言或者golang调用到例子

    1.golang生成c-shared类型到so 建立文件夹hello,创建main.go文件,内容如下 package main import "C" func main() {} ...

  2. Matlab生成动态链接库供C#调用

    1.首先在Matlab中编写一个或几个.m文件 2.然后在命令空间中输入命令:deploytool 3.修改工程名称,修改需要生成文件后缀 4.添加类,添加文件,然后点击生成.

  3. 新手学python(2):C语言调用完成数据库操作

    继续介绍本人的python学习过程.本节介绍如何利用python调用c代码.内容还是基于音乐信息提取的过程,架构如图一.Python调用c实现的功能是利用python访问c语言完成mysql数据库操作 ...

  4. c#下调用dll动态链接库[转]

    C# 调用传统的 API 动态链接库,是.NET开发经常被讨论的问题. 比如有这么一个动态链接库(delphi 语言): library DelphiDLL; uses SysUtils, Class ...

  5. 《30天自制操作系统》笔记(02)——导入C语言

    <30天自制操作系统>笔记(02)——导入C语言 进度回顾 在上一篇,记录了计算机开机时加载IPL程序(initial program loader,一个nas汇编程序)的情况,包括IPL ...

  6. [自制简单操作系统] 1、从0-1到汇编再到c语言的奥秘

    目录: 1.用0-1编写最简单的操作系统 2.用汇编改写上面0-1程序 2.1 只用DB的汇编改写版  2.2 加入RESB汇编的改写版  2.3 进一步使用汇编替换0-1文件  2.4 核心程序也用 ...

  7. 使用swift语言进行IOS应用开发

    在Swift中能够直接使用Objective-C语言提供的api (包括系统框架与自己的定制代码),也能够在Objective-C中使用Swift提供的类和api ,还能够在一个工程中同时混合使用Sw ...

  8. Sword redis C语言接口介绍

    hiredis安装 hiredis是redis官方推荐的基于C接口的客户端组件,它提供接口,供c语言调用以操作数据库. 在redis的源码包的deps/hiredis下就有它的源码 安装方法,进入de ...

  9. 【go】go语言socket通信样例

    server.go package main import ( "net" "fmt" "io" ) func main() { liste ...

  10. C 语言疑难杂症 [转:http://blog.chinaunix.net/uid-20688544-id-1894880.html]

    无聊在网上找了些C语言的东东练一下手,竟然发现其实还有好多细节之前,没注意到,该好好复习一下先. 解决掉的问题先不发出来,把疑问的先做个笔记,过几天解决了就回来修改补上.   #include < ...

随机推荐

  1. 自研ORM嵌套查询和子查询,强不强大您说了算。

    测试代码 var count = 0; var refAsync = new RefAsync<int>(); //下面示例方法的重载均支持 var query = db.Query< ...

  2. ORM总览

    ORM(Object-Relational Mapping)是一种常见的数据访问技术,它将对象模型和关系模型之间进行映射.ORM的主要作用是简化数据访问和管理,提高开发效率和代码质量.在实际应用中,O ...

  3. 爆肝万字带你超级详细全面了解Linux命令大全

    前言 作者主页:CSDN丨博客园 学习交流:在下周周ovoの社区 对这篇万字博客目录总结如下: 关机命令.重启命令,创建用户.删除用户.修改密码.切换用户.切换到超级用户.禁用/解锁用户账户.修改信息 ...

  4. Zabbix Timeout 设置不当导致的问题

    哈喽大家好,我是咸鱼 今天跟大家分享一个关于 zabbix Timeout 值设置不当导致的问题,这个问题不知道大家有没有碰到过 问题 事情经过是这样的: 把某一台 zabbix agent 的模板由 ...

  5. 2023-06-25:redis中什么是缓存穿透?该如何解决?

    2023-06-25:redis中什么是缓存穿透?该如何解决? 答案2023-06-25: 缓存穿透 缓存穿透指的是查询一个根本不存在的数据,在这种情况下,无论是缓存层还是存储层都无法命中.因此,每次 ...

  6. Self-Instruct 论文解读:利用大模型自己给自己生成指令数据,指令数据自动生成

    总览 大规模"指令调整"的语言模型,即指令微调的LLM,已经表现出非凡的零样本能力,尤其是推广新任务上. 然而,这些模型严重依赖于人类编写的指令数据,而这些数据通常在数量.多样性和 ...

  7. 彻底解决各种浏览器访问不了GitHub问题(注意代理)

    如果有穿墙插件如Google助手  VPN  SS 之类别 有可能被全局代理 首先关闭这些软件 或者浏览器插件 假设,您的本地代理端口为:1080 ,打开git base窗口进行按下列的方式设置.(在 ...

  8. CDMP国际数据治理认证训练营来了(7-8月)

    大家好,我是独孤风,一位曾经的港口煤炭工人,目前在某国企任大数据负责人,公众号大数据流动主理人.在最近的两年的时间里,因为公司的需求,还有大数据的发展趋势所在,我开始学习数据治理的相关知识. 经过一段 ...

  9. Servlet案例:发生错误org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expecte

    20-Jun-2020 20:48:31.466 信息 [http-nio-8080-exec-7] com.alibaba.druid.pool.DruidDataSource.info {data ...

  10. 一文了解 io.Copy 函数

    1. 引言 io.Copy 函数是一个非常好用的函数,能够非常方便得将数据进行拷贝.本文我们将从io.Copy 函数的基本定义出发,讲述其基本使用和实现原理,以及一些注意事项,基于此完成对io.Cop ...