安全传输平台项目——统一通信组件-统一共享内存组件
在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
10-安全传输平台项目-第04天(统一通信组件-统一共享内存组件)
一、复习
1、wind下制作动态库
2、linux下制作动态库
3、makefile复习
4、统一通信组件—socket通信
5、统一通信组件-服务器端实现
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include "poolsocket.h" void *start_routine(void * arg) { int ret; int timeout = 3; int connfd = (int)arg; unsigned char *out = NULL; int outlen = 0; while (1) { //服务器端端接受报文 ret = sckServer_rev(connfd, timeout, &out, &outlen); if (ret == Sck_ErrPeerClosed) { // 检测到 对端关闭,关闭本端。 printf("----------------ErrPeerClosed 关闭服务器\n"); break; } else if (ret == Sck_ErrTimeOut) { printf("---服务器检测到客户端发送数据 超时 \n"); continue; } else if (ret != 0) { printf("未知错误\n"); break; } // 处理数据。 ----- 回射 printf("====客户端发送了:%s\n", out); //服务器端发送报文 ret = sckServer_send(connfd, timeout, out, outlen); if (ret == Sck_ErrPeerClosed) { // 检测到 对端关闭,关闭本端。 printf("---ErrPeerClosed \n"); break; } else if (ret == Sck_ErrTimeOut) { printf("---服务器检测到本端发送数据 超时 \n"); continue; } else if (ret != 0) { printf("未知错误\n"); break; } } sckServer_close(connfd); return NULL; } int main(void) { int listenfd; int port = 8080; int ret = 0; int timeout = 3; int connfd = -1; pthread_t pid; //服务器端初始化 ret = sckServer_init(port, &listenfd); if (ret != 0) { printf("sckServer_init error %d\n", ret); return ret; } while (1) { ret = sckServer_accept(listenfd, timeout, &connfd); if (ret == Sck_ErrTimeOut){ printf("-----客户端连接超时----\n"); continue; } else if(ret != 0) { printf("sckServer_accept error %d\n", ret); return ret; } ret = pthread_create(&pid, NULL, start_routine, (void *)connfd); } //服务器端环境释放 sckServer_destroy(); return 0; }
二、安全传输平台项目—统一通信组件
1、客户端连接服务器
》编写client.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include "poolsocket.h" int main(void) { char *ip = "127.0.0.1"; int port = 8080; int time = 3; int connfd = -1; int ret = -1; unsigned char *data = "abcdefg"; int datalen = 5; unsigned char *out = NULL; int outlen = -1; //客户端 初始化 ret = sckClient_init(); if (ret != 0) { printf("sckClient_init error %d\n", ret); return ret; } while (1) { //客户端 连接服务器 ret = sckClient_connect(ip, port, time, &connfd); if (ret == Sck_ErrTimeOut) { printf("---客户端连接服务器 超时 \n"); continue; } else if (ret != 0) { printf("客户端连接服务器 失败: errorNO:%d\n", ret); break; } //客户端 发送报文 ret = sckClient_send(connfd, time, data, datalen); if (ret == Sck_ErrPeerClosed) { printf("---服务器关闭,客户端断开连接 \n"); break; } else if (ret == Sck_ErrTimeOut) { printf("---服务器接收数据 超时 \n"); continue; } else if (ret != 0) { printf("客户端发送数据失败:errorNO:%d\n", ret); break; } sleep(1); //客户端 接受报文 ret = sckClient_rev(connfd, time, &out, &outlen); if (ret == Sck_ErrPeerClosed) { printf("---服务器关闭,客户端断开连接 \n"); break; } else if (ret == Sck_ErrTimeOut) { printf("---服务器发送数据 超时 \n"); continue; } else if (ret != 0) { printf("客户端接收数据失败:errorNO:%d\n", ret); break; } } //客户端 关闭和服务端的连接 if (connfd != -1) sckClient_closeconn(connfd); //客户端 释放 sckClient_destroy(); return 0; }
创建目录test,把makefile、libitcastsocket.so、poolsocket.h放入test目录下。
>make
>./server
打开另一个终端,切换到test目录下,执行>./client,然后查看原终端的数据接收情况。
注意中文乱码,需要转换:
为什么会出现服务器检测到客户端发送数据超时?
2、客户端连接池连接服务器
》编写client2.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include "poolsocket.h" /* typedef struct _SCKClitPoolParam { char serverip[64]; int serverport; int bounds; //池容量 int connecttime; int sendtime; int revtime; }SCKClitPoolParam; */ void *mystart_routin(void *arg) { int ret = 0; int connfd = -1; void *handle = arg; unsigned char *data = "abcdefg"; int datalen = 5; unsigned char *out = NULL; int outlen = -1; // 获取一条连接池中的链接: ret = sckCltPool_getConnet(handle, &connfd); if (ret != 0) { printf("从连接池 获取 连接失败:%d\n", ret); return NULL; } //可以增加发送数据的次数,flag=10,flag-- while (1) { ret = sckCltPool_send(handle, connfd, data, datalen); if (ret == Sck_ErrPeerClosed) { printf("---服务器关闭,客户端断开连接 \n"); break; } else if (ret == Sck_ErrTimeOut) { printf("---服务器接收数据 超时 \n"); continue; } else if (ret != 0) { printf("客户端发送数据失败:errorNO:%d\n", ret); break; } ret = sckCltPool_rev(handle, connfd, &out, &outlen); if (ret == Sck_ErrPeerClosed) { printf("---服务器关闭,客户端断开连接 \n"); break; } else if (ret == Sck_ErrTimeOut) { printf("---服务器发送数据 超时 \n"); continue; } else if (ret != 0) { printf("客户端接收数据失败:errorNO:%d\n", ret); break; } printf("------接收到 服务器回发数据:%s\n", out); } sckCltPool_putConnet(handle, connfd, 0); return NULL; } int main(void) { int i = 0; int ret = 0; pthread_t pidArray[6] = {0}; SCKClitPoolParam clientPoolparam; strcpy(clientPoolparam.serverip, "127.0.0.1"); clientPoolparam.serverport = 8080; clientPoolparam.bounds = 10; clientPoolparam.connecttime = 3; clientPoolparam.sendtime = 3; clientPoolparam.revtime = 3; void *handle = NULL; //客户端 初始化 ret = sckCltPool_init(&handle, &clientPoolparam); if (ret != 0) { printf("sckCltPool_init error %d\n", ret); return ret; } while (1) { for (i = 0; i < 6; i++) { pthread_create(&pidArray[i], NULL, mystart_routin, handle); //需要判断返回值 } for (i = 0; i< 6; i++) { pthread_join(pidArray[i], NULL); } } //销毁连接池 sckCltPool_destroy(handle); return 0; }
>make
>./server
打开另一个终端,切换到test目录下,执行>./client,然后查看原终端的数据接收情况。
3、线程传参现象展示
>vi a_server.c
#include <unistd.h> #include <sys/types.h> #include <signal.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <pthread.h> #include "poolsocket.h" void *mystart_routine(void *arg) { int ret = 0; int timeout = 3; int connfd = (int)arg; unsigned char *out = NULL; int outlen = 0; while (1) { //服务器端端接受报文 ret = sckServer_rev(connfd, timeout, &out, &outlen); //1 if (ret == Sck_ErrPeerClosed) { //printf("aaaaa \n"); printf("服务器端检测到客户端有一条连接已关闭 \n"); break; } else if (ret == Sck_ErrTimeOut) { printf("服务器端send超时\n"); continue; } else if (ret != 0) { printf("服务器端 sckServer_send() err\n"); break; } printf("out:%s \n", out); //回射 //服务器端发送报文 ret = sckServer_send(connfd, timeout, out, outlen); if (ret == Sck_ErrPeerClosed) { sck_FreeMem((void **)&out); printf("服务器端检测到客户端有一条连接已关闭\n"); break; } else if (ret == Sck_ErrTimeOut) { sck_FreeMem((void **)&out); printf("服务器端send超时\n"); continue; } else if (ret != 0) { sck_FreeMem((void **)&out); printf("服务器端 sckServer_send() err\n"); break; } sck_FreeMem((void **)&out); } sckServer_close(connfd); return NULL; } int main() { int ret = 0; int port = 8001; int listenfd = 0; int timeout = 3; int connfd = 0; pthread_t pid; //函数声明 //服务器端初始化 ret = sckServer_init(port, &listenfd); if (ret != 0) { printf("func sckServer_init() err:%d \n", ret); return ret; } while (1) { ret = sckServer_accept(listenfd, timeout, &connfd); if (ret == Sck_ErrTimeOut) { printf("func sckServer_accept() Sck_ErrTimeOut\n"); continue; } else if (ret != 0) { ret = 2; printf("fun sckServer_accept() err :%d \n", ret); break; } pthread_create(&pid, NULL, mystart_routine, (void *)(connfd)); } //服务器端环境释放 int sckServer_destroy(); printf("hello....\n"); }
>vi a_client3err.c
#include <unistd.h> #include <sys/types.h> #include <signal.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <pthread.h> #include "poolsocket.h" typedef struct _ThreadInfo { void *handle; int iLoop; int iArrayIndex; //线程数组的下标 }ThreadInfo; void* myclient_startroutine (void *arg) { int i = 0, ret = 0; int connfd = 0; char data[64] = {0}; int datalen = 0; unsigned char *out = NULL; int outlen = 0; ThreadInfo *pThreadInfo = (ThreadInfo *)arg; void *handle = pThreadInfo->handle; // //客户端 socket池 获取一条连接 ret = sckCltPool_getConnet(handle, &connfd); if (ret != 0) { printf("func sckCltPool_getConnet() err:%d\n", ret); return NULL; } for (i=0; i<pThreadInfo->iLoop; i++) { //客户端 socket池 发送数据 memset(data, 0, sizeof(data)); sprintf(data, "第%d线程, 第%d圈", pThreadInfo->iArrayIndex, i+1); ret = sckCltPool_send(handle, connfd, data, strlen(data)); if (ret == Sck_ErrPeerClosed) { printf("sckCltPool_send 客户端检测到 服务器已经关闭 退出\n"); break; } else if (ret == Sck_ErrTimeOut) { printf(" sckCltPool_send timeout \n"); break; } else if (ret != 0) { printf("fun sckServer_rev() err:%d \n", ret); break; } //客户端 socket池 接受数据 ret = sckCltPool_rev(handle, connfd, &out, &outlen); //1 if (ret == Sck_ErrPeerClosed) { printf("sckCltPool_rev 客户端检测到 服务器已经关闭 退出\n"); break; } else if (ret == Sck_ErrTimeOut) { printf(" sckCltPool_rev timeout \n"); break; } else if (ret != 0) { printf("fun sckCltPool_rev() err:%d \n", ret); break; } printf("客户端 out:%s \n", out); sck_FreeMem((void **)&out); } //客户端 socket池 把连接放回 socket池中 sckCltPool_putConnet(handle, connfd, 0); //0正常 1 return NULL; } int main(void) { int ret = 0, i = 0; char *ip = "127.0.0.1"; int port = 8001; int time = 3; int connfd = 0; int iLoop = 0; //圈数 int iThreadNum = 0 ; //线程数 void *handle = NULL; pthread_t pidArray[1024]; ThreadInfo threadInfo; memset(&threadInfo, 0, sizeof(ThreadInfo)); SCKClitPoolParam sckClitPoolParm; memset(&sckClitPoolParm, 0, sizeof(SCKClitPoolParam)); strcpy(sckClitPoolParm.serverip, "127.0.0.1"); sckClitPoolParm.serverport = 8001; sckClitPoolParm.bounds = 10; sckClitPoolParm.connecttime = 3; sckClitPoolParm.sendtime = 3; sckClitPoolParm.revtime = 3; printf("\n请输入线程的个数: "); scanf("%d", &iThreadNum); printf("\n请输入每个线程运行圈数: "); scanf("%d", &iLoop); if (iThreadNum >= 1024) { printf("iThreadNum大于1024\n"); return 0; } //客户端 socket池初始化 ret = sckCltPool_init(&handle, &sckClitPoolParm); if (ret != 0) { printf("func sckCltPool_init() err:%d \n ", ret); return ret; } //启动多线程 for (i=0; i<iThreadNum; i++) { threadInfo.handle = handle; //alt + 鼠标键左键拖动 threadInfo.iLoop = iLoop; threadInfo.iArrayIndex = i + 1; pthread_create(&pidArray[i], NULL, myclient_startroutine, (void *)&threadInfo); } //主进程 等待子线程 结束 for (i=0; i<iThreadNum; i++) { pthread_join(pidArray[i], NULL); } //客户端 socket池 销毁连接 sckCltPool_destroy(handle); printf("client hello....\n"); return 0; }
>make
>./a_server
打开另一个终端,切换到test目录下,执行>./a_client3err,然后查看原终端的数据接收情况。
1)问题:pthread_create(&pidArray[i], NULL, myclient_startroutine, (void *)&threadInfo);中的最后一个参数需要加地址符号吗?
由于threadInfo是一个结构体,从大小及值拷贝需要的空间复杂度考虑,所以必须传入地址,不能传值。结论——结构体做参数,最好传地址!!!
2)问题:每次都是最后一个线程跑圈?
分析:pthread_create传参最后一个参数threadInfo使用了地址传参。需要看4、线程传参内存冗余法
4、线程传参内存冗余法
》内存冗余分析图:
注意:形参和局部变量地位等同,都位于栈上。
原因:线程子函数栈空间调用了main函数栈空间的值,当线程几乎同时创建时,去读取main栈空间的值时,main栈空间的值i还在变化(自增)。所以需要为每个线程子函数创建属于自己的栈空间。
>vi a_client4.c
#include <unistd.h> #include <sys/types.h> #include <signal.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <pthread.h> #include "poolsocket.h" typedef struct _ThreadInfo { void *handle; int iLoop; int iArrayIndex;//线程数组的下标 }ThreadInfo; void* myclient_startroutine (void *arg) { int i = 0, ret = 0; int connfd = 0; char data[64] = {0}; int datalen = 0; unsigned char *out = NULL; int outlen = 0; ThreadInfo *pThreadInfo = (ThreadInfo *)arg; void *handle = pThreadInfo->handle; // //客户端 socket池 获取一条连接 ret = sckCltPool_getConnet(handle, &connfd); if (ret != 0) { printf("func sckCltPool_getConnet() err:%d\n", ret); return NULL; } for (i=0; i<pThreadInfo->iLoop; i++) { //客户端 socket池 发送数据 memset(data, 0, sizeof(data)); sprintf(data, "第%d线程, 第%d圈", pThreadInfo->iArrayIndex, i+1); ret = sckCltPool_send(handle, connfd, data, strlen(data)); if (ret == Sck_ErrPeerClosed) { printf("sckCltPool_send 客户端检测到 服务器已经关闭 退出\n"); break; } else if (ret == Sck_ErrTimeOut) { printf(" sckCltPool_send timeout \n"); break; } else if (ret != 0) { printf("fun sckServer_rev() err:%d \n", ret); break; } //客户端 socket池 接受数据 ret = sckCltPool_rev(handle, connfd, &out, &outlen); //1 if (ret == Sck_ErrPeerClosed) { printf("sckCltPool_rev 客户端检测到 服务器已经关闭 退出\n"); break; } else if (ret == Sck_ErrTimeOut) { printf(" sckCltPool_rev timeout \n"); break; } else if (ret != 0) { printf("fun sckCltPool_rev() err:%d \n", ret); break; } printf("客户端 out:%s \n", out); sck_FreeMem((void **)&out); } //客户端 socket池 把连接放回 socket池中 sckCltPool_putConnet(handle, connfd, 0); //0正常 1 if (arg != NULL) free(arg); return NULL; } int main() { int ret = 0, i = 0; char *ip = "127.0.0.1"; int port = 8001; int time = 3; int connfd = 0; int iLoop = 0; //圈数 int iThreadNum = 0 ; //线程数 void *handle = NULL; pthread_t pidArray[1024]; //ThreadInfo threadInfo; //memset(&threadInfo, 0, sizeof(ThreadInfo)); SCKClitPoolParam sckClitPoolParm; memset(&sckClitPoolParm, 0, sizeof(SCKClitPoolParam)); strcpy(sckClitPoolParm.serverip, "127.0.0.1"); sckClitPoolParm.serverport = 8001; sckClitPoolParm.bounds = 20; //node: 客户端线程池的个数是10个 sckClitPoolParm.connecttime = 3; sckClitPoolParm.sendtime = 3; sckClitPoolParm.revtime = 3; printf("\n请输入线程的个数: "); scanf("%d", &iThreadNum); printf("\n请输入每个线程运行圈数: "); scanf("%d", &iLoop); if (iThreadNum >= 1024) { printf("iThreadNum大于1024\n"); return 0; } //客户端 socket池初始化 ret = sckCltPool_init(&handle, &sckClitPoolParm); if (ret != 0) { printf("func sckCltPool_init() err:%d \n ", ret); return ret; } //启动多线程 for (i=0; i<iThreadNum; i++) { ThreadInfo *pInfo = (ThreadInfo *)malloc(sizeof(ThreadInfo)); pInfo->handle = handle; //alt + 鼠标键左键拖动 pInfo->iLoop = iLoop; pInfo->iArrayIndex = i + 1; pthread_create(&pidArray[i], NULL, myclient_startroutine, (void *)pInfo); } //主进程 等待子线程 结束 for (i=0; i<iThreadNum; i++) { pthread_join(pidArray[i], NULL); } //客户端 socket池 销毁连接 sckCltPool_destroy(handle); printf("client hello....\n"); return 0; }
>make
>./a_server
打开另一个终端,切换到test目录下,执行>./a_client4,然后查看原终端的数据接收情况。
三、安全传输平台项目—统一共享内存组件
1、常见IPC
2、简单内存模型分析
3、共享内存操作函数—shmget
4、共享内存操作函数—shmat_dt
5、共享内存操作函数—shmctl
6、Linux内核管理共享内存方法
7、共享内存操作函数接口
四、客户端服务器**协商预说明
在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。