Linux下网络编程之自定义协议进行并发多客户端与服务器的通信(多进程处理并发)不足占用资源太多

自定义协议消息体

 
  
*********msg.h*************
#ifndef _MSG_H_
#define _MSG_H_
struct msg
{
  char head[10]; //头部
  char msg_chck; //效验码
  char buff[512];//体部
}Msg;
extern int write_msg(int socktf, char* buff, size_t len);

extern int read_msg(int socktf, char* buff, size_t len);


 
  
*****msg.c***************
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "msg.h"

/*计算校验码
*/
static unsigned char msg_check(Msg* message)
{
  //将头部和体部进行累加
  unsigned char s = 0;
  int i;
  for(i = 0; i < sizeof(message->head); ++i)
  {
    s += message->head[i];
  }
  for(i = 0; i < sizeof(message->buff); ++i)
  {
    s += message->buff[i];
  }
  return s;
}

/*
 * 发送一个基于自定义协议的message
 * 发送的数据存放在buff中*/
 
int write_msg(int socktf, char* buff, size_t len)
{
  Msg message;
  memset(&message, 0, sizeof(message));
  strcpy(message.head, "wc2017000");
  memcpy(message.buff, buff, len);
  message.checknum = msg_check(&message);
  //发送一个message消息
  if(write(socktf, &message, sizeof(message)) != sizeof(message))
  {
    return -1;
  }
}


/*
 * 读取一个基于兹定于协议的message
 * 读取的数据存在在buff中
 */
int read_msg(int socktf, char* buff, size_t len)
{
  Msg message;
  memset(&message, 0, sizeof(message));
  size_t size;
  if(( size = read(socktf,
          &message, sizeof(message))) < 0 )
  {
    return -1;
  }
  else if(size == 0) //数据读光了
  {
    return 0;
  }
  //进行效验码验证,判断接受到message数据是否完整
  unsigned char s = msg_check(&message);
  if(s == (unsigned char)message.checknum
      && (!strcmp("wc2017000", message.head)))
  {
    memcpy(buff, message.buff, len);
    return sizeof(message);
  }
  return -1;
}
客户端:
/*
 *  文 件 名:time_tcp_client.cpp
 *  作    者:wc
 *  邮    件:[email protected]
 *  版    权:Copyright (c) 完全自由
 *  意    图:
 */


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


int main(int argc, char* argv[])
{
  if(argc < 3)
   printf("usage: %s ip port\n", argv[0]);
   exit(1);
  
   /*步骤1:创建socket套接字
    *
    * */
  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if(sockfd < 0)
  {
    perror("socket error");
    exit(1);
  }

  /*往serveraddr中填入ip,port和地址族类型 ipv4
   *
   */
  struct sockaddr_in  serveraddr;
  memset(&serveraddr, 0, sizeof(serveraddr));
  serveraddr.sin_family = AF_INET;
  serveraddr.sin_port = htons(atoi(argv[2]));
  /*p地址转换为网络字节序后填入tuserveraddr中*/
  inet_pton(AF_INET, argv[1],  &serveraddr.sin_addr.s_addr);
  
  /*步骤2:客户端调用connect函数连接到服务器端
   * */
  if(connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0)
  {
   // perror("connect error);
    exit(1);
  }

  /*步骤3:调用IO函数(read/write)和服务器端进行双向通信
   * */
  char buff[512];
  size_t size;
  char* prompt = ">";
  while(1)
  {
    memset(buff, 0, sizeof(buff));
    write(STDOUT_FILENO, prompt, 1); //连接提示服
    size = read(STDIN_FILENO, buff, sizeof(buff));
    if(size < 0) continue;
    buff[size-1] = '\0';
    if(write_msg(sockfd, buff, sizeof(buff)) < 0 )
    {
      perror("write msg error");
      continue;
    }
    else if(read_msg(sockfd, buff, sizeof(buff)) < 0)
      {
       perror("read msg error");
      }
    else
    {
      printf("buff:%s", buff);
    }
    }
  }

  /*步骤4:
   *关闭套接字socket
   * */
   close(sockfd);



  return 0;
}
服务器:
/*
 *  文 件 名:time_tcp_server.cpp
 *  作    者:wc
  *  版    权:Copyright (c) 完全自由
 *  意    图:study
 */
#include 
#include  
#include
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "msg.h"
#include 
#include 
int sockfd; 

void sig_handler(int signo)//信号捕捉函数
{
  if(signo == SIGINT)
  {
   printf("server close\n");
   //关闭socket
   close(sockfd);
  // exit(1);
  }
  if(signo == SIGCHLD)
  {
      perror("sigchld deaed");
      wait(0);
  }

}

/*输出连接上来的客户端相关信息
 *
 */
void out_addr(struct sockaddr_in* clientaddr)
{
  /*将端口从网络字节序转换成主机字节序
   *
   */
  int port = ntohs(clientaddr->sin_port);
  char ip[16];
  memset(ip, 0, sizeof(ip));
  /*将ip地址从网络字节序转换成点分十进制*/
  inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, ip, sizeof(ip));
  printf("client: %s(%d) connectd\n", ip, port);
}

void do_service(int fd)
{
  /*和客户端进行读写操作(双向通信)
   */
  char buff[512];
  while(1)
  {
    memset(buff, 0, sizeof(buff));
    printf("start read and wirte...\n");
    size_t size;
    if((size = read_msg(fd, buff, sizeof(buff))) < 0)
    {
      perror("protocal erorr");
      break;
    }
    else if(size == 0)
    {
      break;
    }
    else
    {
      printf("%s\n", buff);
      if(write_msg(fd, buff, sizeof(buff)) < 0)
      {
            //if(error == EPIPE)
           // break;
      }
      perror("protacal error");
    }
  }
}

int main(int argc, char* argv[])
{
  if(argc < 2) //外部输入参数监听 端口
  {
  printf("usage  %s #port\n", argv[0]);
 // exit(1);
  }

  if(signal(SIGINT, sig_handler) == SIG_ERR) //信号处理函数
  {
    perror("signal sigint error");
   // exit(1);
  }
   if(signal(SIGCHLD, sig_handler) == SIG_ERR) //信号处理函数
  {
    perror("signal sigchld  error");
   // exit(1);
  }

  /*步骤1:创建socket(套接字)
  *socket创建在内核中,是一个结构体
  *AF_INET:  IPV4
  *SOCK_STREAM:  tcp协议
   */

  sockfd = socket(AF_INET, SOCK_STREAM, 0);


  /*步骤2:调用bind函数将socket和地址
   * (包括ip、port)进行绑定
   */

  struct sockaddr_in serveraddr;    //网络基本地址族
  memset(&serveraddr, 0, sizeof(serveraddr));
  serveraddr.sin_family = AF_INET; //ipv4
  serveraddr.sin_port = htons(atoi(argv[1]));//整数转换为网络字节序 prot
  serveraddr.sin_addr.s_addr = INADDR_ANY;  //获取所有网卡客户端对应连接的请求
  if(bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0)
  {
    perror("bind error");
    exit(1);
  }


  /*步骤3:调用listen函数启动监听(指定port监听)
   * 通知系统去接受来自客户端的连接请求
   * 将接收到的客户端连接请求放置到对应的队列中
   * 第二个参数 客户端队列的长度
   */
  if(listen(sockfd, 10) < 0)
  {
    perror("lisen error");
    exit(1);
  }

  /*步骤4:调用accept函数从队列(先进先出)中获
   * 取一个客户端的请求连接,并返回新的socket描述符
   * 注意:若没有客户端连接,调用此函数后阻塞,直到获得一个客户端的连接
   */
  struct sockaddr_in clientaddr;
  socklen_t clientlen = sizeof(clientaddr);
  while(1)
  {
    int fd = accept(sockfd, (struct sockaddr*)&clientaddr, &clientlen);//sockfd此时是新的客户但套接字的描述符
    if(fd < 0)
    {
      perror("acdept error");
      continue;
    }

    /*步骤5:调用IO函数(read/write)和连接额度客户端进行双向的通信
     *启动子进程去调用IO函数(read/write)和连接的客户端进行双向的通信
     (并发处理来自客户端的请求启动多进程  当一个新的客户端父进程new出一个子进程,父进程继续等待下个客户端的连接  )
     */
    pid_t pid = fork(); //创建一个进程
    if(pid < 0)
      continue;
    else if(pid == 0)//子进程
    {
      out_addr(&clientaddr);
      do_service(fd);
      close(fd);//内存在有计数器的 根据close的数量达到2的时候内存会释放的
      break;
    }
    else//父进程
      close(fd);
    }

  return 0;
}


 
  

你可能感兴趣的:(linux,网络)