在学习安全传输平台项目总结了笔记,并分享出来。
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;
}
server.c
二、安全传输平台项目—统一通信组件
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;
}
client.c
创建目录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;
}
client2.c
>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");
}
a_server.c
>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;
}
a_client3err.c
>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;
}
a_client4.c
>make
>./a_server
打开另一个终端,切换到test目录下,执行>./a_client4,然后查看原终端的数据接收情况。
三、安全传输平台项目—统一共享内存组件
1、常见IPC
2、简单内存模型分析
3、共享内存操作函数—shmget
4、共享内存操作函数—shmat_dt
5、共享内存操作函数—shmctl
6、Linux内核管理共享内存方法
7、共享内存操作函数接口
四、客户端服务器密钥协商预说明
在学习安全传输平台项目总结了笔记,并分享出来。