使用了自己做的通用API函数库,参考之前的文章:
网络编程中的select实现超时检测和通用API
第一篇文章能找到客户端和服务器的头文件和套接字编程相关的API,第二篇文章能找到信号量和共享内存相关的API。
基于select+多进程+信号量+共享内存
客户端
#include "comsocket.h"
#include <sys/wait.h>
void handle(int signum)
{
int pid = 0;
printf("recv signum:%d \n", signum);
//避免僵尸进程
while ((pid = waitpid(-1, NULL, WNOHANG) ) > 0)
{
printf("退出子进程pid%d \n", pid);
fflush(stdout);
}
}
int main()
{
void * handle = NULL;
int ret = 0;
unsigned char *data = (unsigned char *)"asdfghjklaedas";
unsigned char out[1024] = {0};
int datalen = 10;
int outlen = 1024;
int con_fd;
int i,j;
int proc_num,loop_num;
pid_t pid;
printf("Input the procnum:\n");
scanf("%d",&proc_num);
printf("Input the loop_num:\n");
scanf("%d",&loop_num);
signal(SIGPIPE,SIG_IGN);
signal(SIGCHLD, handle);
//客户端环境初始化
ret = sckClient_init(&handle,5,5,5,1);
for(i = 0;i < proc_num;i++)
{
pid = fork();
if(0 == pid)
{
ret = sckClient_getConn(handle,"192.168.1.110",8001,&con_fd);
for(j=0;j<loop_num;j++)
{
//客户端发送报文
ret = sckClient_send(handle,con_fd, data,datalen);
if (ret != 0)
{
if (ret == Sck_ErrTimeOut)
{
continue;
}
break;
}
//客户端端接受报文
ret = sckClient_rcv(handle,con_fd,out, &outlen);
if (ret != 0)
{
if (ret == Sck_ErrTimeOut)//认为已经断开了
{
break;
}
break;
}
out[outlen] = '\0';
printf("data: %s \nlen: %d\n", out,outlen);
}
close(con_fd);
exit(0);//避免产生孙子进程
}
}
// 客户端环境释放
sckClient_destroy(&handle);
while ((pid = waitpid(-1, NULL, WNOHANG) ) >= 0)//返回0的话,表示子进程并没有真结束完毕,知道返回-1才退出收尸过程,结束父进程。
{
}
printf("child over pid %d \n", pid);
fflush(stdout);
return ret;
}
服务器
#include "comsocket.h"
#include <sys/wait.h>
#define PORT 8001
#include "myipc_sem.h"
#include "myipc_shm.h"
void chld_handle(int sig)
{
pid_t my_pid;
//wait(NULL);//只能处理单进程的情况
while((my_pid = waitpid(-1, NULL, WNOHANG)) > 0)
printf("child is died,parent take care of it:%d\n", my_pid);
}
int srv_init()
{
int ret = 0;
int shmdl = 0;
key_t key = 0;
int semid = 0;
signal(SIGCHLD,SIG_IGN);
//创建共享内存--父进程创建的时候使用的key和子进程一样,
//所以即使在子进程再次创建,实则是获取已存在的共享内存--所以是同一块共享内存
//当然可以在子进程里面不再重新调用创建共享内存的函数(实际上也没有真的创建)
//可以通过向子进程传递已经创建好的共享内存ID--让他映射到自己的内存空间
//就算父进程没有创建,众多子进程创建了,只要key一样--就是同一块共享内存
ret = IPC_CreatShm("./", sizeof(int), &shmdl);
if(ret != 0)
{
printf("IPC_CreatShm err\n");
return -1;
}
//利用获取的key打开/创建信号量集合
key = ftok("./",'a');
//创建信号量集合--默认创建1个信号量
//尽管子进程再次调用该部分代码--表面上重新创建信号量
//但由于key相同,系统里面已经有父进程创建的信号量,所以他们是同一个信号量集合
//原理和父子进程创建共享内存一样
ret = sem_open(key, &semid);
if(ret == SEMERR_ENOXIST)
{
ret = sem_create(key, &semid);
if(ret != 0)
{
printf("sem_create err\n");
return -1;
}
}
int val= 1;
ret = sem_set(semid, val);//设置信号量计数值--默认设置第1个,下标位0
if(ret == -1)
{
printf("sem_set err\n");
return -1;
}
ret = sem_get(semid, &val);//获取信号量计数值
if(ret != 0)
{
printf("sem_get err\n");
return -1;
}
printf("val:%d\n",val);
return 0;
}
void Test()
{
int cnt = 0;
int ret = 0;
int shmdl = 0;
int *addr = NULL;
key_t key ;
int semid = 0;
//利用获取的key打开/创建信号量集合
key = ftok("./",'a');
//创建信号量集合--默认创建1个信号量
ret = sem_open(key, &semid);
if(ret == SEMERR_ENOXIST)
{
ret = sem_create(key, &semid);
if(ret != 0)
{
printf("sem_create err\n");
return ;
}
}
sem_p(semid);//p操作
ret = IPC_CreatShm("./", sizeof(int), &shmdl);
if(ret != 0)
{
printf("IPC_CreatShm err\n");
return ;
}
ret = IPC_MapShm(shmdl,(void**)&addr);
cnt = ++(*addr);
printf("cnt:%d\n",cnt);
ret = IPC_UnMapShm(addr);//显示解除映射关系
sem_v(semid);//v操作
}
int main()
{
int ret = 0;
int listenfd;
int con_fd;
int datalen = 1024;
int timeout = 5;
//unsigned char *data = (unsigned char *)"lzjyrm";
unsigned char recv[1024] = {0};
pid_t pid;
signal(SIGCHLD,chld_handle);
signal(SIGPIPE,SIG_IGN);
srv_init();
ret = sckServer_init(PORT, &listenfd);
if(ret != 0)
{
printf("sckServer_init err:%d\n ",ret);
return ret;
}
printf("Waiting for connect.....\n");
while(1)
{
ret = sckServer_accept(listenfd, &con_fd, timeout);
if(ret == Sck_ErrTimeOut)
{
printf("sckServer_accept Sck_ErrTimeOut");
continue;
}
pid = fork();
if(pid == -1)
{
perror("fork");
return errno;
}
else if(pid == 0)
{
close(listenfd);
while(1)
{
memset(recv,0,sizeof(recv));
ret = sckServer_rcv(con_fd, recv, &datalen, timeout); //服务器端端接受报文
if(ret != 0)
{
printf("sckServer_rcv: %d\n",ret);
break;
}
recv[datalen] = '\0';
printf("rcv:%s\n==================\n",recv);
Test();
//服务器端发送报文
ret = sckServer_send(con_fd,recv, datalen, timeout);
if(ret != 0)
{
printf("sckServer_send: %d\n",ret);
break;
}
}
close(con_fd);
exit(ret);
}
else if(pid > 0)
{
close(con_fd);
}
}
//服务器端环境释放
//ret = sckServer_destroy(void **handle);
return 0;
}
基于select+多线程+信号量+共享内存
客户端
和多进程版本的一样,也可以写成多线程版本的!
服务器
#include "comsocket.h"
#include <sys/wait.h>
#define PORT 8001
#include "myipc_sem.h"
#include "myipc_shm.h"
#include <pthread.h>
void chld_handle(int sig)
{
pid_t my_pid;
//wait(NULL);//只能处理单进程的情况
while((my_pid = waitpid(-1, NULL, WNOHANG)) > 0)
printf("child is died,parent take care of it:%d\n", my_pid);
}
int srv_init()
{
int ret = 0;
int shmdl = 0;
key_t key = 0;
int semid = 0;
signal(SIGCHLD,SIG_IGN);
//创建共享内存--父进程创建的时候使用的key和子进程一样,
//所以即使在子进程再次创建,实则是获取已存在的共享内存--所以是同一块共享内存
//当然可以在子进程里面不再重新调用创建共享内存的函数(实际上也没有真的创建)
//可以通过向子进程传递已经创建好的共享内存ID--让他映射到自己的内存空间
//就算父进程没有创建,众多子进程创建了,只要key一样--就是同一块共享内存
ret = IPC_CreatShm("./", sizeof(int), &shmdl);
if(ret != 0)
{
printf("IPC_CreatShm err\n");
return -1;
}
//利用获取的key打开/创建信号量集合
key = ftok("./",'a');
//创建信号量集合--默认创建1个信号量
//尽管子进程再次调用该部分代码--表面上重新创建信号量
//但由于key相同,系统里面已经有父进程创建的信号量,所以他们是同一个信号量集合
//原理和父子进程创建共享内存一样
ret = sem_open(key, &semid);
if(ret == SEMERR_ENOXIST)
{
ret = sem_create(key, &semid);
if(ret != 0)
{
printf("sem_create err\n");
return -1;
}
}
int val= 1;
ret = sem_set(semid, val);//设置信号量计数值--默认设置第1个,下标位0
if(ret == -1)
{
printf("sem_set err\n");
return -1;
}
ret = sem_get(semid, &val);//获取信号量计数值
if(ret != 0)
{
printf("sem_get err\n");
return -1;
}
printf("val:%d\n",val);
return 0;
}
void Test()
{
int cnt = 0;
int ret = 0;
int shmdl = 0;
int *addr = NULL;
key_t key ;
int semid = 0;
//利用获取的key打开/创建信号量集合
key = ftok("./",'a');
//创建信号量集合--默认创建1个信号量
ret = sem_open(key, &semid);
if(ret == SEMERR_ENOXIST)
{
ret = sem_create(key, &semid);
if(ret != 0)
{
printf("sem_create err\n");
return ;
}
}
sem_p(semid);//p操作
ret = IPC_CreatShm("./", sizeof(int), &shmdl);
if(ret != 0)
{
printf("IPC_CreatShm err\n");
return ;
}
ret = IPC_MapShm(shmdl,(void**)&addr);
cnt = ++(*addr);
printf("cnt:%d\n",cnt);
ret = IPC_UnMapShm(addr);//显示解除映射关系
sem_v(semid);//v操作
}
#define TIME_OUT 5
void* threadRoute(void* var)
{
int con_fd = 0;
unsigned char recv[1024] = {0};
int datalen = 1024;
int timeout = TIME_OUT;
int ret = 0;
pthread_detach(pthread_self());
if(var == NULL)
{
printf("param err\n");
return NULL;
}
con_fd = *((int*)var);
free(var);
while(1)
{
memset(recv,0,sizeof(recv));
ret = sckServer_rcv(con_fd, recv, &datalen, timeout); //服务器端端接受报文
if(ret != 0)
{
if(ret == Sck_ErrTimeOut)
{
continue;
}
printf("sckServer_rcv err: %d\n",ret);
break;
}
recv[datalen] = '\0';
printf("rcv:%s\n==================\n",recv);
Test();
//服务器端发送报文
ret = sckServer_send(con_fd,recv, datalen, timeout);
if(ret != 0)
{
printf("sckServer_send: %d\n",ret);
break;
}
}
close(con_fd);
return NULL;
}
int main()
{
int ret = 0;
int listenfd;
int con_fd;
pthread_t tid;
int timeout = TIME_OUT;
signal(SIGCHLD,chld_handle);
signal(SIGPIPE,SIG_IGN);
srv_init();
ret = sckServer_init(PORT, &listenfd);
if(ret != 0)
{
printf("sckServer_init err:%d\n ",ret);
return ret;
}
printf("Waiting for connect.....\n");
while(1)
{
timeout = TIME_OUT;
ret = sckServer_accept(listenfd, &con_fd, timeout);
if(ret == Sck_ErrTimeOut)
{
printf("sckServer_accept Sck_ErrTimeOut");
continue;
}
int *pCon = (int*)malloc(sizeof(int));
*pCon = con_fd;
//pthread_t *ptr_tid = (pthread_t*)malloc(sizeof(pthread_t));
pthread_create(&tid, NULL, threadRoute, (void*)pCon);
//close(con_fd);
}
return 0;
}
当然还可以使用”基于select+多线程+互斥锁+共享内存”的模式,将PV操作修改为加解锁即可!