[vs创建DLL/使用][1]
[1]:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29121609&id=3851844

知识:

  1. Linux 动态库剖析
    https://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/index.html
  1. linux下动态链接库(.so)的显式调用和隐式调用

https://blog.csdn.net/lc_910927/article/details/42393121

  1. DLL中类的显式链接
    http://www.moon-soft.com/doc/14639.htm
  1. Windows关键字
1
BOOL APIENTRY DllMain(HANDLE hModule,DOWRD dwReason,void * lpReserved)
  • APIENTRY:表明此函数是应用程序的入口点,相当于c的main()函数
  1. visualstudio2015加载DLL

将编译好的DLL_AGM.libDLL_AGM.dll还有头文件DLL_AGM.h文件放入vs工程源码目录里,以及子目录Debug目录里;

每次更改代码都要替换.dll以及.lib文件,如修改.h文件也要替换.h文件

Linux下编写so

参考文章:

[linux与windows调用动态库so dll文件] [2]
[2]: https://blog.csdn.net/cbbbc/article/details/45102861

  1. add.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include"add.h"

    int add(int a,int b)
    {
    int sum;
    sum=a+b;
    //std::cout<<sum<<std::endl;
    return sum;
    }
  2. add.h

    1
    2
    3
    #include<iostream>

    int add(int a,int b);
  1. 封装成.so
    1
    g++   add.cpp  -fPIC  -shared -o libADD.so

    生成libADD.so

调用代码
4. main.cpp

1
2
3
4
5
6
7
8
9
10
11
#include"add.h"
using namespace std;
int main()
{

int sum = add(1,2);

std::cout<<sum<<endl;

return 0;
}

g++链接.so
5.

1
g++ main.cpp -L. -lADD -Wl,-rpath,./ -o b.out

VS创建DLL

  1. 用Visualstudio新建一个工程Templates—–>Visual c++——–>Win32 Project

Overview Application Settings 选择Application type:下的DLL 勾选Empty project

通过编译会生成^&*.dll以及*&^.lib

  1. VS使用dll
  • 在工程里新建一个include文件,将编译dll工程里的.h文件放入include里面

  • 在工程目录新建一个lib文件夹,将编译dll工程生成的.lib文件放入该目录

  • 在使用dll工程中设置加载lib,

工程添加lib


2018-06-22

隐式加载

  • add_header.h

    1
    2
    3
    4
    /*      add_header.h  */
    #include<iostream>

    const int add(cosnt int a, const int b);
  • libadd_source.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     int add(const int a, const int b)
    {
    return a + b;
    }
    ````





    编译指令:
    ```linux
    g++ -shared -fPIC libadd_source.cpp -o libadd.so
  • main_test.cpp

    隐式加载

1
2
3
4
5
6
7
8
9
10
#include"add_header.h"

int main()
{
using namespace std;
int a = 3;
int b = 7;
cout<<a<<"+"<<b<<"="<<add(a,b)<<endl;
return 0;
}

编译指令:

1
g++ -o a.out main_test.cpp  -L. -ladd -Wl,-rpath,./

socket编程教程_C语言中文网

代码参考socket

sokect原理参考

服务器: 它是被动的,它只能等待别人跟它建立连接,自己不会去主动连接;

原理:使用SOCKET实现TCP/IP协议的通讯

参考资料_什么是原端口和目的端口

基本TCP套接字编程讲解_流程图

图解TCP-IP协议

Socket详解

[MAKEWORD(2,2)使用][]

[[MAKEWORD(2,2)使用]: http://blog.sina.com.cn/s/blog_a74f39a201010sb3.html

什么叫“低八位、高八位;低四位、高四位”

c++中冒号(:)和双冒号(::)的用法

Windows Socket和Linux Socket编程的区别

C++使用thread类多线程编程


基础概念

  1. TCP/IP协议族

    英语:TCP/IP Protocol Suite,或TCP/IP Protocols),简称TCP/IP
    因为该协议家族的两个核心协议:TCP(传输控制协议)和IP(网际协议),为该家族中最早通过的标准[3]。由于在网络通讯协议普遍采用分层的结构,当多个层次的协议共同工作时,类似计算机科学中的堆栈,因此又被称为TCP/IP协议栈(英语:TCP/IP Protocol Stack)

  1. 端口
    端口就是特定的程序或者软件也可以理解为特定软件或程序的接口,

  2. 原端口/目的端口
    源端口就是本机程序用来发送数据的端口,目的端口就是对方主机用哪个端口接收;

    封目的端口自己就出不去,封源端口别人就进不来。

数据发送时计算机会在数据段上添上本机端口号(源端口号)和目的主机接收数据的端口号(目的端口号)
数据包通过网络设备走的时候,第一个检查的就是你这个数据包的目的地址在什么地方。

  1. TCP通信3个步骤:

1)建立TCP连接很简单,通过三次握手便可建立连接。
2)建立好连接后,开始传输数据。TCP数据传输牵涉到的概念很多:超时重传、快速重传、流量控制、拥塞控制等等。
3)断开连接的过程也很简单,通过四次握手完成断开连接的过程。

  1. TCP三次握手建立连接
    第一次握手:客户端发送syn包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;
    第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
  1. ACK
    用于确认资料有无正确的传输到接收端,在ASCII中编号为6

  2. 网络中进程之间如何通信
    网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

  1. 侦听端口
    服务器倾听端口 ,是服务器监听客户端用的 ,相当于”地址” ,是服务器与客户端间的纽带

TCP/IP首先要有一个服务端,它是被建立连接的;


  • WSAStartup

WSAStartup就是为了向操作系统说明,我们要用哪个库文件,让该库文件与当前的应用程序绑定,从而就可以调用该版本的socket的各种函数了。

WSAStartup解释1

WSAStartup() - 使用方法
当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。

  • socket

Windows 下也使用 socket() 函数来创建套接字,原型为:

1
SOCKET socket(int af, int type, int protocol);

除了返回值类型不同,其他都是相同的。Windows 不把套接字作为普通文件对待,而是返回 SOCKET 类型的句柄。请看下面的例子:

1
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);  //创建TCP套接字
  1. af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。
  1. type 为数据传输方式,常用的有 SOCK_STREAM 和 SOCK_DGRAM,在《socket是什么意思》一节中已经进行了介绍。
    1. SOCK_STREAM 表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常见的 http 协议就使用 SOCK_STREAM 传输数据,因为要确保数据的正确性,否则网页不能正常解析。
      1. SOCK_DGRAM 表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为 SOCK_DGRAM 所做的校验工作少,所以效率比 SOCK_STREAM 高。
  1. protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。
  • bind 函数

调用函数 socket 创建套接字描述符时,该套接字描述符是存储在它的协议族空间中,没有具体的地址,要使它与一个地址相关联,可以调用函数bind 使其与地址绑定。客户端的套接字关联的地址一般可由系统默认分配,因此不需要指定具体的地址。若要为服务器端套接字绑定地址,可以通过调用函数 bind 将套接字绑定到一个地址。下面是该函数的描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* 套接字的基本操作 */   
/*
* 函数功能:将协议地址绑定到一个套接字;其中协议地址包含IP地址和端口号;
* 返回值:若成功则返回0,若出错则返回-1;
* 函数原型:
*/
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
* 说明:
* sockfd 为套接字描述符;
* addr是一个指向特定协议地址结构的指针;
* addrlen是地址结构的长度;
*/
```




+ ``SOCKADDR_IN`` 结构体

[sockaddr_in结构体简介][]

[sockaddr_in结构体简介]: http://blog.chinaunix.net/uid-20673616-id-1578732.html











# 代码解析

#pragma comment(lib, “ws2_32.lib”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

静态加入一个lib文件

ws2_32.lib文件,提供了对以下网络相关API的支持,若使用其中的API,则应该将ws2_32.lib加入工程(否则需要动态载入ws2_32.dll)。
这句话一般出现在网络编程中,需要使用网络API函数的时候,就必须使用这条语句加载ws2_32.lib库或者动态载入ws2_32.dll













# 非阻塞套接字


[参考_Windows Socket 非阻塞模式开发][]

[参考_Windows Socket 非阻塞模式开发]: http://blog.csdn.net/ithzhang/article/details/8274596




[socket同步/异步通信] []



[socket同步/异步通信]: http://www.it610.com/article/5036113.htm


[重点参考_TCP Server处理多Client请求的方法—非阻塞accept与select][]

[重点参考_TCP Server处理多Client请求的方法—非阻塞accept与select]: http://velep.com/archives/1137.html



[重点参考_linux下TCP连接的client和server][]

[重点参考_linux下TCP连接的client和server]: http://blog.csdn.net/linuxheik/article/details/19326469




[可参考代码_C++多线程socket系统新编][]

[可参考代码_C++多线程socket系统新编]: http://blog.csdn.net/wyansai/article/details/50762479

## 多线程




[参考代码_C++多线程socket系统新编][]


[参考代码_C++多线程socket系统新编]: http://blog.csdn.net/wyansai/article/details/50762479



[Socket编程模型之简单选择模型][]


[Socket编程模型之简单选择模型]: http://blog.csdn.net/caoshiying/article/details/52550761





[使用Cout输出String和CString对象][]


[使用Cout输出String和CString对象]: http://www.cnblogs.com/hushaojun/p/4920151.html


[ C++基础::为什么不能cout一个string?][]


[ C++基础::为什么不能cout一个string?]: http://blog.csdn.net/lanchunhui/article/details/49757713



[ C++中的C_str()函数用法][]

[ C++中的C_str()函数用法]: http://blog.csdn.net/nancy_m/article/details/7583550








fork()

对于主进程 fork()返回新建的子进程ID, 子进程fork()返回0


[fork()函数详解 ][]


[fork()函数详解 ]: http://blog.chinaunix.net/uid-26495963-id-3150116.html





# 问题

+ 提示:blind fail

尝试解决:

使用

SO_REUSEADDR

1
2
3
4
5
6
7
8
9
10
11
12
13
14



+ socket客户端使用recv从服务器`接收数据:有“烫烫”乱码


结论:
TCP中send和recv函数针对的是字符(无边界), 而不是字符串(以'\0'作为边界)。 换句话说, tcp的send和recv函数对所传的内容不敏感, 它关注的是字符, 仅此而已。
recv要做数组初始化,否则会出现``烫烫``的乱码




问题代码
  recv(sHost, bufRecv,5 , 0);     // 接收服务器端的数据, 只接收5个字符  
    cout << endl <<"从服务器接收数据:" << bufRecv;  
}  
//退出 
1
2
3
4
5

方法1.

解决后的代码
手动在recv接受数组添加0作为结束
    auto len = recv(sHost, bufRecv, BUF_SIZE, 0);     // 接收服务器端的数据, 只接收5个字符            // client4:  读
    bufRecv[len] = 0;
    cout << endl << "从服务器`接收数据:" << bufRecv;
}
//退出 
1
2
3
4
5


方法2.

在recv前做数组初始化
    char bufRecv[100] = { 0 };
     recv(sHost, bufRecv, 100, 0);
1
2
3
4


方法3.
在recv前做清零操作
    ZeroMemory(bufRecv, BUF_SIZE);  //清0   /*或者这样:    memset(bufRecv, 0, BUF_SIZE);*/
     recv(sHost, bufRecv, BUF_SIZE, 0);






1. 是不是说char数组没有结尾`\0`输出会出现乱码?


2. server端,send数组明明有``\0``但是client端的rev却没有接收到``\0``?









[需要了解_c语言——'\0','0',"0" ,0之间的区别][]

[需要了解_c语言——'\0','0',"0" ,0之间的区别]: http://blog.sina.com.cn/s/blog_73428e9a0101bdkh.html

[需要了解_字符和字符字面值][]

[需要了解_字符和字符字面值]: http://blog.csdn.net/candcplusplus/article/details/8264676




[CSDN类似问题][]


[CSDN类似问题]: http://bbs.csdn.net/topics/390408164














District 9(2009

  1. typename
    “typename”是一个C++程序设计语言中的关键字。当用于泛型编程时是另一术语”class”的同义词。[1]这个关键字用于指出模板声明(或定义)中的非独立名称(dependent names)是类型名,而非变量名。以下是对于泛型编程中typename两种迥然不同的用法的解释。

char 数组长度不能存储’\0’,导致 后面的字符贴上来了

#define  _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stdio.h>
using namespace  std;


typedef unsigned char U8;

typedef struct stInitMode {
    char chModeStartTime[15];        // IN,模式发生时间(YYYYMMDDhh24mmss);
    U8 ucModeID;                    // IN,模式ID (参见第4分册“3.2.11 降级运营模式ID”)
}_stInitMode;


struct stInitReaderInfo_CD
{
    char chLineNumber[3];                // IN,线路编码
    char chStationNumber[3];            // IN,车站编码
    char chEquipType[3];                // IN,设备类型
    char chEquipNumber[4];                // IN,3bytes设备编号
    U8 EquipWorkMode;                    // IN,设备工作模式(读写器工作模式:0x00——使用指令中的工作模式;0x10——ES;0x20——非付费区BOM;0x21——付费区BOM;0x22——TVM;0x23——TCM;0x24——PCA;0x40——进站AGM;0x41——出站AGM)
    U8 DownWorkModenum;                    // IN,模式数量
    char chRCodeBaudrate[12];       //  IN,关联扫码枪通讯波特率
    _stInitMode Mode[16];
    //    stInitMode_CD  Mode[16];

    char chRCodeCOM[10];            //  IN,关联扫码枪串口号
    char chModeStartTime[15];            // IN,模式发生时间(YYYYMMDDhh24mmss);
    char chRCodeServerAddr[256];    //  IN, 支付平台接入地址,例如http://182.140.238.35:61082/
    U8 ucModeID;                        // IN,模式ID (参见第4分册“3.2.11 降级运营模式ID”)
    bool Mark_IsUseQRcode;           //take:add是否启用二维码  在读写器里传参标志需要
};

typedef struct stInitReaderInfo {
    char chLineNumber[3];            // IN,线路编码
    char chStationNumber[3];        // IN,车站编码
    char chEquipmentType[3];        // IN,设备类型
    char chEquipmentNumber[4];        // IN,3bytes设备编号
    U8 ucEquipWorkMode;                // IN,设备工作模式(读写器工作模式:0x00——使用指令中的工作模式;0x10——ES;0x20——非付费区BOM;0x21——付费区BOM;0x22——TVM;0x23——TCM;0x24——PCA;0x40——进站AGM;0x41——出站AGM)
    U8 ucDownWorkModenum;            // IN,模式数量
    _stInitMode Mode[16];
    //char chEquipmentID[17];            // OUT,16位PSAM卡ID
} _stInitReaderInfo;


typedef struct _stRCodeInfo {
    char chRCodeCOM[10];            //  IN,关联扫码枪串口号
    char chRCodeBaudrate[12];       //  IN,关联扫码枪通讯波特率
    char chRCodeServerAddr[256];    //  IN, 支付平台接入地址,例如http://182.140.238.35:61082/
} _stRCodeInfo;




void startReaderAFC(void* i_StartAFCData)
{


    /****>>>>>>>>>>>>>>>>>>take:第一个参数结构体***>>>>>>>>>>>>>>>>>>>>>>>>>******************/

    _stInitReaderInfo take_stInitReaderInfo;
    strcpy(take_stInitReaderInfo.chLineNumber, ((stInitReaderInfo_CD*)i_StartAFCData)->chLineNumber);
    strcpy(take_stInitReaderInfo.chStationNumber, ((stInitReaderInfo_CD*)i_StartAFCData)->chStationNumber);
    strcpy(take_stInitReaderInfo.chEquipmentType, ((stInitReaderInfo_CD*)i_StartAFCData)->chEquipType);
    strcpy(take_stInitReaderInfo.chEquipmentNumber, ((stInitReaderInfo_CD*)i_StartAFCData)->chEquipNumber);
    take_stInitReaderInfo.ucEquipWorkMode = ((stInitReaderInfo_CD*)i_StartAFCData)->EquipWorkMode;
    take_stInitReaderInfo.ucDownWorkModenum = ((stInitReaderInfo_CD*)i_StartAFCData)->DownWorkModenum;
    //take_stInitReaderInfo.Mode = ((stInitReaderInfo_CD*)i_StartAFCData)->Mode;
    memcpy(take_stInitReaderInfo.Mode, ((stInitReaderInfo_CD*)i_StartAFCData)->Mode, sizeof(take_stInitReaderInfo.Mode));//take:这样可以吗?

    /***<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<take:第一个参数结构体**<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<******/


    /*>>>>>>>>>>>>>>>>>>>>>>>>>>>take:第二个参数*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
    _stRCodeInfo *ptr_stRCodeInfo = (_stRCodeInfo*)malloc(sizeof(_stRCodeInfo));

    memset(ptr_stRCodeInfo->chRCodeBaudrate, '\0', sizeof(ptr_stRCodeInfo->chRCodeBaudrate));
    memset(ptr_stRCodeInfo->chRCodeCOM, '\0', sizeof(ptr_stRCodeInfo->chRCodeCOM));
    memset(ptr_stRCodeInfo->chRCodeServerAddr, '\0', sizeof(ptr_stRCodeInfo->chRCodeServerAddr));

    cout << "(stInitReaderInfo_CD*)i_StartAFCData)->chRCodeCOM):" << ((stInitReaderInfo_CD*)i_StartAFCData)->chRCodeCOM << endl;
    strcpy(ptr_stRCodeInfo->chRCodeCOM, ((stInitReaderInfo_CD*)i_StartAFCData)->chRCodeCOM);

    cout << ptr_stRCodeInfo->chRCodeCOM << " line:" << __LINE__ << endl;   //只有这里是正确的

    strcpy(ptr_stRCodeInfo->chRCodeBaudrate, ((stInitReaderInfo_CD*)i_StartAFCData)->chRCodeBaudrate);
    cout << ptr_stRCodeInfo->chRCodeCOM <<" line:"<<__LINE__<< endl;
    strcpy(ptr_stRCodeInfo->chRCodeServerAddr, ((stInitReaderInfo_CD*)i_StartAFCData)->chRCodeServerAddr);

    cout << ptr_stRCodeInfo->chRCodeCOM << " line:" << __LINE__ << endl;
    //cout << "(stInitReaderInfo_CD*)i_StartAFCData)->chRCodeCOM):" << ((stInitReaderInfo_CD*)i_StartAFCData)->chRCodeCOM << endl;
    /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*******************************************************<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
//    cout << ((test*)i_StartAFCData)->chEquipNumber << endl;



//    cout<<((stInitReaderInfo_CD*)i_StartAFCData)->chEquipmentNumber;
}

int main()
{
    stInitReaderInfo_CD temp[2];


//    strcpy(temp[0].chEquipNumber_, "aa");
    temp[0].EquipWorkMode = 34;

     strcpy(temp[0].Mode[0].chModeStartTime, "201804-=18");
    strcpy( temp[0].chLineNumber, "nu");

    strcpy(temp[0].chStationNumber,"st");
     strcpy(temp[0].chEquipType,"tp");
     strcpy(temp[0].chEquipNumber, "chn");
     sprintf(temp[0].chRCodeBaudrate, "%d", 115200);
     strcpy(temp[0].chRCodeCOM, "/dev/ttyS8");
     strcpy(temp[1].chRCodeCOM, "/dev/ttyS9");

     temp[1] = { 0 };

  startReaderAFC(&temp);




}

Fork me on GitHub