微信消息推送消息加解密(golang)

2023-05-16

本篇介绍如何使用golang对微信消息推送进行加解密,后续会补充,目前先写个原理,大概自己看一下,其他的自己应该也能写。老套路,分为三步,为啥写,教程,总结。懒得看的直接看第二步就行。

为什么突然写这个?

由于项目需要,需要用go做一个微服务,一个微信的授权中心,管理所有微信授权。在微信官方文档没有go的demo,本来想着搜一搜别人写的,直接用,一搜全是csdn里的文章,大概看了一下,都是差不多一致的,csdn目前的常态啊,不得不说,乱且不好用,看了看,还是自己来吧,完全跟着官方的demo逻辑,个人感觉自己写的还是挺清晰的,go自己也接触不久,有啥问题的也欢迎指出。

微信消息加解密

大概分为四个文件

wechat_err_code.go

package wechat

var SuccessCode = 0
var ValidateSignatureError = -40001
var ParseXmlError = -40002
var ComputeSignatureError = -40003
var IllegalAesKey = -40004
var ValidateAppidError = -40005
var EncryptAESError = -40006
var DecryptAESError = -40007
var IllegalBuffer = -40008

message.go(消息)

package wechat

import (
	"bytes"
	"encoding/binary"
	"encoding/xml"
	"errors"
	"strconv"
	"time"
)

var EventTicket = "component_verify_ticket"    //ticket推送
var EventUnauthorized = "unauthorized"         //取消授权
var EventUpdateAuthorized = "updateauthorized" //更新授权
var EventAuthorized = "authorized"             //授权成功

var MsgTypeText = "text"   //文本消息
var MsgTypeImage = "image" //文本消息
var MsgTypeVoice = "voice" //语音消息
var MsgTypeVideo = "Video" //视频消息
var MsgTypeMusic = "music" //音乐消息
var MsgTypeNews = "news"   //图文消息

//EventMessageBody 事件推送
type EventMessageBody struct {
	XMLName                      xml.Name `xml:"xml"`
	AppId                        string   `xml:"AppId" json:"app_id"`
	CreateTime                   int      `xml:"CreateTime" json:"create_time"`
	InfoType                     string   `xml:"InfoType" json:"info_type"`
	ComponentVerifyTicket        string   `xml:"ComponentVerifyTicket" json:"component_verify_ticket"`
	AuthorizerAppid              string   `xml:"AuthorizerAppid" json:"authorizer_appid"`
	AuthorizationCode            string   `xml:"AuthorizationCode" json:"authorization_code"`
	AuthorizationCodeExpiredTime string   `xml:"AuthorizationCodeExpiredTime" json:"authorization_code_expired_time"`
	PreAuthCode                  string   `xml:"PreAuthCode" json:"pre_auth_code"`
}

//MessageBodyDecrypt 消息体
type MessageBodyDecrypt struct {
	XMLName      xml.Name `xml:"xml"`
	ToUserName   string   `xml:"ToUserName"`
	FromUserName string   `xml:"FromUserName"`
	CreateTime   string   `xml:"CreateTime"`
	MsgType      string   `xml:"MsgType"`
	Url          string   `xml:"Url"`
	PicUrl       string   `xml:"PicUrl"`
	MediaId      string   `xml:"MediaId"`
	ThumbMediaId string   `xml:"ThumbMediaId"`
	Content      string   `xml:"Content"`
	MsgId        int      `xml:"MsgId"`
	Location_X   string   `xml:"Location_x"`
	Location_Y   string   `xml:"Location_y"`
	Label        string   `xml:"Label"`
}

type MessageEncryptBody struct {
	XMLName      xml.Name `xml:"xml"`
	Encrypt      CDATA    `xml:"Encrypt"`
	MsgSignature CDATA    `xml:"MsgSignature"`
	TimeStamp    string   `xml:"TimeStamp"`
	Nonce        CDATA    `xml:"Nonce"`
}

type MessageText struct {
	XMLName      xml.Name `xml:"xml"`
	ToUserName   CDATA    `xml:"ToUserName"`
	FromUserName CDATA    `xml:"FromUserName"`
	CreateTime   string   `xml:"CreateTime"`
	MsgType      CDATA    `xml:"MsgType"`
	Content      CDATA    `xml:"Content"`
}

type MessageImage struct {
	XMLName      xml.Name `xml:"xml"`
	ToUserName   CDATA    `xml:"ToUserName"`
	FromUserName CDATA    `xml:"FromUserName"`
	CreateTime   string   `xml:"CreateTime"`
	MsgType      CDATA    `xml:"MsgType"`
	Image        Media    `xml:"Image"`
}

type MessageVoice struct {
	XMLName      xml.Name `xml:"xml"`
	ToUserName   CDATA    `xml:"ToUserName"`
	FromUserName CDATA    `xml:"FromUserName"`
	CreateTime   string   `xml:"CreateTime"`
	MsgType      CDATA    `xml:"MsgType"`
	Voice        Media    `xml:"Voice"`
}

type MessageVideo struct {
	XMLName      xml.Name `xml:"xml"`
	ToUserName   CDATA    `xml:"ToUserName"`
	FromUserName CDATA    `xml:"FromUserName"`
	CreateTime   string   `xml:"CreateTime"`
	MsgType      CDATA    `xml:"MsgType"`
	Video        Video    `xml:"Video"`
}

type MessageMusic struct {
	XMLName      xml.Name `xml:"xml"`
	ToUserName   CDATA    `xml:"ToUserName"`
	FromUserName CDATA    `xml:"FromUserName"`
	CreateTime   string   `xml:"CreateTime"`
	MsgType      CDATA    `xml:"MsgType"`
	Music        Music    `xml:"Music"`
}

type MessageArticle struct {
	XMLName      xml.Name      `xml:"xml"`
	ToUserName   CDATA         `xml:"ToUserName"`
	FromUserName CDATA         `xml:"FromUserName"`
	CreateTime   string        `xml:"CreateTime"`
	MsgType      CDATA         `xml:"MsgType"`
	ArticleCount string        `xml:"ArticleCount"`
	Articles     []ArticleItem `xml:"Articles"`
}

type CDATA struct {
	Text string `xml:",innerxml"`
}

type Media struct {
	MediaId CDATA `xml:"MediaId"`
}

type Video struct {
	MediaId     CDATA `xml:"MediaId"`
	Title       CDATA `xml:"Title"`
	Description CDATA `xml:"Description"`
}

type Music struct {
	Title        CDATA `xml:"Title"`
	Description  CDATA `xml:"Description"`
	MusicUrl     CDATA `xml:"MusicUrl"`
	HQMusicUrl   CDATA `xml:"HQMusicUrl"`
	ThumbMediaId CDATA `xml:"ThumbMediaId"`
}

type ArticleItem struct {
	Title       CDATA `xml:"Title"`
	Description CDATA `xml:"Description"`
	PicUrl      CDATA `xml:"PicUrl"`
	Url         CDATA `xml:"Url"`
}

func FormatMessage(plainText []byte, data interface{}) (*interface{}, error) {
	length := GetMessageLength(plainText)
	err := xml.Unmarshal(plainText[20:20+length], data)
	if err != nil {
		return nil, errors.New("格式化消息失败:format message error")
	}
	return &data, nil
}

func GetMessageLength(plainText []byte) int32 {
	// Read length
	buf := bytes.NewBuffer(plainText[16:20])
	var length int32
	err := binary.Read(buf, binary.BigEndian, &length)
	if err != nil {
		panic("获取消息长度失败:read message length error")
	}
	return length
}

func ValueToCDATA(content string) CDATA {
	return CDATA{"<![CDATA[" + content + "]]>"}
}

//FormatTextMessage 格式化文本消息
func FormatTextMessage(fromUserName string, toUserName string, content string) []byte {
	timestamp := strconv.Itoa(int(time.Now().Unix()))
	textMessage := MessageText{FromUserName: ValueToCDATA(fromUserName), ToUserName: ValueToCDATA(toUserName), Content: ValueToCDATA(content), CreateTime: timestamp, MsgType: ValueToCDATA(MsgTypeText)}
	messageBytes, err := xml.MarshalIndent(textMessage, " ", "  ")
	if err != nil {
		panic("格式化文本消息失败:xml marsha1 error;" + err.Error())
	}
	return messageBytes
}

//FormatImageMessage 格式化图片消息
func FormatImageMessage(fromUserName string, toUserName string, mediaId string) []byte {
	timestamp := strconv.Itoa(int(time.Now().Unix()))
	imageMessage := MessageImage{FromUserName: ValueToCDATA(fromUserName), ToUserName: ValueToCDATA(toUserName), CreateTime: timestamp, MsgType: ValueToCDATA(MsgTypeImage), Image: Media{ValueToCDATA(mediaId)}}
	messageBytes, err := xml.MarshalIndent(imageMessage, " ", "  ")
	if err != nil {
		panic("格式化图片消息失败:xml marsha1 error;" + err.Error())
	}
	return messageBytes
}

//FormatVoiceMessage 格式语音消息
func FormatVoiceMessage(fromUserName string, toUserName string, mediaId string) []byte {
	timestamp := strconv.Itoa(int(time.Now().Unix()))
	voiceMessage := MessageVoice{FromUserName: ValueToCDATA(fromUserName), ToUserName: ValueToCDATA(toUserName), CreateTime: timestamp, MsgType: ValueToCDATA(MsgTypeVoice), Voice: Media{ValueToCDATA(mediaId)}}
	messageBytes, err := xml.MarshalIndent(voiceMessage, " ", "  ")
	if err != nil {
		panic("格式化语音消息失败:xml marsha1 error;" + err.Error())
	}
	return messageBytes
}

//FormatVideoMessage 格式化视频消息
func FormatVideoMessage(fromUserName string, toUserName string, mediaId string, title string, description string) []byte {
	timestamp := strconv.Itoa(int(time.Now().Unix()))
	videoMessage := MessageVideo{FromUserName: ValueToCDATA(fromUserName), ToUserName: ValueToCDATA(toUserName), CreateTime: timestamp, MsgType: ValueToCDATA(MsgTypeVideo), Video: Video{
		MediaId:     ValueToCDATA(mediaId),
		Title:       ValueToCDATA(title),
		Description: ValueToCDATA(description),
	}}
	messageBytes, err := xml.MarshalIndent(videoMessage, " ", "  ")
	if err != nil {
		panic("格式化语音消息失败:xml marsha1 error;" + err.Error())
	}
	return messageBytes
}

//FormatMusicMessage 格式化音乐消息
func FormatMusicMessage(fromUserName string, toUserName string, thumbMediaId string, title string, description string, musicUrl string, hQMusicUrl string) []byte {
	timestamp := strconv.Itoa(int(time.Now().Unix()))
	musicMessage := MessageMusic{FromUserName: ValueToCDATA(fromUserName), ToUserName: ValueToCDATA(toUserName), CreateTime: timestamp, MsgType: ValueToCDATA(MsgTypeMusic), Music: Music{
		Title:        ValueToCDATA(title),
		Description:  ValueToCDATA(description),
		MusicUrl:     ValueToCDATA(musicUrl),
		HQMusicUrl:   ValueToCDATA(hQMusicUrl),
		ThumbMediaId: ValueToCDATA(thumbMediaId),
	}}
	messageBytes, err := xml.MarshalIndent(musicMessage, " ", "  ")
	if err != nil {
		panic("格式化音乐消息失败:xml marsha1 error;" + err.Error())
	}
	return messageBytes
}

//FormatArticlesMessage 格式化音乐消息
func FormatArticlesMessage(fromUserName string, toUserName string, items []ArticleItem) []byte {
	timestamp := strconv.Itoa(int(time.Now().Unix()))
	articleNum := strconv.Itoa(len(items))
	musicMessage := MessageArticle{FromUserName: ValueToCDATA(fromUserName), ToUserName: ValueToCDATA(toUserName), CreateTime: timestamp, MsgType: ValueToCDATA(MsgTypeNews), Articles: items, ArticleCount: articleNum}
	messageBytes, err := xml.MarshalIndent(musicMessage, " ", "  ")
	if err != nil {
		panic("格式化图文消息失败:xml marsha1 error;" + err.Error())
	}
	return messageBytes
}

//FormatEncryptData 格式化微信加密数据
func FormatEncryptData(encrypted string, token string) MessageEncryptBody {
	nonce := makeRandomString(16)
	timestamp := strconv.Itoa(int(time.Now().Unix()))
	data := MessageEncryptBody{Encrypt: ValueToCDATA(encrypted), Nonce: ValueToCDATA(nonce), MsgSignature: ValueToCDATA(GetSignature(timestamp, nonce, encrypted, token)), TimeStamp: timestamp}
	return data
}

wechat_message.go

package wechat

import (
	"bytes"
	"encoding/base64"
	"encoding/binary"
	"encoding/xml"
	"errors"
	"fmt"
)

type msgCrypt struct {
	token  string
	aesKey string
	appid  string
}

//EventEncryptRequest 微信事件推送结构体
type EventEncryptRequest struct {
	XMLName xml.Name `xml:"xml"`
	Encrypt string   `xml:"Encrypt"`
	Appid   string   `xml:"Appid"`
}

//MessageEncryptRequest 微信消息加密结构体
type MessageEncryptRequest struct {
	XMLName      xml.Name `xml:"xml"`
	Encrypt      string   `xml:"Encrypt"`
	MsgSignature string   `xml:"MsgSignature"`
	TimeStamp    string   `xml:"TimeStamp"`
	Nonce        string   `xml:"Nonce"`
}

//NewWechatMsgCrypt 实例化微信加解密
func NewWechatMsgCrypt(token string, aesKey string, appid string) *msgCrypt {
	instance := new(msgCrypt)
	instance.token = token
	instance.aesKey = aesKey
	instance.appid = appid
	return instance
}

//WechatEventDecrypt 微信事件推送解密
func (w *msgCrypt) WechatEventDecrypt(eventRequest EventEncryptRequest, msgSignature string, timestamp, nonce string) interface{} {
	errCode, data := w.decryptMsg(msgSignature, timestamp, nonce, eventRequest.Encrypt)
	if errCode != SuccessCode {
		panic(fmt.Sprintf("消息解密失败,code:%d", errCode))
	}
	message := EventMessageBody{}
	_, err := FormatMessage(data, &message)
	if err != nil {
		panic(fmt.Sprintf("消息格式化失败,%s", err.Error()))
	}
	return message
}

//WechatMessageDecrypt 微信消息解密
func (w *msgCrypt) WechatMessageDecrypt(messageEncryptRequest MessageEncryptRequest) interface{} {
	errCode, data := w.decryptMsg(messageEncryptRequest.MsgSignature, messageEncryptRequest.TimeStamp, messageEncryptRequest.Nonce, messageEncryptRequest.Encrypt)
	if errCode != SuccessCode {
		panic(fmt.Sprintf("消息解密失败,code:%d", errCode))
	}
	message := MessageBodyDecrypt{}
	_, err := FormatMessage(data, &message)
	if err != nil {
		panic(fmt.Sprintf("消息格式化失败,%s", err.Error()))
	}
	return message
}

//WechatTextMessage 微信文本消息加密
func (w *msgCrypt) WechatTextMessage(fromUserName string, toUserName string, content string) MessageEncryptBody {
	message := FormatTextMessage(fromUserName, toUserName, content)
	encrypted, err := w.encryptMsg(message)
	if err != nil {
		panic("消息加密失败:" + err.Error())
	}
	data := FormatEncryptData(encrypted, w.token)
	return data
}

//WechatImageMessage 微信图片消息加密
func (w *msgCrypt) WechatImageMessage(fromUserName string, toUserName string, mediaId string) MessageEncryptBody {
	message := FormatImageMessage(fromUserName, toUserName, mediaId)
	encrypted, err := w.encryptMsg(message)
	if err != nil {
		panic("消息加密失败:" + err.Error())
	}
	data := FormatEncryptData(encrypted, w.token)
	return data
}

//WechatVoiceMessage 微信语音消息加密
func (w *msgCrypt) WechatVoiceMessage(fromUserName string, toUserName string, mediaId string) MessageEncryptBody {
	message := FormatVoiceMessage(fromUserName, toUserName, mediaId)
	encrypted, err := w.encryptMsg(message)
	if err != nil {
		panic("消息加密失败:" + err.Error())
	}
	data := FormatEncryptData(encrypted, w.token)
	return data
}

//WechatVideoMessage 微信视频消息加密
func (w *msgCrypt) WechatVideoMessage(fromUserName string, toUserName string, mediaId string, title string, description string) MessageEncryptBody {
	message := FormatVideoMessage(fromUserName, toUserName, mediaId, title, description)
	encrypted, err := w.encryptMsg(message)
	if err != nil {
		panic("消息加密失败:" + err.Error())
	}
	data := FormatEncryptData(encrypted, w.token)
	return data
}

//WechatMusicMessage 微信音乐消息加密
func (w *msgCrypt) WechatMusicMessage(fromUserName string, toUserName string, thumbMediaId string, title string, description string, musicUrl string, hQMusicUrl string) MessageEncryptBody {
	message := FormatMusicMessage(fromUserName, toUserName, thumbMediaId, title, description, musicUrl, hQMusicUrl)
	encrypted, err := w.encryptMsg(message)
	if err != nil {
		panic("消息加密失败:" + err.Error())
	}
	data := FormatEncryptData(encrypted, w.token)
	return data
}

//WechatArticlesMessage 微信图文消息加密
func (w *msgCrypt) WechatArticlesMessage(fromUserName string, toUserName string, items []ArticleItem) MessageEncryptBody {
	message := FormatArticlesMessage(fromUserName, toUserName, items)
	encrypted, err := w.encryptMsg(message)
	if err != nil {
		panic("消息加密失败:" + err.Error())
	}
	data := FormatEncryptData(encrypted, w.token)
	return data
}

//decryptMsg aes消息解密
func (w *msgCrypt) decryptMsg(msgSignature string, timestamp, nonce string, encrypted string) (int, []byte) {
	//验证aes
	if len(w.aesKey) != 43 {
		return IllegalAesKey, nil
	}
	//判断签名是否一致
	if err := w.validSignature(msgSignature, timestamp, nonce, encrypted); err != nil {
		return ValidateSignatureError, nil
	}
	//解密
	prp := NewPrpCrypt(w.aesKey)
	plainText, err := prp.decrypt(encrypted)
	if err != nil {
		return DecryptAESError, nil
	}
	//验证appid是否一致(消息来源是否一致)
	if err := w.validMessageSource(plainText); err != nil {
		return ValidateAppidError, nil
	}
	return SuccessCode, plainText
}

//encryptMsg aes消息加密
func (w *msgCrypt) encryptMsg(message []byte) (string, error) {
	//计算消息长度
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, int32(len(message)))
	if err != nil {
		return "", err
	}
	messageLength := buf.Bytes()
	//生成随机字符串
	randBytes := []byte(makeRandomString(16))
	plainData := bytes.Join([][]byte{randBytes, messageLength, message, []byte(w.appid)}, nil)
	prp := NewPrpCrypt(w.aesKey)
	//消息加密
	encrypted, err := prp.encrypt(plainData)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(encrypted), nil
}

//validSignature 验证签名是否一致
func (w *msgCrypt) validSignature(msgSignature string, timestamp, nonce string, encrypted string) error {
	validSignature := GetSignature(timestamp, nonce, encrypted, w.token)
	if validSignature != msgSignature {
		return errors.New("签名不一致:valid sign error")
	}
	return nil
}

//validMessageSource 验证消息来源
func (w *msgCrypt) validMessageSource(plainText []byte) error {
	messageLength := GetMessageLength(plainText)
	//获取appid位置
	appIdStartPos := 20 + messageLength
	id := plainText[appIdStartPos : int(appIdStartPos)+len(w.appid)]
	if string(id) != w.appid {
		return errors.New("消息来源不一致:Appid is invalid")
	}
	return nil
}

prpcrypt.go

package wechat

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"io"
)

type prpCrypt struct {
	Key []byte
	Iv  []byte
}

func NewPrpCrypt(aesKey string) *prpCrypt {
	instance := new(prpCrypt)
	//网络字节序
	instance.Key, _ = base64.StdEncoding.DecodeString(aesKey + "=")
	instance.Iv = randomIv()
	return instance
}

func randomIv() []byte {
	iv := make([]byte, aes.BlockSize)
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic("random iv error")
	}
	return iv
}

func (prp *prpCrypt) decrypt(encrypted string) ([]byte, error) {
	encryptedBytes, _ := base64.StdEncoding.DecodeString(encrypted)
	k := len(prp.Key) //PKCS#7
	if len(encryptedBytes)%k != 0 {
		panic("ciphertext size is not multiple of aes key length")
	}
	block, err := aes.NewCipher(prp.Key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, prp.Iv)
	plainText := make([]byte, len(encryptedBytes))
	blockMode.CryptBlocks(plainText, encryptedBytes)
	return plainText, nil
}

func (prp *prpCrypt) encrypt(plainText []byte) ([]byte, error) {
	k := len(prp.Key)
	if len(plainText)%k != 0 {
		plainText = pKCS7Pad(plainText, k)
	}
	block, err := aes.NewCipher(prp.Key)
	if err != nil {
		return nil, err
	}
	cipherData := make([]byte, len(plainText))
	blockMode := cipher.NewCBCEncrypter(block, prp.Iv)
	blockMode.CryptBlocks(cipherData, plainText)
	return cipherData, nil
}


helpers.go(工具)

package wechat

import (
	"bytes"
	"crypto/sha1"
	"fmt"
	"io"
	"math/rand"
	"sort"
	"strings"
)

//GetSignature 获取签名
func GetSignature(timestamp, nonce string, encrypted string, token string) string {
	data := []string{
		encrypted,
		token,
		timestamp,
		nonce,
	}
	sort.Strings(data)
	s := sha1.New()
	_, err := io.WriteString(s, strings.Join(data, ""))
	if err != nil {
		panic("签名错误:sign error")
	}
	return fmt.Sprintf("%x", s.Sum(nil))
}

func makeRandomString(length int) string {
	randStr := ""
	strSource := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyl"
	maxLength := len(strSource) - 1
	for i := 0; i < length; i++ {
		randomNum := rand.Intn(maxLength)
		randStr += strSource[randomNum : randomNum+1]
	}
	return randStr
}

func pKCS7Pad(plainText []byte, blockSize int) []byte {
	// block size must be bigger or equal 2
	if blockSize < 1<<1 {
		panic("block size is too small (minimum is 2 bytes)")
	}
	// block size up to 255 requires 1 byte padding
	if blockSize < 1<<8 {
		// calculate padding length
		padLen := padLength(len(plainText), blockSize)

		// define PKCS7 padding block
		padding := bytes.Repeat([]byte{byte(padLen)}, padLen)

		// apply padding
		padded := append(plainText, padding...)
		return padded
	}
	// block size bigger or equal 256 is not currently supported
	panic("unsupported block size")
}

func padLength(sliceLength, blockSize int) int {
	padLen := blockSize - sliceLength%blockSize
	if padLen == 0 {
		padLen = blockSize
	}
	return padLen
}

最后加一个demo运行

instance := wechat.NewWechatMsgCrypt("token", "aesKey", "appid")
data := instance.WechatTextMessage("示例内容", "示例内容2", "你好")
fmt.Println("加密结果:", data)
bytes, _ := xml.Marshal(data)
xmlData := wechat.MessageEncryptRequest{}
_ = xml.Unmarshal(bytes, &xmlData)
fmt.Println("解析结果:", xmlData)
fmt.Println("解密结果:", instance.WechatMessageDecrypt(xmlData))

总结

思路还是挺清晰的,大概就这样,没啥特别,这样用起来清晰点,应该不难看懂,有问题再提出来,不想一搜全是那些,看着头疼,这样我还怎么面向百度编程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

微信消息推送消息加解密(golang) 的相关文章

  • 车路协调场景与演进与V2X SDK技术解析

    车路协调场景与演进与V2X SDK技术解析 xff1a 回看链接 https apposcmf8kb5033 h5 xiaoeknow com content page eyJ0eXBlIjoiMiIsInJlc291cmNlX3R5cGU
  • 新的开始之Win7、CentOS 6.4 双系统 硬盘安装

    目的 xff1a 在已经有Win7的操作系统上安装CentOS6 4的32位操作系统 本博客结合了以下的博客 http blog csdn net markho365 article details 8969591 http www cnb
  • 详解protobuf-从原理到使用

    这里写的少 xff0c 后面再补充 https www jianshu com p 419efe983cb2
  • signal(SIGCHLD, SIG_IGN)和signal(SIGPIPE, SIG_IGN);

    这个链接写的比较好 xff1a https yq aliyun com articles 42215 signal SIGCHLD SIG IGN 因为并发服务器常常fork很多子进程 xff0c 子进程终结之后需要服务器进程去wait清理
  • Linux c 网络socket编程

    网络编程 xff0c 一定离不开套接字 xff1b 那什么是套接字呢 xff1f 在Linux下 xff0c 所有的I O操作都是通过读写文件描述符而产生的 xff0c 文件描述符是一个和打开的文件相关联的整数 xff0c 这个文件并不只包
  • Linux c 下socket编程全面

    网络的Socket数据传输是一种特殊的I O xff0c Socket也是一种文件描述符 Socket也具有一个类似于打开文件的函数调用Socket xff0c 该函数返回一个整型的Socket描述符 xff0c 随后的连接建立 数据传输等
  • CANoe与金溢的obu can连接的环境问题 Cifconfig can0 up 失败 设置波特率失败

    今天搭建了CANoe与金溢的obu can连接的环境问题 遇到了一个让人不解的问题 can0起不来 xff0c 于是怀疑波特率不匹配 xff0c 使用调不了 Linux 设置波特率 ifconfig can0 down 关闭CAN0 ip
  • V2X-Locate方案,解决隧道内自动车辆定位问题

    2019年3月连网自驾车辆 Connected and Autonomous Vehicles xff0c CAV 通讯技术厂商CohdaWireless于挪威B rum市新建隧道 长达1 3英里 2 2公里 内采用V2X Locate方案
  • 一个高级软件工程师面试被问的问题

    使用new和malloc如何解决内存碎片问题 xff1f 多进程间通信几种方式 xff0c 你用过几种方式 xff1f 线程间通信 xff0c 用过几种方式 分不同的场景 xff0c 适合用哪种通信方式 内存管理 xff0c 如果让你来实现
  • 面试时你需要问HR什么问题?

    与职位相关的问题要多问 xff0c 如 xff1a 1 我知道该职位的首要职责 xff0c 但公司有没有其他的要求 xff1f 2 我的专长是XX xff0c 请问XX部门在公司占有什么样的位置 xff1f 6 面试之后的安排都是什么 xf
  • Linux中断实现浅析

    本文描述内容针对2 6 31 43 x86平台 xff0c 不包含硬件相关的细节 作者 xff1a 独孤九贱 xff1b 版权所有 xff0c 转载请注明出处 有问题欢迎与我交流讨论 一 概述 中断 xff0c 本质上是一个电信号 xff0
  • Linux常用命令

    应用patch patch p1 lt test1 patch 卸载patch patch Rp1 lt test1 patch
  • 【jetson nano、NX、TX2 开机自启动程序,开机自动解除硬件限制,开机默认最大性能工作。】

    jetson nano NX TX2 开机自启动程序 xff0c 开机自动解除硬件限制 xff0c 开机默认最大性能工作 jetson nano NX TX2是英伟达开发的边缘平台 xff0c 其良好的性能 亲民的价格非常适合部署深度学习模
  • 蒙特卡洛法(一)

    蒙特卡洛法也成为统计模拟方法 xff0c 通过从概率模型的随机抽样进行近似数值计算的方法 马尔科夫链蒙特卡洛法则是以马尔科夫链为概率模型的蒙特卡洛法 xff0c 构建一个马尔科夫链 xff0c 使其平稳分布就是要进行抽样的分布 xff0c
  • 芯片测试术语 ,片内测试(BIST),ATE测试

    芯片测试分为如下几类 xff1a 1 WAT xff1a Wafer AcceptanceTest xff0c wafer level 的管芯或结构测试 xff1b 2 CP xff1a chip probing xff0c wafer l
  • SBUS协议及编解码

    1 简介 SBUS本质是一种串口通信协议 xff0c 采用100K的波特率 xff0c 8位数据位 xff0c 两位停止位 xff0c 偶效验 xff0c 即8E2的串口通信 值得注意的有三点 xff1a 1 SBUS采用负逻辑 xff0c
  • Linux内核跨模块函数调用:EXPORT_SYMBOL()宏定义

    一 查看内核驱动代码你会发现很多的函数带有EXPORT SYMBOL 宏定义 二 那么EXPORT SYMBOL的作用是什么 xff1f EXPORT SYMBOL标签内定义的函数或者符号对全部内核代码公开 xff0c 不用修改内核代码就可
  • linux内核I2C子系统详解

    1 I2C通信协议 参考博客 xff1a I2C通信协议详解和通信流程分析 xff1b https csdnimg cn release blogv2 dist pc themesSkin skin3 template images bg
  • 内核驱动中断申请类型及函数分析

    ret 61 request irq chip gt irq xxx intr handler IRQF TRIGGER FALLING IRQF NO THREAD IRQF NO SUSPEND name chip 上面是中断初始化中调
  • I2C设备注册的4种方法

    文章目录 前言一 静态注册二 动态注册三 用户空间注册四 i2c驱动扫描注册 前言 I2C设备的4种添加方法 xff1a 1 xff09 静态注册 2 xff09 动态注册 3 xff09 用户空间注册 4 xff09 i2c驱动扫描注册

随机推荐

  • pm_wakeup.h

    pm wakeup h Power management wakeup interface Copyright C 2008 Alan Stern Copyright C 2010 Rafael J Wysocki Novell Inc T
  • GTK+ Reference Manual

    GTK 43 Reference Manual for GTK 43 2 6 2 Table of Contents I GTK 43 Overview Compiling the GTK 43 libraries How to compi
  • Linux获取进程列表

    实现思路是 xff1a 遍历 proc目录下的所有进程描述文件夹 xff0c 从而获取进程列表 代码如下 xff1a include lt stdio h gt include lt dirent h gt include lt unist
  • ubuntu18.04 下firefox 不能 播放视频,因为默认未安装FLASH插件。(当然只是原因之一)

    ubuntu18 04 下firefox 不能 播放视频 xff0c 默认未安装FLASH插件 终端输入 xff1a sudo apt get install flashplugin nonfree
  • Ubuntu上可使用的15个桌面环境

    Ubuntu上可使用的15个桌面环境 发布者 红黑魂 来自 Ubuntu之家 摘要 Linux下桌面环境很多 xff0c Ubuntu之家给大家总结了比较常用的15个桌面环境 xff0c 并附上Ubuntu 12 10 xff08 Linu
  • C语言数据类型

    数据类型在数据结构中的定义是一个值的集合以及定义在这个值集上的一组操作 数据类型包括原始类型 多元组 记录单元 代数数据类型 抽象数据类型 参考类型以及函数类型 本文主要以51单片机中的数据类型为中心而展开的话题 在keil C51或者ia
  • 《Cortex-M0权威指南》之Cortex-M0技术综述

    Cortex M0权威指南 之Cortex M0技术综述 转载请注明来源 xff1a cuixiaolei的技术博客 Cortex M0 处理器简介 1 Cortex M0 处理器基于冯诺依曼架构 xff08 单总线接口 xff09 xff
  • xos详解5:PendSV_Handler

    PendSV Handler PendSV Handler LDR R2 61 OSTcbCurr 不必关中断 嵌套中断发生时会自动保存 R0 R3 到 MSP 并恢复 LDR R0 R2 如果发生咬尾的多个 PendSV xff0c 上半
  • M0最高优先级的中断设计

    1 Reset 3 Highest Reset 绝大部分处理器设计时 xff0c 将复位中断放在最高优先级 一般来说这样设计是合理的 xff0c 个人认为在某些应用场景这样处理仍有局限性 2 NMI 2 Nonmaskable interr
  • 如何从零开始写一个操作系统?

    首页发现等你来答 登录加入知乎 如何从零开始写一个简单的操作系统 xff1f 关注问题 写回答 操作系统 编程学习 如何从零开始写一个简单的操作系统 xff1f 看了这个 xff1a 从零开始写一个简单的操作系统 求指教 关注者 4 787
  • 每次听到同事跳槽后的薪资,我就像打了鸡血一样

    本文总结了现阶段 34 大龄程序员 34 的职业生存状况 xff0c 内容包含职位需求量 xff0c 议价能力如何以及如何度过传说中的 34 中年危机 34 等等 xff0c 供大家参考 xff01 值此金 三 银四跳槽季 的开端 xff0
  • Lua性能优化—Lua内存优化

    原文链接https blog uwa4d com archives usparkle luaperformance html 这是侑虎科技第236篇原创文章 xff0c 感谢作者舒航供稿 xff0c 欢迎转发分享 xff0c 未经作者授权请
  • Jetson Xavier NX(emmc)烧录系统时可能遇到的问题(避坑)

    目录 Ununtu18虚拟机无法联网 当NX接上电源后 xff0c 指示灯没有亮 xff08 不工作 xff09 在登陆SDK时 xff0c 可能会出现卡在初始界面的情况 在烧录镜像时 xff0c 可能会卡在该处没有变化 Ununtu18虚
  • 互斥量、条件变量与pthread_cond_wait()函数的使用,详解(一)

    1 首先pthread cond wait 的定义是这样的 The pthread cond wait and pthread cond timedwait functions are used to block on a conditio
  • STAR法则写简历

    STAR 法则是在面试 xff0c 求职 xff0c 写简历时候的常用利器 虽然常用 xff0c 但是我想知道的人一定很少很少 xff0c 不然为什么那么多人面试的时候犯那么低级的错误呢 xff1f STAR法则无法帮你提高你的实力 xff
  • 论文发表为什么不可以一稿多投呢

    论文发表为什么不可以一稿多投呢 很多作者在担心投一家杂志会被拒稿 xff0c 就会选择一篇稿件投多家期刊的方法 xff0c 大家应该或多或少都听过不能一稿多投 xff0c 但具体原因是什么大家知道吗 一稿多投会有什么后果 一稿多投是自稿件发
  • ROS下视频消息发布与订阅

    https download csdn net download v7xyy 10869743 下下来后 1 发布视频消息 rosrun video transport tutorial video publisher xff08 节点 c
  • ros中标志位设计(4)

    由于需要涉及控制权的交接事件 xff0c 需要通过标志位的方式进行设计 首先需要自定一个标志位的信息在ros中用于标志位信息的发布 下面是用于标志位的头文件Flag h Generated by gencpp from file xx ms
  • 全球最贵域名Sex.com将再度出售

    金融时报消息称 xff0c Sex com域名曾位居全球最贵域名前例 xff0c 四年前 xff0c 它以1400万美元成交 xff0c 不过 xff0c 买下此域名的公司面临破产 xff0c 因此Sex com将再度拿来出售 Sex co
  • 微信消息推送消息加解密(golang)

    本篇介绍如何使用golang对微信消息推送进行加解密 xff0c 后续会补充 xff0c 目前先写个原理 xff0c 大概自己看一下 xff0c 其他的自己应该也能写 老套路 xff0c 分为三步 xff0c 为啥写 xff0c 教程 xf