Modbus TCP Server端(附超全注释)_modbustcpserver-程序员宅基地

技术标签: 网络控制  modbus  

开发语言:C
开发平台:VS2019
开发工具:Visual studio 2019、Modbus/TCP Master

学习Modbus见:Modbus协议中文手册

获取Modbus TCP Master见:Modbus TCP Master

完整代码见文末,我会修改一点内容,请读者自行发现并改正,练习练习动手能力哈。

一、实验目的

熟悉并掌握Modbus协议,能用代码实现基于modbus协议的简单应用。

二、实验内容

(1)编写一个Modbus-TCP程序,用modbus Poll等模拟主站设备。从站设备具有16路DI、16路DO以及16路AI(从站可用曲线模拟)和16路AO(从站可用曲线模拟)。

(2)除了完成过程控制量的数据输入和输出外(使用已有的功能代码完成),还需要完成以下几个功能:

  1. 提供Modbus TCP的状态检查,检查系统是否正常、每个AI、AO、DI、DO通道是否正常;
  2. 自定义一个命令,实现对这些AI、AO、DI、DO通道选配,如果本次采集只采集8路DI和8路AO;
  3. 每次主从通信过程应该包括以下流程:
    (1) 首先主站轮询从站的状态;
    (2) 接着,从站发出响应,如果是OK,那么执行下一步,否则等待主站下一次轮询状态;
    (3) 主站对从站进行配置,配置DI/AI、DO /AO使能、采样周期;
    (4) 从站如果响应OK的话,执行下一步,否则等待主站的配置命令;
    (5)主站和从站进行数据交换和轮询。
    (6) 有通信的结束和退出命令。

三、实验环境

操作系统:Windows10

开发工具:Visual studio 2019、Modbus/TCP Master

四、设计方案

    本次实验使用TCP/IP来进行modbus协议的传输,主要使用了windows下的winsock2及其相关静态链接库ws2_32.lib,使用socket(套接字)进行主从机的连接。

    连接前,先进行从机(server)自检,检查AI、AO、DI、DO的数据是否已经准备好。我在从机部分用四个数组代替AI、AO、DI、DO,如图所示,所以自检OK。
在这里插入图片描述

    自检完成后,创建从机套接字,并将其与使用的电脑当前IPV4地址和相关端口进行绑定。绑定后将绑定状态输出到控制台。

    接着进入监听状态,当有主机连接进来时在控制台打印连接成功的信息。使用accept()函数阻塞程序运行,直到新的请求到来。每次将新的请求放入缓冲区(请求队列),处理完毕后再从缓冲区读取请求,并在while循环中,使用solve_all(SOCKET clnSock, byte request[])函数来判断请求的类型,并转到相应的处理函数。处理完后重置相应的缓冲区。solve_all()函数的实现如下。
在这里插入图片描述

注:由于modbus
tcp的报文帧格式如下,所以通过switch语句判断缓冲区的第8位:buff[7],即可直到功能码的类型。在这里,我只简单实现了01、02、03、04功能码,其他功能码类似,有一些在RTU中做了实现。

在这里插入图片描述

    这里再简单看一个功能码的实现,完整程序见附录或者压缩包。以01功能码为例进行说明:由于我将AI、AO、DI、DO都设置为16路,而DI、DO是以位为基本单位读写,而AI、AO是以字(这里是16位)为基本单位读写的,所以对于DI、DO的byte型数组只需要2个元素,而后者则需要32个元素。

    01功能码是读线圈/离散量的输出状态即ON/OFF。先通过报文的第12位(实际应该是第11、12个字节,但这里只用了16路,所以第11个字节为0x00,不予考虑)判断要读取的点的个数,再确定要返回的字节数,当读取的点数不超过8就返回一个字节,否则返回2个字节数据。相应的,还需要修改返回报文的第六位即这一位后面的字节数。除此之外,请求和响应的前8个字节是相同的。而响应报文的第九位是数据区的字节长度,即程序中的n,要说明的是在使用modbus tcp master软件测试时需要将n乘以2才能正确显示。再将第九位后面的区域填充为数据即可。最后用send()函数返回响应,同时在控制台输出请求、响应的内容。
在这里插入图片描述

    整个程序的流程图如下:

在这里插入图片描述

五、实验结果及分析(或设计总结)

(1)先查看本机IP地址,在程序中修改为今天的IP。
在这里插入图片描述在这里插入图片描述

(2)功能码测试
未连接时:
在这里插入图片描述
在这里插入图片描述

02功能码:
在这里插入图片描述
在这里插入图片描述

03功能码:
在这里插入图片描述
在这里插入图片描述

01与02,03与04相似,此处不再贴图。

六、完整代码

6.1 server.c

/*
* File_name:server.c
* Author:dahu
* Description:modbus tcp socket主要实现源文件
* Time:2021-04-21
* encoding:UTF-8
* Version:1.0
*/


/*********************************************************************
*                          Modbus-TCP报文帧格式
* |-----------------MBAP报头------------------|-----PDU-----|
* 
* 事务处理标识箱    协议表示符    长度    单元标识符   功能码   数据     
* 
    2Bytes          2Bytes    2Bytes    1Bytes    1Bytes  nBytes
exp:00 00         00  00      00 06     01         03     00 00   00 0A
**********************************************************************/


#include<stdio.h>
#include<winsock2.h>
//#include"scom.h"
#include"respond.h"

#pragma comment(lib,"ws2_32.lib")                //使用静态链接库

#define BUF_SIZE 20                              //缓冲区大小
#define Request_Queue_len 20                     //请求队列最大长度 

const char *server_addr = "172.20.10.3";       //服务器IP地址
short port = 502;                                //服务器端口号  

void TCP_server(short port,const char *p);       //TCP服务器函数声明
void STM32_IO(void);                             //与STM32通信,有点问题,没使用到,用的模拟方法实现
void init();                                     //从站状态检查,本来应该是STM32开机自检,由于用的是模曲线拟,基本上没什么用



int main() {
    
	//初始化 DLL
	WSADATA wsaData;   
	WSAStartup(MAKEWORD(2, 2), &wsaData);        //初始化,以指明 WinSock 规范的版本

	init();
	TCP_server(port,server_addr);
	//STM32_IO();

	WSACleanup();                                 //终止 DLL 的使用
	return 0;
}


/*********************************************************************************************
* 名称:main(short port,const char* p)
* 功能:创建tcp服务器,与client通信
* 入口参数:port:端口号;p:ip地址,本机ipv4地址
* 返回参数:无
* 说明:无
**********************************************************************************************/
void TCP_server(short port,const char* p) {
    
	/*Windows下的socket函数原型:
	SOCKET socket(int af, int type, int protocol);
	Windows 不把套接字作为普通文件对待,而是返回 SOCKET 类型的句柄,如:
	SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
	*/
	SOCKET server_socket;                                     //创建一个套接字,参数(地址簇,socket类型,使用的协议) 
	server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


	struct sockaddr_in server_addr;                            //sockaddr_in结构体,可在ws2def.h中查看原型
	/*memset()函数原型是:extern void* memset(void* buffer, int c, int count)
	buffer:为指针或是数组, c:是赋给buffer的值,count:是buffer的长度.
	这个函数在socket中多用于清空数组.如:原型是memset(buffer, 0, sizeof(buffer))*/
	memset(&server_addr, 0, sizeof(server_addr));              //每个字节都用0填充
	server_addr.sin_family = AF_INET;                          //使用IPv4地址
	server_addr.sin_addr.s_addr = inet_addr(p);                //具体的IP地址,inet_addr是比较老的定义,要取消SDL检查。或者用inet_pton()函数
	server_addr.sin_port = htons(port);                        //端口

	/*Windows下的bind()函数原型:
	int bind(SOCKET sock, const struct sockaddr* addr, int addrlen);
	*/
	int bind_status = -1;
	bind_status = bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));           //将套接字与特定的IP地址和端口绑定起来

	if (bind_status == -1) {
                                         //绑定状态监测,成功返回0,失败返回-1
		printf("绑定失败\n");
		exit(1);
	}
	else printf("套接字与IP地址、端口绑定成功\n");

	/*Windows下listeb()函数原型:
	int listen(SOCKET sock, int backlog);
	backlog 为请求队列的最大长度当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。
	如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列。
	*/
	int listen_status = -1;
	listen_status = listen(server_socket, Request_Queue_len);      //让套接字处于监听状态(并没有接收请求。接收请求需要使用 accept() 函数)

	if (listen(server_socket, 5) == -1) {
                              //监听状态监测
		printf("监听失败\n");
		exit(1);
	}
	else printf("创建TCP服务器成功\nTCP连接正常,开始监听\n\n");

	/*Windows下accept()函数原型:
	SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);
	accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号。后面和客户端通信时,要使用这个新生成的套接字,而不是原来服务器端的套接字。
	accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来
	*/
	SOCKADDR clntAddr;
	int nSize = sizeof(SOCKADDR);
	//char buff[BUF_SIZE] = { 0 };                                                   //缓冲区

	byte buff[BUF_SIZE] = {
    0};
	SOCKET clntSock = accept(server_socket, (SOCKADDR*)&clntAddr, &nSize);          //接收客户端请求
	printf("%d Link successfull\n", clntSock);


	while (1) {
         

		//int recv(SOCKET sock, char *buf, int len, int flags);
		//int send(SOCKET sock, const char *buf, int len, int flags);

		int strLen = recv(clntSock, buff, BUF_SIZE, 0);         //接收客户端发来的数据,recv返回的是字节数

		if (strLen = 0)
			printf("客户端连接关闭\n\n");
		else if (strLen < 0) 
			printf("Linking Error");

		printf("\n\n**************************************************************************************************\n");
		//int length = buff[5];                                  //MBAP报文头,2+2+2+1   功能码1+起始地址2+数量2
		printf("收到报文为:\n");
		for (int i = 0;i < 12;i++)printf("0x0%x ", buff[i]);     //显示主站的请求报文
		printf("\n");

		solve_all(clntSock,buff);                                //根据功能码进行相应处理,从站返回相应报文

		memset(buff, 0, BUF_SIZE);                               //重置缓冲区
		//Sleep(1000);
	}

	closesocket(server_socket);                                   //关闭套接字
}




/*********************************************************************************************
* 名称:init()
* 功能:服务器端(从机)开机自检,但我没连接stm32,所以这个函数暂时是个空壳子
* 入口参数:无
* 返回参数:无
* 说明:无
**********************************************************************************************/
void init(void) {
    
	printf("从站状态检查...");
	printf("\n从站状态正常\n");
}




/*********************************************************************************************
* 名称: STM32_IO()
* 功能:与32通信,实现modbus tcp的AI、AO等
* 入口参数:无
* 返回参数:无
* 说明:调试不成功,暂时没使用
**********************************************************************************************/
void STM32_IO(void) {
    
	int data[9];  //
	HANDLE hcom;  //声明串口操作句柄
	hcom = open_scom("COM3", 9600, NOPARITY, 8, 1);//打开串口
	while (1)
	{
    
		read_scom(hcom, data);  //读串口
		// do someting;
		//break
		//printf("%d\n", data);
	}
	
	close_scom(hcom);         //关闭串口
}

6.2 respond.c

/*
* File_name:respond.c
* Author:dahu
* Description:定义了不同功能码的响应函数
* Time:2021-04-21
* encoding:UTF-8
* Version:1.0
*/


#include<stdio.h>
#include"respond.h"



/*********************************************************************************************
* 功能:模拟16路DI、DO、AI、AO
* 说明:使用的Modbus/TCP master ,使用了01 02 03 04功能码
*      前两个以bit为基本单位,16路即2bytes;后两个以word为基本单位,16路即32bytes(16words)。
* 注意:连续两个寄存器之间存在字节序和大小端的问题,需注意
*********************************************************************************************/
byte coil[2] = {
    0x00,0x00};    //读线圈/离散量输出状态(ON/OFF)   01    bit   16路DO:0000 0000 0000 0000(高到低)
byte relay[2] = {
    0x0f,0x06};   //读离散量输入值(ON/OFF)         02    bit   16路DI:0000 0110 0000 1111(高到低)
byte holding_reg[32] = {
    0x01,0x01,0x01,0x02,0x00,0x03,0x00,0x04,0x00,0x05,0x00,0x06,0x00,0x07,0x00,0x08,
						0x00,0x01,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x05,0x00,0x06,0x00,0x07,0x00,0x08,};     
                               //读保持寄存器值                   03    word  (响应报文按字节来的,这里不用word定义了)
byte input_reg[32] = {
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};       
                               //读输入寄存器值                   04    word    全为0x0101,即257




/*********************************************************************************************
* 名称:solve_01(SOCKET clntSock, byte request[], byte coil[])
* 功能:01功能码,读线圈/离散量输出状态
* 入口参数:clnSock:客户端socket,request:接收数据缓冲区,coil:事先准备好的线圈/离散量状态输出值
* 返回参数:无
* 说明:收到报文的关键部分为:01(1byte)+ 起始地址(2bytes)+ 寄存器数(2bytes)。
*      例如: 02 00 00 00 06表示读取0x0000开始的6个线圈的状态(1个线圈占用1位,共1个字节)
*      返回报文关键部分为:02(1byte)+ N(1byte)+ n=N/N+1字节状态(n bytes)
*      对上诉例子的响应:02 01 01
*上面只是关键部分报文分析,完整报文符合server.c中我写的 Modbus TCP报文格式
**********************************************************************************************/
void solve_01(SOCKET clntSock, byte request[], byte coil[]) {
    
	int len, n;
	len = request[11];                                                     //需要读取的点的个数
	n = (len % 8) == 0 ? (len / 8) : (len / 8 + 1);                        //对应的的字节数
	byte send_buff[8 + 3] = {
     0x00,0x00,0x00,0x00,0x00,0x06,0x01,0x02 };   //返回数据缓冲区,前八个字节不变(除了长度位),后面最多3个字节
	send_buff[5] = 2 + 1 + n;                                              //确定响应报文第六位,即后续报文长度:01+02+n+n bytes data
	send_buff[8] = 2 * n;                                                  //我用的是Modbus/TCP这个软件来测试的,软件有点小问题,n要乘以2,正确的报文这里不需要乘以2  
	for (int i = 0;i < n;i++) {
    
		send_buff[i + 9] = coil[i];
	}
	printf("\n该指令为读线圈/离散量状态输出即DO,返回内容应该为:\n");
	for (int i = 0;i < 8 + 1 + n;i++)printf("0x0%x ", send_buff[i]);
	printf("\n**************************************************************************************************");
	send(clntSock, send_buff, 8 + 1 + n, 0);                               //响应请求
}




/*********************************************************************************************
* 名称:solve_02(SOCKET clntSock, byte request[], byte relay[])
* 功能:02功能码,读离散量输入
* 入口参数:clnSock:客户端socket,request:接收数据缓冲区,relay:事先准备好的离散量输入值
* 返回参数:无
* 说明:同01功能码
**********************************************************************************************/
void solve_02(SOCKET clntSock, byte request[], byte relay[]) {
    
	int len,n;
	len = request[11];                                                   //需要读取的点的个数
	n = (len % 8) == 0 ? (len/8):(len/8+1);                              //对应的的字节数
	byte send_buff[8 + 3] = {
     0x00,0x00,0x00,0x00,0x00,0x06,0x01,0x02 }; //返回数据缓冲区,前八个字节不变(除了长度位),后面最多3个字节
	send_buff[5] = 2+1+n;                                                //确定响应报文第六位,即后续报文长度:01+02+n+n bytes data
	send_buff[8] = 2*n;                                                  //我用的是Modbus/TCP这个软件来测试的,软件有点小问题,n要乘以2,正确的报文这里不需要乘以2  
	for (int i = 0;i < n;i++) {
    
		send_buff[i + 9] = relay[i];
	}
	printf("\n该指令为读离散量输入即DI,返回内容应该为:\n");
	for (int i = 0;i < 8+1+n;i++)printf("0x0%x ", send_buff[i]);
	printf("\n**************************************************************************************************");
	send(clntSock, send_buff, 8+1+n, 0);                                 //响应请求
}




/*********************************************************************************************
* 名称:solve_03(SOCKET clntSock, byte request[], byte holding_reg[])
* 功能:03功能码,读保持寄存器
* 入口参数:clnSock:客户端socket,request:接收数据缓冲区,holding_reg:事先准备好的保持寄存器
* 返回参数:无
* 说明:类似01功能码,不在赘述
**********************************************************************************************/
void solve_03(SOCKET clntSock,byte request[],byte holding_reg[]) {
    
	int len, n;
	len = request[11];                                                       //需要读取的寄存器的个数
	n = 2 * len;                                                             //对应的的字节数
	byte send_buff[8 + 33] = {
     0x00,0x00,0x00,0x00,0x00,0x06,0x01,0x03 };    //返回数据缓冲区,前八个字节不变(除了长度位),后面最多33个字节
	send_buff[5] = 2 + 1 + n;                                                //确定响应报文第六位,即后续报文长度:01+02+n+n bytes data
	send_buff[8] = n;                                                        //我用的是Modbus/TCP这个软件来测试的,软件有点小问题,n要乘以2,正确的报文这里不需要乘以2  
	for (int i = 0;i < n;i++) {
    
		send_buff[i + 9] = holding_reg[i];
	}
	printf("\n该指令为读保持寄存器,返回内容应该为:\n");
	for (int i = 0;i < 8 + 1 + n;i++)printf("0x0%x ", send_buff[i]);
	printf("\n**************************************************************************************************");
	send(clntSock, send_buff, 8 + 1 + n, 0);                                 //响应请求
}




/*********************************************************************************************
* 名称:solve_04(SOCKET clntSock, byte request[], byte input_reg[])
* 功能:04功能码,读输入寄存器
* 入口参数:clnSock:客户端socket,request:接收数据缓冲区,relay:事先准备好的离散量输入值
* 返回参数:无
* 说明:同01功能码
**********************************************************************************************/
void solve_04(SOCKET clntSock, byte request[], byte input_reg[]) {
    
	int len, n;
	len = request[11];                                                       //需要读取的寄存器的个数
	n = 2 * len;                                                             //对应的的字节数
	byte send_buff[8 + 33] = {
     0x00,0x00,0x00,0x00,0x00,0x06,0x01,0x04 };    //返回数据缓冲区,前八个字节不变(除了长度位),后面最多33个字节
	send_buff[5] = 2 + 1 + n;                                                //确定响应报文第六位,即后续报文长度:01+02+n+n bytes data
	send_buff[8] = n;                                                        //我用的是Modbus/TCP这个软件来测试的,软件有点小问题,n要乘以2,正确的报文这里不需要乘以2  
	for (int i = 0;i < n;i++) {
    
		send_buff[i + 9] = input_reg[i];
	}
	printf("\n该指令为读输入寄存器,返回内容应该为:\n");
	for (int i = 0;i < 8 + 1 + n;i++)printf("0x0%x ", send_buff[i]);
	printf("\n**************************************************************************************************");
	send(clntSock, send_buff, 8 + 1 + n, 0);                                 //响应请求
}






/*********************************************************************************************
* 名称:solve_all(SOCKET clnSock,byte request[])
* 功能:对 功能码进行判别,并转到相应的功能码的处理函数
* 入口参数:clnSock:客户端socket,request:接收数据缓冲区
* 返回参数:无
* 说明:无
**********************************************************************************************/
void solve_all(SOCKET clnSock,byte request[]) {
    
	switch (request[7]) {
    
		case 1:solve_01(clnSock,request,coil);break;
		case 2:solve_02(clnSock,request,relay);break;    //DI 读离散量输入
		case 3:solve_03(clnSock,request, holding_reg);break;
		case 4:solve_04(clnSock,request,input_reg);break;
	}
}

6.3 respond.h

#pragma once
#include<winsock2.h>

#ifndef _RESPOND_H_
#define _RESPOND_H_


void solve_01(SOCKET clntSock, byte request[], byte coil[]);
void solve_02(SOCKET clntSock, byte request[], byte relay[]);
void solve_03(SOCKET clntSock, byte request[], byte holding_reg[]);
void solve_04(SOCKET clntSock, byte request[], byte input_reg[]);


void solve_all(SOCKET clnSock, byte request[]);  


#endif
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_43764974/article/details/119213899

智能推荐

windows terminal配置_在 windows terminal 中设置 powershell 的 startingdirect-程序员宅基地

文章浏览阅读244次。Windows 终端是一个面向命令行工具和 shell(如命令提示符、PowerShell 和适用于 Linux 的 Windows 子系统 (WSL))用户的新式终端应用程序。 它的主要功能包括多个选项卡、窗格、Unicode 和 UTF-8 字符支持、GPU 加速文本呈现引擎,还可以用于创建你自己的主题并自定义文本、颜色、背景和快捷键绑定。安装 从 windows store中安装,搜索terminal 有两个,一个microsoft terminal一个windows terminal prev_在 windows terminal 中设置 powershell 的 startingdirectory 值为 null

Java处理post请求中的csv文件(判断文件编码)_java如何判断上传csv文件内部编码格式-程序员宅基地

文章浏览阅读1.6k次。今天在工作中遇到了一个需求,需要用Java处理post请求中的csv文件,由于之前没处理过,遇到了很多问题,特此记录。由于具体的业务会有区别,本文主要介绍了处理逻辑的代码,如需要请结合具体业务与注释做出修改即可。pom坐标<!--GetByteEncode,用来判断二进制字节流的编码--> <dependency> <groupId>com.googlecode.juniversalchardet</groupId>_java如何判断上传csv文件内部编码格式

SFTP服务命令行可以连接成功编辑器插件却连接失败_命令行sftp 可以连,但程序sftp连不行-程序员宅基地

文章浏览阅读1.1k次。最近用phpstorm编辑器的远程内置功能连接远程服务器时,一直报连接失败。 查看系统日志也只是说,连接被拒绝。 在网上找问题时,看到这种情况: 本机网络dns变化也会导致xshell自动断开连接,这种情况通过修改ssh配置文件将其中“UseDNS yes”去掉注释并改成no即可,过程如下vim /etc/ssh/sshd_config ... #ClientAliveInter..._命令行sftp 可以连,但程序sftp连不行

Electron安装配置_electron配置-程序员宅基地

文章浏览阅读747次。Electron把Chromium和HTMLWeb页面整合到了一起,最终成为一个GUI桌面应用,还有其他很强大的功能,如访问硬件、通信等。_electron配置

垃圾模块清理工具小程序功能模块0.33_模块碎片清理模块-程序员宅基地

文章浏览阅读162次。简介:是一款可以帮你快速删除模块本身删除后中数据库残留的模块信息,能够让你快速重新安装相关模块。用途:1.把自己觉得实在用不到的模块从网站目录及数据库中删除2.有的朋友站内装了很多模块,过了段时间发现很多又用不上,浪费了网站空间资源,影响运行速度,想想删除,可除了模块文件夹外,又不知道哪里应该删除。使用方法:模块会列出站内所有除了系统功能之外的模块,删除时将删除模块对应的数据库及模块目录。注意事项:1.模块只能是站长身份才可以使用,其它人没办法使用。2.删除模块后,想在恢复只需要重新_模块碎片清理模块

HTTP文档-程序员宅基地

文章浏览阅读1.4k次。http是客户端与服务端传输文本的一种协议标准http协议是无状态的http协议默认端口是80hrrps协议(加密传输)端口443一、请求方法get(获取信息页面)post(上传,修改,添加,服务端信息)put(修改)delete(删除)head(只返回头信息)trace(回显服务器收到的请求,主要用于测试或诊断)connect(HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器)options(客户端查看服务器性能)二、请求报文建立TCP连接在HT_http文档

随便推点

Android JNI开发-程序员宅基地

文章浏览阅读586次。AndroidStudio进行JNI开发:第一种方法使用Android.mk以及Application.mk生成不同内核下的.文件。1.要将AndroidStudio配置支持ndk开发配置的方法如下: 只要在Android NDK location这个位置配置上ndk的路径,之后就可以使用AndroidStudio进行ndk开发.JNI开发的步骤:1、将使用的方法进行定义,之后使用javac..._android jni开发

vmware下fedora与宿主机windows的FTP通信实验_fedora 虚拟机访问宿主-程序员宅基地

文章浏览阅读646次。闲来无聊,做了个小小的FTP通信实验,也学到了一些专业外的东西。实验目的:利用虚拟机下fedora系统架设FTP服务器,利用宿主机windows系统访问FTP。实验设置:1.fedora下:ip:192.168.0.20 掩码:255.255.255.0 网关:192.168.0.1 与宿主机连接方式位桥接。2.windows下:ip: 192.168.0.1 掩码:255.255.255.0(显然与fedora在同一网段内,形成局域网)。实验软件:1.fedora下服务器软件:vsftpd2.window_fedora 虚拟机访问宿主

Qt界面篇:用Qt制作一个简单的属性页对话框_qt 设计属性窗口-程序员宅基地

文章浏览阅读1k次。Qt界面篇:用Qt制作一个简单的属性页对话框_qt 设计属性窗口

信息项目管理师项目管理领域理论知识_信息管理工程师10大-程序员宅基地

文章浏览阅读499次。一、项目管理分为五大过程组(47个过程),10大知识领域,如下图:典型生命周期模型如下:二、管理过程1. 项目整体管理说明:项目整体管理包括隶属于项目管理过程组的各种过程和项目管理活动进行识别、定义、组合、统一和协调的过程。在项目管理中,整合兼具统一、合并、沟通和建立联系的性质,这些行动应该贯穿项目始终。作用:通过项目整合管理,我们完成了资源分配,平衡竞争性需求,研究各..._信息管理工程师10大

MySQL学习好书推荐-程序员宅基地

文章浏览阅读68次。5、《深入浅出MySQL+数据库开发、优化与管理维护+第2版+唐汉明》1、《MySQL技术内幕 InnoDB存储引擎》4、《千金良方:MySQL性能优化金字塔法则》3、《MySQL是怎样运行的》2、《MySQL排错指南》6、《MySQL大智小技》

改良规则引擎_规则引擎分布式改造-程序员宅基地

文章浏览阅读1.7k次。 规则引擎在国内有过一段时期的热潮,特别是JBoss收购了Drools之后,很多人开始关注规则引擎究竟是什么东西。 通过一定程度的了解,大家知道,规则引擎是一个业务逻辑的配置实现。业务逻辑层的代码一般我们通过程序代码实现,但是程序代码不能直观的体现逻辑,而且修改必须由程序员进行。因此我们希望提供一种实现,能够直观的体现逻辑,并且最好可以由业务人员直接修改。 因此从这个角度来说,需要有一个可供业务人_规则引擎分布式改造

推荐文章

热门文章

相关标签