学习计算机网络编程
一、思路和学习方法
本文学习于:C语言技术网(www.freecplus.net),在 b 站学习于 C 语言技术网,并加以自己的一些理解和复现,如有侵权会删除。
接下来对网络编程继续深入学习。通过上篇文章学习,感觉对每个点都记录会很花费时间,但是不记录又对有些地方理解一知半解,综合考虑,先运行出来,对每行代码如何执行要明白,实现什么功能也要明白,freecplus 框架里面知识,后面再仔细学习。
二、网络编程继续深入
2.1 搭建多线程网络服务框架
使用多线程方式搭建网络服务框架,在实际应用中会广泛一些,但是难度也会高一些。下面开始进行学习,在这之前有一些前置知识,要对多线程网络通信等知识进行学习。其中服务端程序如下,
/*
* 程序功能:
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include "_freecplus.h"
void *pthmain(void * arg); // 线程主函数
vector<long> vpthid; // 存放线程 id 的容器
void mainexit(int sig); // 信号 2 和 15 的处理函数
void pthmainexit(void * arg); // 线程清理函数
CLogFile logfile;
CTcpServer TcpServer; // 创建服务端对象
// 处理业务的主函数
bool _main(const char *strrecvbuffer, char *strsendbuffer);
// 心跳报文
bool biz000(const char *strrecvbuffer, char *strsendbuffer);
// 身份验证业务处理函数
bool biz001(const char *strrecvbuffer, char *strsendbuffer);
// 查询余业务处理函数
bool biz002(const char *strrecvbuffer, char *strsendbuffer);
int main(int argc, char *argv[]){
if(argc != 3){
printf("Using:./ExitAndFreeServer port logfile\nExample:./ExitAndFreeServer 5005 /tmp/ExitAndFreeServer.log\n\n"); return -1;
}
// 关闭全部的信号,也把僵尸进程关闭
for(int ii = 0; ii <= 64; ii++) signal(ii, SIG_IGN);
// 打开日志文件
if(logfile.Open(argv[2], "a+") == false){
printf("logfile.Open(%s) failed.\n", argv[2]); return -1;
}
// 设置信号,在 shell 状态下可用 “kill + 进程号”正常终止些进程 Ctrl + c
// 但请不要用 “kill -9 + 进程号”
signal(SIGINT, mainexit); signal(SIGTERM, mainexit);
// 初始化 TcpServer 的通信端口
if(TcpServer.InitServer(atoi(argv[1])) == false){
logfile.Write("TcpServer.InitServer(%s) failed. \n", argv[1]); return -1;
}
while(true){
if(TcpServer.Accept() == false){ // 等待客户端连接
logfile.Write("TcpServer.Accept() failed. \n"); continue;
}
// 以下是子进程,负责与客户端通信
logfile.Write("客户端(%s)已连接。 \n", TcpServer.GetIP());
pthread_t pthid;
if(pthread_create(& pthid, NULL, pthmain, (void *)(long)TcpServer.m_connfd) != 0){
logfile.Write("pthread_create failed. \n"); return -1;
}
vpthid.push_back(pthid); // 把线程 id 保存到 vpthid 容器中
}
return 0;
}
void *pthmain(void * arg){
pthread_cleanup_push(pthmainexit, arg); // 设置线程清理函数
pthread_detach(pthread_self()); // 分离线程
pthread_setcanceltype(PTHREAD_CANCEL_DISABLE, NULL); // 设置取消方式为立即取消
int socket = (int)(long)arg; // 客户端的 socket 连接
int ibuflen = 0;
char strrecvbuffer[1024], strsendbuffer[1024]; // 存放数据的缓冲区
while(true){
memset(strrecvbuffer, 0, sizeof(strrecvbuffer));
memset(strsendbuffer, 0, sizeof(strsendbuffer));
// 接收客户端发过来的请求报文
if(TcpRead(socket, strrecvbuffer, &ibuflen, 50) == false) break;
logfile.Write("接收:%s \n", strrecvbuffer);
// 处理业务的主函数
if(_main(strrecvbuffer, strsendbuffer) == false) break;
logfile.Write("发送:%s \n", strsendbuffer);
if(TcpWrite(socket, strsendbuffer) == false) break; // 向客户端回应报文
}
pthread_cleanup_pop(1);
pthread_exit(0);
}
void pthmainexit(void * arg){
logfile.Write("pthmainexit begin.\n");
// 关闭与客户端的 socket
close((int)(long)arg);
// 从 vpthid 中删除本线程的 id
for(int ii = 0; ii < vpthid.size(); ii++){
if(vpthid[ii] == pthread_self()){
vpthid.erase(vpthid.begin() + ii);
}
}
logfile.Write("pthmainexit end.\n");
}
// 信号 2 和 15 的处理函数
void mainexit(int sig){
logfile.Write("mainexit begin. \n");
// 关闭监听的 socket
TcpServer.CloseListen();
// 取消全部的线程
for(int ii = 0; ii < vpthid.size(); ii++){
logfile.Write("cancel %ld\n", vpthid[ii]);
pthread_cancel(vpthid[ii]);
}
logfile.Write("mainexit end.\n");
exit(0);
}
bool _main(const char * strrecvbuffer, char * strsendbuffer){ // 处理业务的主函数
int ibizcode = -1;
GetXMLBuffer(strrecvbuffer, "bizcode", &ibizcode);
switch(ibizcode){
case 0: // 心跳
biz000(strrecvbuffer, strsendbuffer); break;
case 1: // 身份验证
biz001(strrecvbuffer, strsendbuffer); break;
case 2: // 余额查询
biz002(strrecvbuffer, strsendbuffer); break;
default:
logfile.Write("非法报文:%s\n", strrecvbuffer); return false;
}
return true;
}
// 身份验证业务处理函数
bool biz001(const char * strrecvbuffer, char * strsendbuffer){
char username[51], password[51];
memset(username, 0, sizeof(username));
memset(password, 0, sizeof(password));
GetXMLBuffer(strrecvbuffer, "username", username, 50);
GetXMLBuffer(strrecvbuffer, "password", password, 50);
if( (strcmp(username, "wucz") == 0) && (strcmp(password, "p@ssw0rd") == 0) )
sprintf(strsendbuffer, "<retcode>0</retcode><message>成功。</message>");
else
sprintf(strsendbuffer, "<retcode>-1</retcode><message>用户名或密码不正确。</message>");
return true;
}
bool biz002(const char *strrecvbuffer, char *strsendbuffer){
char cardid[51];
memset(cardid, 0, sizeof(cardid));
GetXMLBuffer(strrecvbuffer, "cardid", cardid, 50);
if(strcmp(cardid, "62620000000001") == 0)
sprintf(strsendbuffer, "<retcode>0</retcode><message>成功。</message><ye>100.50</ye>");
else
sprintf(strsendbuffer, "<retcode>-1</retcode><message>卡号不存在。</message>");
return true;
}
bool biz000(const char *strrecvbuffer, char *strsendbuffer){
sprintf(strsendbuffer, "<retcode>0</retcode><message>成功。</message>");
return true;
}
客户端程序不变化,然后运行结果如下,
可以看出实现功能满足和多进程实现的一样。里面线程的一些知识,在后面会继续学习,但是其功能能够大致看懂。接下来继续学习。
其中要注意的点有,使用多进程中和 socketfd 和多线程里面的 socketfd 是不一样的,因此用两种方式来进行通信。这些在 up 主前面视频都讲过。
2.2 性能测试的重要性
在实际项目开发中,除了完成程序的功能,还需要测试性能。 在充分了解服务端的性能后,才能决定如何选择服务端的框架,还有网络带宽、硬件配置等。 服务端的性能指标是面试中必问的。如果不了解性能指标,面试官会认为你没有实际开发经验或对网络编程一知半解。主要性能指标如下:1. 服务端的并发能力;(可以同时响应多少业务) 2. 服务端的业务处理能力;(每段时间可以响应多少业务请求) 3. 客户端业务响应时效;(响应需要的时间) 4. 网络宽带。(和网络流量请求)
2.3 服务端并发性能测试
服务端最大并发量,即可以接受客户端连接的最大数量。注意客户端业务请求不要太频繁。重视 CPU 和内存使用率的变化(磁盘 I/O,网络 I/O)。在性能测试时,最好是客户端用一台独立的虚拟机,服务端测试程序用另外一台独立的虚拟机,不然会导致测试不准确。为了学习过程,我就在同一台虚拟机上面跑了,以后在正式测试中再使用其他方法。这里需要注意几个语句,
// 查看进程数量
ps -ef |grep ExitAndFreeServer|wc
// 运行 sh 脚本文件,和 touch 配合使用
touch test.sh // 加入运行脚本文件
sh test.h
// 查看资源内存
free -m
// 查看 cpu 资源
top
其中客户端程序改为如下,只有心跳程序开启,然后运行,
/*
* 程序功能:
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include "_freecplus.h"
CTcpClient TcpClient; // 创建服务端对象
bool biz000(); // 发送心跳报文
bool biz001(); // 身份验证
bool biz002(); // 余额查询
int main(int argc, char *argv[]){
if(argc != 3){
printf("Using:./client ip port\n Example:./client 127.0.0.1 5005\n\n"); return -1;
}
if(TcpClient.ConnectToServer(argv[1], atoi(argv[2])) == false){ // 向服务端发起连接请求
printf("TcpClient.ConnectToServer(\"%s\", %s) failed.\n", argv[1], argv[2]); return -1;
}
/*
// 身份验证
if(biz001() == false){
printf("biz001() failed.\n"); return -1;
}
sleep(10);
biz002(); // 余额查询
sleep(5);
biz002(); // 余额查询
*/
for(int ii = 0; ii < 10; ii++){
if(biz000() == false) break;
sleep(10);
}
// 程序直接退出,析构函数会释放资源
}
bool biz001(){ //
char strbuffer[1024]; // 存放数据的缓存区
memset(strbuffer,0,sizeof(strbuffer));
snprintf(strbuffer, 1000, "<bizcode>1</bizcode><username>wucz</username><password>p@ssw0rd</password>");
printf("发送:%s\n",strbuffer);
if (TcpClient.Write(strbuffer) == false) return false; // 向服务端发送请求报文
memset(strbuffer,0,sizeof(strbuffer));
if (TcpClient.Read(strbuffer, 20) == false) return false; // 接收服务端的回应报文
printf("接收:%s\n",strbuffer);
int iretcode = -1;
GetXMLBuffer(strbuffer, "retcode", &iretcode);
if(iretcode == 0){printf("身份验证成功。\n"); return true;}
printf("身份验证失败。\n");
return false;
}
bool biz002(){
char strbuffer[1024]; // 存放数据的缓冲区
snprintf(strbuffer, 1000, "<bizcode>2</bizcode><cardid>62620000000001</cardid>");
printf("发送:%s\n", strbuffer);
if(TcpClient.Write(strbuffer) == false) return false; // 向服务端发送请求报文
memset(strbuffer, 0, sizeof(strbuffer));
if(TcpClient.Read(strbuffer, 20) == false) return false; // 接收服务端的回应报文
printf("接收:%s\n", strbuffer);
int iretcode = -1;
GetXMLBuffer(strbuffer, "retcode", &iretcode);
if(iretcode == 0) {printf("查询余额成功。\n"); return true; }
printf("查询余额成功。\n");
return true;
}
bool biz000(){ // 发送心跳报文
char strbuffer[1024]; // 存放数据的缓存区
memset(strbuffer,0,sizeof(strbuffer));
snprintf(strbuffer, 1000, "<bizcode>0</bizcode>");
// printf("发送:%s\n",strbuffer);
if (TcpClient.Write(strbuffer) == false) return false; // 向服务端发送请求报文
memset(strbuffer,0,sizeof(strbuffer));
if (TcpClient.Read(strbuffer,20) == false) return false; // 接收服务端的回应报文
// printf("接收:%s\n",strbuffer);
return true;
}
在这里建立了一个 sh 文件脚本,把客户端程序运行个数 * 4000 ,然后放在 test.sh 文件脚本里,其效果如下。一定要注意的一点,在测试中,最高一次性不要太多,一点一点的加,我为了出效果,直接运行那么多,
在同一台虚拟机运行,第一次运行 test.sh 文件时,虚拟机开始有点卡;后来第二次运行 test.sh 文件时,资源不够,然后运行卡壳,系统承载不了那么多进程,其运行结果如下,
2.4 服务端业务性能测试
服务端最大业务处理能力,即每秒可以处理的业务请求数量。注重客户端的数量不要太多。重视 CPU 和内存使用率的变化。
这里对客户端程序改为,
for(int ii = 0; ii < 2000; ii++){
if(biz000() == false) break;
usleep(100000);
}
把 sh 文件中客户端程序运行脚本改为,
运行结果如下,
可以看出 CPU 消耗和内存消耗,及其进程的情况。然后为了测试每秒接受信号情况,对接收进行抓包,使用 linux 语句为,
grep "2021-10-30 22:11:19 接收" /tmp/ExitAndFreeServer.log|wc
这是在运行过一段时间以后的了,有些客户端程序运行结束了,少了一些,所以运行结果如下,
这就是从日志里面抓取的数据接收情况,分析上面运行结果就能够知道,在当下 19 s 接收的情况,相当于 1s 有 60 条记录。可以看出,这样客户端的压力还不够,继续加压,把测试客户端程序改为,
// 用 killall client 杀死所有相关进程
for(int ii = 0; ii < 2000; ii++){
if(biz000() == false) break;
usleep(10000);
}
再运行 test.sh 脚本,在运行两次以后,观察到结果如下,
能够看到,这样测试时候,CPU 资源已经被占用的还剩 16% 多了,现在压力已经相当大了,也可以查看到相应的进程数和内存情况。在过一会后又恢复到了正常情况。
为了能够看出处理业务情况,查看服务端数据报文,然后看 1 s 中报文情况,使用语句
grep "2021-10-30 22:41:25 接收" /tmp/ExitAndFreeServer.log|wc
grep "2021-10-30 22:41:21 接收" /tmp/ExitAndFreeServer.log|wc
使用语句查看服务端报文情况结果如下
可以看出,在 1s 中接收到 18107 报文,然后 CPU 还剩资源 10% 多,因此知道服务端压力差不多是 18107 。
2.5 多线程/线程服务端性能差异
用相同的方法来测试多进程/多线程。测试的方法是一样的,没有什么变化。需要关注内存,CPU 的情况,这里为了学习进度就不进行实践了。得出结论,多线程比多进程在 CPU 和内存消耗情况都是占有优势的。
2.6 测试客户端的响应时间
客户端业务的响应时间,即是发出业务请求与收到服务端回应的时间间隔,关系到用户的体验。测试环境:1. 业务的闲时时/忙时;2. 不同的网络环境(局域网、互联网、移动通信网络)。需要测试需要一个计时器,需要精确到 微秒。
这里测试每个业务运行时间,用到了计时器功能,freecplus 把计时器封装好了,然后直接使用就行。用了定时器,来查看每个业务运行的时间,其中客户端程序如下,
/*
* 程序功能:
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include "_freecplus.h"
CTcpClient TcpClient; // 创建服务端对象
bool biz000(); // 发送心跳报文
bool biz001(); // 身份验证
bool biz002(); // 余额查询
int main(int argc, char *argv[]){
if(argc != 3){
printf("Using:./client ip port\n Example:./client 127.0.0.1 5005\n\n"); return -1;
}
CTimer Timer;
if(TcpClient.ConnectToServer(argv[1], atoi(argv[2])) == false){ // 向服务端发起连接请求
printf("TcpClient.ConnectToServer(\"%s\", %s) failed.\n", argv[1], argv[2]); return -1;
}
printf("TcpClient.ConnectToServer() 耗时%lf\n", Timer.Elapsed());
// 身份验证
biz001();
printf("biz001() 耗时%lf\n", Timer.Elapsed());
biz002(); // 余额查询
printf("biz002() 耗时%lf\n", Timer.Elapsed());
biz000(); // 余额查询
printf("biz000() 耗时%lf\n", Timer.Elapsed());
// 程序直接退出,析构函数会释放资源
}
bool biz001(){ //
char strbuffer[1024]; // 存放数据的缓存区
memset(strbuffer, 0, sizeof(strbuffer));
snprintf(strbuffer, 1000, "<bizcode>1</bizcode><username>wucz</username><password>p@ssw0rd</password>");
// printf("发送:%s\n",strbuffer);
if (TcpClient.Write(strbuffer) == false) return false; // 向服务端发送请求报文
memset(strbuffer,0,sizeof(strbuffer));
if (TcpClient.Read(strbuffer, 20) == false) return false; // 接收服务端的回应报文
// printf("接收:%s\n",strbuffer);
int iretcode = -1;
GetXMLBuffer(strbuffer, "retcode", &iretcode);
if(iretcode == 0)return true; //{ printf("身份验证成功。\n"); }
// printf("身份验证失败。\n");
return false;
}
bool biz002(){
char strbuffer[1024]; // 存放数据的缓冲区
snprintf(strbuffer, 1000, "<bizcode>2</bizcode><cardid>62620000000001</cardid>");
// printf("发送:%s\n", strbuffer);
if(TcpClient.Write(strbuffer) == false) return false; // 向服务端发送请求报文
memset(strbuffer, 0, sizeof(strbuffer));
if(TcpClient.Read(strbuffer, 20) == false) return false; // 接收服务端的回应报文
// printf("接收:%s\n", strbuffer);
int iretcode = -1;
GetXMLBuffer(strbuffer, "retcode", &iretcode);
if(iretcode == 0)return true; // {printf("查询余额成功。\n"); return true; }
// printf("查询余额成功。\n");
return true;
}
bool biz000(){ // 发送心跳报文
char strbuffer[1024]; // 存放数据的缓存区
memset(strbuffer,0,sizeof(strbuffer));
snprintf(strbuffer, 1000, "<bizcode>0</bizcode>");
// printf("发送:%s\n",strbuffer);
if (TcpClient.Write(strbuffer) == false) return false; // 向服务端发送请求报文
memset(strbuffer,0,sizeof(strbuffer));
if (TcpClient.Read(strbuffer,20) == false) return false; // 接收服务端的回应报文
// printf("接收:%s\n",strbuffer);
return true;
}
结果如下所示,
2.7 网络带宽测试
测试的目的是根据业务的 需求,判断出对网络带宽的要求。测试网络带宽,参考这篇文章:https://www.linuxprobe.com/speedtest-network-in-linux.html。测试网络带宽能承载的业务量,不同的业务对宽带的利用率不一样。要求测试环境的各环节不能存在性能的瓶颈,唯一瓶颈就是网络带宽。注意是只发送数据,不接受回应;上行和下行分开测试。 其中测试服务端程序为,
/*
* 程序功能:
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include "_freecplus.h"
CTcpServer TcpServer; // 创建服务端对象
// 程序退出时调用的函数
void FathEXIT(int sig); // 父进程退出函数
void ChldEXIT(int sig); // 子进程退出函数
int main(int argc, char *argv[]){
// 关闭全部的信号,也把僵尸进程关闭
for(int ii = 0; ii <= 64; ii++) signal(ii, SIG_IGN);
// 设置信号,在 shell 状态下可用 “kill + 进程号”正常终止些进程 Ctrl + c
// 但请不要用 “kill -9 + 进程号”
signal(SIGINT, FathEXIT); signal(SIGTERM, FathEXIT);
// 初始化 TcpServer 的通信端口
if(TcpServer.InitServer(5005) == false){
printf("TcpServer.InitServer(5005) failed. \n"); FathEXIT(-1);
}
while(true){
if(TcpServer.Accept() == false){ // 等待客户端连接
printf("TcpServer.Accept() failed. \n"); continue;
}
// 父进程返回到循环首部
if(fork() > 0){TcpServer.CloseClient(); continue; }
// 子进程重新设置退出信号
signal(SIGINT, ChldEXIT); signal(SIGTERM, ChldEXIT);
TcpServer.CloseListen();
// 以下是子进程,负责与客户端通信
printf("客户端(%s)已连接。 \n", TcpServer.GetIP());
char strbuffer[1024]; // 存放数据的缓冲区
while(true){
memset(strbuffer, 0, sizeof(strbuffer));
// 接收客户端发过来的请求报文
if(TcpServer.Read(strbuffer, 50) == false) break;
printf("接收:%s \n", strbuffer);
strcat(strbuffer, "ok"); // 在客户端的报文后加上“ok”
printf("发送:%s \n", strbuffer);
if(TcpServer.Write(strbuffer) == false)break; // 向客户端回应报文
}
printf("客户端已断开。 \n"); // 程序直接退出,析构函数会释放资源
ChldEXIT(-1); // 通信完成后,子进程退出。
}
}
void FathEXIT(int sig){ // 父进程退出函数
if(sig > 0){
// 免除不再受到其他信号的打扰
signal(sig, SIG_IGN);signal(SIGINT, SIG_IGN);signal(SIGTERM, SIG_IGN);
printf("catching the signal(%d). \n", sig);
}
kill(0, 15); // 通知其它的子进程退出。
printf("父进程退出。 \n");
// 编写善后代码(释放资源、提交或回滚事务)
TcpServer.CloseClient();
exit(0);
}
void ChldEXIT(int sig){ // 子进程退出函数
if(sig > 0){
signal(sig, SIG_IGN);signal(SIGINT, SIG_IGN);signal(SIGTERM, SIG_IGN);
}
printf("子进程退出。 \n");
// 编写善后代码(释放资源、提交或回滚事务)
TcpServer.CloseClient();
exit(0);
}
测试客户端程序为,
/*
* 程序功能:
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include "_freecplus.h"
CTcpClient TcpClient; // 创建服务端对象
bool biz000(); // 发送心跳报文
bool biz001(); // 身份验证
bool biz002(); // 余额查询
int main(int argc, char *argv[]){
if(argc != 3){
printf("Using:./client ip port\n Example:./client 127.0.0.1 5005\n\n"); return -1;
}
CTimer Timer;
if(TcpClient.ConnectToServer(argv[1], atoi(argv[2])) == false){ // 向服务端发起连接请求
printf("TcpClient.ConnectToServer(\"%s\", %s) failed.\n", argv[1], argv[2]); return -1;
}
printf("TcpClient.ConnectToServer() 耗时%lf\n", Timer.Elapsed());
for(int ii = 0; ii < 10000; ii++){
biz001();
}
// 身份验证
biz001();
printf("biz001() 耗时%lf\n", Timer.Elapsed());
return 0;
biz002(); // 余额查询
printf("biz002() 耗时%lf\n", Timer.Elapsed());
biz000(); // 余额查询
printf("biz000() 耗时%lf\n", Timer.Elapsed());
// 程序直接退出,析构函数会释放资源
}
bool biz001(){ //
char strbuffer[1024]; // 存放数据的缓存区
memset(strbuffer, 0, sizeof(strbuffer));
snprintf(strbuffer, 1000, "<bizcode>1</bizcode><username>wucz</username><password>p@ssw0rd</password>");
// printf("发送:%s\n",strbuffer);
if (TcpClient.Write(strbuffer) == false) return false; // 向服务端发送请求报文
memset(strbuffer,0,sizeof(strbuffer));
if (TcpClient.Read(strbuffer, 20) == false) return false; // 接收服务端的回应报文
// printf("接收:%s\n",strbuffer);
int iretcode = -1;
GetXMLBuffer(strbuffer, "retcode", &iretcode);
if(iretcode == 0)return true; //{ printf("身份验证成功。\n"); }
// printf("身份验证失败。\n");
return false;
}
bool biz002(){
char strbuffer[1024]; // 存放数据的缓冲区
snprintf(strbuffer, 1000, "<bizcode>2</bizcode><cardid>62620000000001</cardid>");
// printf("发送:%s\n", strbuffer);
if(TcpClient.Write(strbuffer) == false) return false; // 向服务端发送请求报文
memset(strbuffer, 0, sizeof(strbuffer));
if(TcpClient.Read(strbuffer, 20) == false) return false; // 接收服务端的回应报文
// printf("接收:%s\n", strbuffer);
int iretcode = -1;
GetXMLBuffer(strbuffer, "retcode", &iretcode);
if(iretcode == 0)return true; // {printf("查询余额成功。\n"); return true; }
// printf("查询余额成功。\n");
return true;
}
bool biz000(){ // 发送心跳报文
char strbuffer[1024]; // 存放数据的缓存区
memset(strbuffer,0,sizeof(strbuffer));
snprintf(strbuffer, 1000, "<bizcode>0</bizcode>");
// printf("发送:%s\n",strbuffer);
if (TcpClient.Write(strbuffer) == false) return false; // 向服务端发送请求报文
memset(strbuffer,0,sizeof(strbuffer));
if (TcpClient.Read(strbuffer,20) == false) return false; // 接收服务端的回应报文
// printf("接收:%s\n",strbuffer);
return true;
}
运行结果如下,
可以看出在双向发送和接收数据时,发送 10000 个心跳业务双向时间情况。现在改为单向测试,客户端只发送不接收了;服务端只接收不发送了。
// 服务端
while(true){
memset(strbuffer, 0, sizeof(strbuffer));
// 接收客户端发过来的请求报文
if(TcpServer.Read(strbuffer, 50) == false) break;
printf("接收:%s \n", strbuffer);
/*
strcat(strbuffer, "ok"); // 在客户端的报文后加上“ok”
printf("发送:%s \n", strbuffer);
if(TcpServer.Write(strbuffer) == false)break; // 向客户端回应报文
*/
}
// 客户端
bool biz001(){ //
char strbuffer[1024]; // 存放数据的缓存区
memset(strbuffer, 0, sizeof(strbuffer));
snprintf(strbuffer, 1000, "<bizcode>1</bizcode><username>wucz</username><password>p@ssw0rd</password>");
// printf("发送:%s\n",strbuffer);
if (TcpClient.Write(strbuffer) == false) return false; // 向服务端发送请求报文
return true;
memset(strbuffer,0,sizeof(strbuffer));
if (TcpClient.Read(strbuffer, 20) == false) return false; // 接收服务端的回应报文
// printf("接收:%s\n",strbuffer);
int iretcode = -1;
GetXMLBuffer(strbuffer, "retcode", &iretcode);
if(iretcode == 0)return true; //{ printf("身份验证成功。\n"); }
// printf("身份验证失败。\n");
return false;
}
现在查看运行效果,
可以看出单向测试效果还是不错的,直接体现了 TCP 单向传输带宽业务情况。我感觉 UP 主要讲了网络测试的方法,还是比较直接但是不太深入,以后至少遇到时候知道怎么处理,学习还在比较表面,以后在项目中再继续理解。
三、总结
后续继续续学习网络编程知识,现在进行了多线程服务框架测试,能够更好了解网络编程的性能基本情况。