进程通信:一个进程不能读写另一个进程的内存,所以这儿要使用进程通信。
操作系统中,一个进程不能读写(访问)另一个进程的内存空间,进程的内存空间使用是各自独立的。但C语言有个特点,即C语言可以把所有的设备都当做一个“文件”来操作。C语言可以把一片内存空间当作一个设备,即文件来操作,这时别的进程也可以读取这片内存空间,从而达到数据共享,进程通信的目底。
进程通信有下图所示的几种方式:
下面代码实现了多线程的管道通信:
客户端随生成二个随机数,连接到管道并把随机数发给服务器,服务从管道中获得数据计算好后把结果返还给客户端,而服务器端开了多线程可以提供多任务处理功能,客户端的代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<Windows.h>
#include<time.h>
#define SIZE 4096 //管道大小
char pipename[128] = "\\\\.\\pipe\\test"; //以设备命名,注意命名书写方式 管道分匿名和命名管道
HANDLE m_pipe = NULL; //命名管道的句柄
int a;
int b;
//生成二个随机数,在云端计算
void run()
{
time_t ts;
unsigned int num = time(&ts);
srand(num);
a = rand() % 1000;
b = rand() % 1000;
}
void main()
{
printf("连接服务器...\n");
m_pipe = CreateFileA(pipename,//名称
GENERIC_WRITE | GENERIC_READ, //读写
0, //1:不共享属性 0:共享,并发
NULL, //默认安全属性
OPEN_EXISTING, //打开已经存在的
FILE_ATTRIBUTE_NORMAL,
NULL);
if (m_pipe == INVALID_HANDLE_VALUE)
{
printf("打开失败!\n");
return;
}
int nwrite = 0;
int nread = 0;
run();
char winfo[1024] = { 0 };
sprintf(winfo, "%d %d", a, b); //打印数据进去
WriteFile(m_pipe, winfo, strlen(winfo), &nwrite, NULL);// 写入
//因为又要读出,所以把这片区域清零
memset(winfo, 0, sizeof(winfo));
ReadFile(m_pipe,winfo,1024,&nread, NULL);
int res;
sscanf(winfo, "%d", &res); //从字符串中扫描出来数据
printf("\n%d+%d=%d\n", a, b, res);
system("pause");
}
下面是服务器端,服务器从管道中获取客户写入的数据,计算出结果后再写入管道,因为服务器开了多线程,所以可以同时处理多个客户端的请求,这也是云计算的模型,这个也可以称为云端服务器。
/*
一个客户请求我们就创建一个线程进行响应。
如果总是阻塞就没办发实现并发处理多客户请求。
所以我们这儿就使用多线程来处理。
*/
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<Windows.h>
#define SIZE 4096 //管道大小
#define MAX_CONNECT 128 //最大的链接
int startthreadnum = 10; //一开始开辟10个线程
char pipename[128] = "\\\\.\\pipe\\test"; //以设备命名,注意命名书写方式 管道分匿名和命名管道
typedef struct info
{
int id;
HANDLE hthread; //线程
HANDLE hpipe; //管道,一个线程必须要有一个管道
HANDLE hevent; //需要使用消息机制,发一条消息
}PIPE_ST;
PIPE_ST pipeist[MAX_CONNECT]; //128个结构体,即最大128个线程
//线程
DWORD WINAPI severThread(void *p)
{
DWORD nread = 0;
DWORD nwrite = 0;
DWORD dwbyte = 0; //
char szbuf[SIZE] = { 0 };
PIPE_ST curpipe = *(PIPE_ST*)p; //获取当前结构体
OVERLAPPED overlap = {0,0,0,0,curpipe.hevent}; //初始化结构体
//处理数据
while (1)
{
memset(szbuf, 0, sizeof(szbuf));
ConnectNamedPipe(curpipe.hpipe, &overlap); //链接管道,把信息写到结构体
WaitForSingleObject(curpipe.hevent, INFINITE); //等待事件
printf("%d:线程开始处理...\n",curpipe.id);
//需要做二个判断 1:什么时间跳出循环
//检测 I/0,如果完成就跳出
if (!GetOverlappedResult(curpipe.hpipe, &overlap, &dwbyte, TRUE))
{
break;
}
if (!ReadFile(curpipe.hpipe, szbuf, SIZE, &nread, NULL))
{
puts("read fail");
break;
}
int a, b;
sscanf(szbuf, "%d %d", &a, &b);
memset(szbuf, 0, sizeof(szbuf)); //清零
sprintf(szbuf, "%d", a + b); //打印到szbuf中
WriteFile(curpipe.hpipe,szbuf,strlen(szbuf),&nread,NULL); //写回管道中
//写完后要断开链接
DisconnectNamedPipe(curpipe.hpipe);
}
return 0;
}
//开启服务,这些线程是一产次性开完?还是等到客户端有链接才会开?
void start()
{
for (int i = 0; i < startthreadnum; i++)
{
//创建管道
pipeist[i].hpipe = CreateNamedPipeA(
pipename,//管道名称
PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, //管道读写属性
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, //消息模式,读模式,等待模式阻塞
startthreadnum, //先开10
0, //输出缓冲区大小,为 0 实时生效
0, //输入缓冲区大小 为 0 实时生效
3000, //超时,3秒等待
NULL); //安全
pipeist[i].id = i;
if (pipeist[i].hpipe == INVALID_HANDLE_VALUE)
{
printf("%d 创建失败!\n",i);
return;
}
printf("%d 线程运行......\n", i);
//创建事件 处理完了就释放等待下个 线程
pipeist[i].hevent = CreateEventA(NULL, FALSE, FALSE, FALSE);
//创建线程
pipeist[i].hthread = CreateThread(NULL, 0, severThread, &pipeist[i], 0, NULL);
}
printf("sever start...\n");
}
//结束服务
void end()
{
}
void main()
{
start();
system("pause");
}
下面提供一个压力测试的代码:
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
void main()
{
//system() 不行,不能异步。
while (1)
{
for (int i = 0; i < 100; i++)
{
//system("I:\\fudan\\C\\Library\\黑马\\CGI\\管道SERVER\\Debug\\客户端.exe");
//ShellExecute的功能是运行一个外部程序(或者是打开一个已注册的文件、打开一个目录、打印一个文件等等
ShellExecuteA(NULL,
"open", //指定动作, 譬如: open、runas、print、edit、explore、find
"I:\\fudan\\C\\Library\\黑马\\CGI\\管道SERVER\\Debug\\客户端.exe", //指定要打开的文件或程序
NULL, //给要打开的程序指定参数; 如果打开的是文件这里应该是 nil
NULL,
1 //打开选项,即是隐藏、最小化、最大化还是用最近的大小和位置显示, 激活
);
}
Sleep(5000);
}
}