简单高并发C/S模型

使用了自己做的通用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操作修改为加解锁即可!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值