学习计算机网络编程

一、思路和学习方法

  本文学习于: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");//  关闭与客户端的 socketclose((int)(long)arg);//  从 vpthid 中删除本线程的 idfor(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");//  关闭监听的 socketTcpServer.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>");elsesprintf(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>");elsesprintf(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 主要讲了网络测试的方法,还是比较直接但是不太深入,以后至少遇到时候知道怎么处理,学习还在比较表面,以后在项目中再继续理解。

三、总结

  最近马上就要中期答辩了,在 11 月 6 号,今天准备开始写中期答辩文档和答辩 PPT 了。最近工作先签了海康微视,做一个保底工作,后面继续学习去找一个更好的工作。或者考虑考试公务员或者事业单位,后面看情况吧,C++继续学习着,等 C++ 网络编程,数据库部分学习完,然后肝完项目,深入学习算法,理解这些知识时候再看行测和申论。继续加油。

学习C++项目—— 搭建多线程网络服务框架,性能测试(并发性能测试,业务性能测试,客户端响应时间测试,网络带宽测试)相关推荐

  1. 学习C++项目—— 搭建多进程网络服务框架,增加业务和日志,心跳机制

    学习计算机网络编程 一.思路和学习方法   本文学习于:C语言技术网(www.freecplus.net),在 b 站学习于 C 语言技术网,并加以自己的一些理解和复现,如有侵权会删除.   接下来对 ...

  2. No6-6.从零搭建spring-cloud-alibaba微服务框架,添加用户鉴权逻辑,动态数据权限(使用AOP实现)等(六,no6-6)

    代码地址与接口看总目录:[学习笔记]记录冷冷-pig项目的学习过程,大概包括Authorization Server.springcloud.Mybatis Plus~~~_清晨敲代码的博客-CSDN ...

  3. No6-3.从零搭建spring-cloud-alibaba微服务框架,实现资源端用户认证与授权等(三,no6-3)

    代码地址与接口看总目录:[学习笔记]记录冷冷-pig项目的学习过程,大概包括Authorization Server.springcloud.Mybatis Plus~~~_清晨敲代码的博客-CSDN ...

  4. 开源的网络服务框架:Apache Etch 1.4.0 发布

    Apache Etch是一个开源.跨平台.语言和传输方式独立的网络服务框架,可用来构建和使用网络服务.Etch提供的工具集包括一个网络服务描述语言.一个编译器和与各种语言进行绑定的库.它具有传输方式独 ...

  5. python网络开发框架_greenev首页、文档和下载 - Python网络服务框架 - OSCHINA - 中文开源技术交流社区...

    greenev是一个基于greenlet协程,事件驱动,非阻塞socket模型的Python网络服务框架,它使得可以编写同步的代码,却得到异步执行的优点.reactor模式采用基于epoll, kqu ...

  6. BT源代码学习心得(五):统一网络服务接口--RawServer -- 转贴自 wolfenstein (NeverSayNever)

    BT源代码学习心得(五):统一网络服务接口--RawServer author:wolfenstein 以后的部分都需要网络服务(种子文件的生成在本地就可以完成,但是通过这些种子文件下载实际的内容和提 ...

  7. python模块介绍- SocketServer 网络服务框架

    转载自http://my.oschina.net/u/1433482/blog/190612 摘要 SocketServer简化了网络服务器的编写.它有4个类:TCPServer,UDPServer, ...

  8. 手把手教你搭建自己的Java Web(Android)项目(SpringMVC + Mybatis服务端,Html5 Web端, Android客户端实现)

    刚工作不久的时候,学到了几点内容:软件产品挣的是大家的钱:内容整合是一个比较好的产品形态:可以通过广告的方式挣钱.但是就怀着这个想法,从去年12月份开始,一直想着自己搞点东西出来,即使最终没有人使用, ...

  9. java微服务项目简历_微服务框架-SpringCloud简介

    前面一篇文章谈到微服务基础框架,而Netflix的多个开源组件一起正好可以提供完整的分布式微服务基础架构环境,而对于Spring Cloud正是对Netflix的多个开源组件进一步的封装而成,同时又实 ...

最新文章

  1. boost::gregorian模块实现自出生以来的天数的测试程序
  2. 阮一峰的学习Javascript闭包(Closure)
  3. mac安装homebrew_Homebrew简介:在Mac上轻松安装任何东西的简便方法
  4. java break与continue_java中的break与continue
  5. c#chart背景透明_C#+Layui开发后台管理系统
  6. 如何让路由器摆脱安全困扰
  7. Python3.x+pycharm+Anaconda中缩小打包的.exe体积的方法
  8. 分享不可不知的CAD经典技巧
  9. Redis安装(Windows环境下Redis安装)
  10. hadoop集群搭建(系统Debian,ssh工具MobaXterm)
  11. PPT可以直接剪裁视频
  12. 脾气暴躁的 Linus 不大可能开喷修改 master
  13. 安装X61驱动 xp
  14. 中国皮卡的解禁是个错误!!
  15. git pull 出现 from the remote, but no such ref was fetched 已解决
  16. SEO面试题与面试攻略,SEO面试技巧以及常见问题分享
  17. Write Like You Talk
  18. wirshark抓包产生的pcap文件分析
  19. 【面试】815- 面试官常问的 webpack 插件
  20. 2018回顾录——花信之年

热门文章

  1. 删除指定目录下的所有文件与更改文件扩展名
  2. python开发环境
  3. 堆栈用数组和用链表实现
  4. php环境搭建及入门
  5. [Ajax] 如何使用Ajax传递多个复选框的值
  6. oc21--super
  7. gulp + angular + requirejs 简单学习
  8. 【C++】继承时构造函数和析构函数
  9. UIView局部点击(转)
  10. 使用rsync完成内网数据备份