一 ini文件简介
ini文件就是后缀名为ini的文件,可以是项目的初始化配置文件,也就是说项目发布后可以根据需求修改配置文件内的信息而不用修改项目源码,以下是我这个项目的ini文件内容示例
可以看到在此ini文件中有一个标题[ConnecttionMessage] 此标题下面有两个字段 格式为字段名=字段值的形式,如上图所示。同一个ini文件中可以有多个标题,每个标题下又可以有多个字段。
二 ini文件读取
C++读取ini文件
/*
::GetPrivateProfileString(需要读取的标题,
需要读取的标题内的字段名,
如果标题内没有该字段则填充的值,
将获取到值放到变量的缓冲区中,
缓冲区大小(与前一个参数生命的缓冲区大小一致),
ini文件的路径(因为是在项目中,所以可以用相对路径));
将数据按照字符串读取,参数列表如上
另外::表示调用的是全局的函数而不是类中自己的函数
以下是将数据按照整型读取示例
::GetPrivateProfileInt(_T("ConnectionMessage"),
_T("PORT"),
-1,
_T(".\\Socket.ini"));
参数列表:标题,字段名,如果没读到设置的值,文件的路径
返回值:如果成功找到就是目标字段的值,如果没找到就是设置的值。
补充一点 如果想要写ini文件的话 用下面的函数
BOOL WritePrivateProfileString(
ini文件中的标题名
字段名
字段值(必须是CString或者LPCTSTR类型)
ini文件的路径
);
*/
CString SocketPort, ServerIP; //声明字符串
::GetPrivateProfileString(_T("ConnectionMessage"), _T("IP"), _T("Error"), ServerIP.GetBuffer(50), 50, _T(".\\Socket.ini"));
::GetPrivateProfileString(_T("ConnectionMessage"), _T("PORT"), _T("Error"), SocketPort.GetBuffer(50), 50, _T(".\\Socket.ini"));
//::GetPrivateProfileInt(_T("ConnectionMessage"), _T("PORT"),-1, _T(".\\Socket.ini"));
CStringA s;
s = ServerIP.GetBuffer(0);
/*GetBuffer()提供一个供你可以写入的内存大小,
就是申请一块内存,只不过是在CString对象的内部,
重新修改CString内存的大小和可以修改里面的内容,
参数0表示不申请内存,将内容填充至原来的CString对象中*/
serverIP = s.GetBuffer(0);
/*注意区分我的大小写,因为我这是S和s
另外为什么要这么麻烦呢CString直接转string不就行了吗?
因为这是Unicode编码,如果是多字节编码直接string=CString.GetBuffer即可*/
s.ReleaseBuffer();//注意申请了资源要释放资源
s=SocketPort.GetBuffer(0);
serverPort = s.GetBuffer(0);
s.ReleaseBuffer();
C#读取ini文件
示例ini文件填充如下
string stationPath = @".\example.ini";
/*字符串前加上@ 可以强制不转义
即\不被看作是转义字符而是是一个符号 还可以接受字符中换行*/
string res = "";
if (File.Exists(stationPath))
{
StreamReader sr = new StreamReader(stationPath, true); //为指定的文件名初始化新实例,并带有指定字节顺序的标记检测选项
while (sr.Peek() > 0) //Peek()返回下一个可用字符但是不使用它
{
res = sr.ReadLine().Trim();//因为我们的ini文件中只有一行所以只读一行并且去除后面的空格
}
sr.Close();//记住关闭流
}
string ip=res.Split(';')[0];
string username=res.Split(';')[1];
string pwd=res.Split(';')[2];
///后续继续处理
三 C++ Socket通信
服务端
example* Dlg = (example*)lpParamX;
//我这写了个线程用于通信,同时如果需要弹框需要主窗口句柄,如果不线程可忽略上句
/*定义收发长度*/
int send_len = 0;
int recv_len = 0;
/*定义收发缓冲区*/
char send_buf[200];
char recv_buf[200];
//定义套接字
SOCKET Socket,communicationSocket;
//声明连接信息结构体
SOCKADDR_IN server_addr;
/*这里SOCKADDR_IN SOCKADDR 分别对应着sockaddr_in sockaddr
*/
try{
//创建套接字
Socket = socket(AF_INET, SOCK_STREAM, 0);
if (Socket < 0)
{
Dlg->MessageBox(L"创建套接字失败");
return -1;
}
//结构体填充
const char *IP = serverIP.data();//string转char* 可以用着string.data的方式
const char * portTemp = serverPort.data();
int port = atoi(portTemp);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(port);
int a=sizeof(server_addr);
//bind(Socket, (SOCKADDR *)&server_addr, sizeof(SOCKADDR_IN));
if (bind(Socket,(const struct sockaddr*)&server_addr,a)<0)
{
Dlg->MessageBox(L"绑定失败");
return -1;
}
if (listen(Socket, 1) < 0)
{
Dlg->MessageBox(L"监听失败");
}
struct sockaddr_in clientaddr;
int sockLen = sizeof(struct sockaddr_in);
communicationSocket = accept(Socket,(SOCKADDR *)&clientaddr,&sockLen);
while (true)
{
recv_len = recv(communicationSocket, recv_buf, 200, 0);
if (recv_len < 0)
{
continue;
}
else
{
recv_buf[recv_len] = '\0';
string s = recv_buf;
CString msg;
//msg.Format(L"服务器信息: %s",s.c_str());
//msg.Format(L"服务器信息:%s", CStringW(recv_buf));
//msg.Format(L"服务器信息:%s", CStringW(recv_buf[0]));
temp = Dlg->ascll2Int(recv_buf);//这个是我自己写的函数用于将ascll字符串分割
ss = temp.c_str();
msg.Format(L"客户端消息:%s",ss);
}
}
服务器编写Socket需要注意的事项如下
1 string转char* 可以用string.data()方法
2 if(bind(Socket, (SOCKADDR *)&server_addr, sizeof(SOCKADDR_IN))<0);乍一看这个bind绑定函数没有问题,但是编译就是报错,提示<两边的值类型不匹配。找了半天才发现bind函数的重载不对虽然你写的参数都是返回值为int的bind()的参数,可是编译器可能会认为你写的是别的重载,这个可以通过鼠标放在当前这个bind函数名上查看是不是我们需要的那个重载,为了保证无误的使用我们想要的函数重载,我们可以声明一个参数类型的变量然后赋值放到函数里,如果直接将值放进去的话可能会不对。
3 if(bind(Socket, (SOCKADDR *)&server_addr, int **);中间那个变量一定要注意有&符号,否则会提示不能转换
客户端
客户端结构体填充代码
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = inet_addr(IP);
server_addr.sin_port = htons(port);
int recv_len;
//连接服务器
if (connect(Socket, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
//Dlg->MessageBox(L"套接字连接服务器失败", L"提示");
}
else
{
//Dlg->MessageBox(L"连接服务器成功", L"提示");
recv_len = recv(communicationSocket, recv_buf, 200, 0);
if (recv_len < 0)
{
continue;
}
else//收发操作
{
recv_buf[recv_len] = '\0';
string s = recv_buf;
CString msg;
//msg.Format(L"服务器信息: %s",s.c_str());
/*format函数里不能将s.c_str()直接转换为L"%s"的形式
可以在外面声明一个CString 然后赋值c_str();
format函数里的L"%s" 可以有 CStringW(char *)填充
*/
//msg.Format(L"服务器信息:%s", CStringW(recv_buf));
//msg.Format(L"服务器信息:%s", CStringW(recv_buf[0]));
temp = Dlg->ascll2Int(recv_buf);
//char tempChar = (char)temp[0];
ss = temp.c_str();
msg.Format(L"服务器信息:%s",ss);
Dlg->messageBox(msg);
}
}
四 C++ ASCLL码字符串分割 手动实现split(char a[],char str);
今天写项目遇到个要求是Socket里传的字符串是由字符的ASCLL组成
如66 55 50 51 57 48 48 54 49 44 49 52 46 48 48 48 44 50 55 50 51 46 48 48 48 44 50 53 48
B72390061,14.000,2723.000,250
分割原理如下
//将传入的ASCLL字符串转化为string,为什么字符串?因为我是Socket获取的
string ascll2String(char *ascll)
{
char *p = ascll;
char tempAscllValue[50];//大小自己定
int j = 0;
for (j = 0; j < 50; j++)
{
tempAscllValue[j] = '\0';//初始化
}
int i = 0, temp;
while (*p != '\0'&&i < 50)
{
if (*p == ' ')//传入的字符数组里有空格,移动
p++;
temp = (*p - 48) * 10;
p++;
temp = temp + (*p) - 48;//取两个字符拼成ASCLL码
//tempAscllValue[i] = *p * 10 + *(++p);
tempAscllValue[i] = char(temp);//将拼成的ASCLL转化为对应的符号
p++;
i++;
}
string s = tempAscllValue;
}
//将string按照进行分割
char piHao[20], guiGe[10], theroCount[5], theroWeight[10],realWeight[10];
int i = 0,j=0,k=0,p=0,q=0,ii=0;
int dataType = 0;//0-计划号 1-规格 2-理重 3-理论支数 4-实际重量
while (i < temp.length()) //装填数组
{
if (temp[i] == ',')
{
if (dataType == 0)
ii = i;
i++;
dataType++;
continue;
}
if (dataType == 0)
{
piHao[i] = temp[i];
}
else if (dataType == 1)
{
guiGe[j] = temp[i];
j++;
}
else if (dataType == 2)
{
theroWeight[k] = temp[i];
k++;
}
else if (dataType == 3)
{
theroCount[p] = temp[i];
p++;
}
else if (dataType == 4)
{
realWeight[q] = temp[i];
q++;
}
i++;
}
//封口
guiGe[j] = '\0';
theroCount[p] = '\0';
theroWeight[k] = '\0';
realWeight[q] = '\0';
piHao[ii] = '\0';
分割实现及测试代码 注意一点:VS中不支持使用arr[size+1]这种用法,所以我用了指针
codeBlocks可以使用上述写法,但是也不建议写
#include <iostream>
#include<vector>
using namespace std;
void split(string s, char str, vector<string>& stringVector);
int main()
{
string s1 = "123;456;789;11";
cout << "s1 len=" << s1.length() << endl;
string s2 = ";123;456;123";
string s3 = ";123,456;789;;";
string s4 = "1:2,3,456;789;;";
vector<string> res1, res2, res3;
cout << "start" << endl;
split(s1, ';', res1);
split(s2, ';', res2);
split(s3, ';', res3);
int i = 0;
cout << "*************res1*****************" << endl;
while (i < res1.size())
{
cout << "分割后的第" << i << "个:" << res1[i] << endl;
i++;
}
i = 0;
cout << "*************res2*****************" << endl;
while (i < res2.size())
{
cout << "分割后的第" << i << "个:" << res2[i] << endl;
i++;
}
i = 0;
cout << "*************res3*****************" << endl;
while (i < res3.size())
{
cout << "分割后的第" << i << "个:" << res3[i] << endl;
i++;
}
return 0;
}
//待分割的string字符串,目标字符,分割后的存储位置
void split(string s, char str, vector<string>& stringVector)
{
int i = 0;
char* start = &s[0];
char* endStr = &s[0];
while (i < s.length())
{
if (s[i] == str)
{
int size = endStr - start;
char *arr =new char[size + 1];
int j = 0;
while (start != endStr)//此时endStr==str所以是!=
{
arr[j] = *start;
j++;
start++;
}
arr[j] = '\0';
i++;
if (i < s.length())
{
endStr++;
start = endStr;
}
if (arr[0] != '\0')
stringVector.push_back(arr);
delete[]arr;
arr = NULL;
//cout<<"分割一次"<<endl;
}
else
{
i++;//13
if (i < s.length())
endStr++;
else//判断当前endStr所指向的是str还是其他字符
{
if (*endStr == str)
{
int size = endStr - start;
char* arr = new char[size + 1];
int j = 0;
while (start != endStr)
{
arr[j] = *start;
j++;
start++;
}
arr[j] = '\0';
stringVector.push_back(arr);
delete[]arr;
arr = NULL;
}
else
{
const int size = endStr - start;
/*此时endStr指向的是最后一个非str字符
因为也需要存在所以长度要+1
*/
char* arr = new char[size + 2];//再次+1 预留'\0'
int j = 0;
while (j <= size)//不能判断start<=endStr 或者start!=endStr
{
arr[j] = *start;
j++;
start++;
}
arr[j] = '\0';
stringVector.push_back(arr);
delete[]arr;
arr = NULL;
}
}
// cout<<"未分割"<<endl;
}
}
}