socket 客户端 服务端 简单的脏数据处理 protobuf作为通信协议

写了一个简单的网络通信程序。

客户端向服务端发送一串字符串,服务端计算出字符串长度返回。功能很简单

客户端和服务端通信采用protobuf

客户端每次发送的长度是一定的,包括发送的整个长度,字符串和crc校验码

服务端返回的长度也是一定的,包括客户端发送的字符串长度和crc校验码

服务端和客户端做了简单的超时控制和容错处理。

贴代码:

protobuf定义:

1 message CliPack {
  2   required int32 length = 1;
  3   required string data = 2;
  4   required int32 crc = 3;
  5 }
  6
  7 message SvrPack {
  8   required int32 client_data_length = 1;
  9   required int32 crc = 2;
 10 }

客户端代码:

client.h

#ifndef CLIENT_H_
#define CLIENT_H_

#include <string>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <error.h>
#include <stdio.h> 
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include "package.pb.h"
#include "common.h"
using namespace std;


const unsigned int kServerPort = 9876;
const string kIp = "10.11.18.144";
const unsigned int kTcpBufLen = 64*1024*1024;
const unsigned int kSendBufLen = 1152;
const unsigned int kRecvBufLen = 128;
const unsigned int kRetryCount = 5;
const unsigned int kTimeOut = 50;
const unsigned int kDataMaxLength = 1024;

class Client {
  public:
    //explicit Client():ip_(kIp), port_(kServerPort);
    explicit Client();
    explicit Client(const string& ip, unsigned int port, unsigned int timeout);
    virtual ~Client();
    const bool get_connected() {
      return connected_;
    }
    void set_connected(bool connected) {
      connected_ = connected;
    }
    const unsigned int get_port() {
      return port_;
    }
    void set_port(unsigned port) {
      port = port_;
    }
    const string get_ip() {
      return ip_;
    }
    void set_ip(const string& ip) {
      ip_ = ip;
    }

  public:
    int Request(CliPack& pack);
  private:
    explicit Client(const Client&) {}
    Client& operator = (const Client&) {}

  private:
    bool CheckConnect();
    bool Send(const char* buf, int len);
    bool Recv();
    void SetSocketOpt();
    int GetData(const char* buf);

  private:
    string ip_;
    unsigned int port_;
    int sockfd_;
    bool connected_;
    int time_out_;
    int length_;
};

#endif

 

client.cc

#include "client.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <error.h>

Client::Client() {
  ip_ = kIp;
  port_ = kServerPort;
  time_out_ = kTimeOut;
  connected_ = false;
  sockfd_ = -1;
  length_ = -1;
}

Client::Client(const string& ip, unsigned int port, unsigned int timeout) {
  ip_ = ip;
  port_ = port;
  time_out_ = timeout;
  connected_ = false;
  sockfd_ = -1;
  length_ = -1;
}

Client::~Client() {
  close(sockfd_);  
}

bool Client::CheckConnect() {
  if (connected_) {
    return true;
  }
  if (-1 != sockfd_) {
    close(sockfd_);
  }
  sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
  SetSocketOpt();
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr(ip_.c_str());
  addr.sin_port = htons(port_);
  int res = connect(sockfd_, (sockaddr*)&addr, sizeof(addr));
  if (0 == res) {
    connected_ = true;
    return true;
  } else if (res < 0 && errno != EINPROGRESS) {
    printf("Client::CheckConnect --> err:%s\n", strerror(errno));
    return false;
  }
  struct pollfd fds;
  fds.fd = sockfd_;
  fds.events = POLLOUT;
  res = poll(&fds, 1, time_out_);
  if (1 == res) {
    connected_ = true;
    //printf("haha3\n");
    return true;
  }
  return false;
}

void Client::SetSocketOpt() {
  int res = setsockopt(sockfd_, SOL_SOCKET, SO_RCVBUF, &kTcpBufLen, sizeof(kTcpBufLen));        
  if (res != 0) {
    printf("Client::SetSocketOpt --> set recvbuf err, %s\n", strerror(errno));
    return;
  }
  res = setsockopt(sockfd_, SOL_SOCKET, SO_SNDBUF, &kTcpBufLen, sizeof(kTcpBufLen));        
  if (res != 0) {
    printf("Client::SetSocketOpt --> set sendbuf err, %s\n", strerror(errno));
    return;
  }
  int flags = fcntl(sockfd_, F_GETFL, 0);
  res = fcntl(sockfd_, F_SETFL, flags | O_NONBLOCK);
  if (res < 0) {
    printf("Client::SetSocketOpt --> set fcntl err, %s\n", strerror(errno));
    return;
  }

  struct linger l;
  l.l_onoff = 1;
  l.l_linger = 0; 
  res = setsockopt(sockfd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
  if (res != 0) {
    printf("Client::SetSocketOpt --> set linger err, %s\n", strerror(errno));
    return;
  }
  int nodelay = 1;
  res = setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
  if (res != 0) {
    printf("Client::SetSocketOpt --> set nodelay err, %s\n", strerror(errno));
    return;
  }
}

bool Client::Send(const char* buf, int len) {
  if (buf == NULL || len <= 0)
    return false;

  int tmp = 0;
  int count = 0;
  while (count < kRetryCount) {
    int res = send(sockfd_, buf+tmp, len-tmp, 0);
    if (res < 0) {
      printf("Client::Send --> err, %d,%s,%d\n", errno, ip_.c_str(), port_);
      return false;
    }
    tmp += res;
    if (tmp == len) {
      return true;
    }
    struct pollfd fds;
    fds.fd = sockfd_;
    fds.events = POLLOUT;
    res = poll(&fds, 1, time_out_);
    if (res != 1) {
      printf("Client::Send --> %d, %s\n", res, strerror(errno));
      return false;
    }
    count++;
  }
  return false;
}

bool Client::Recv() {
  int pos = 0;
  char recv_buf[kRecvBufLen];
  memset(recv_buf, 0, kRecvBufLen);
  while (1) {
    struct pollfd fds;
    fds.fd = sockfd_;
    fds.events = POLLIN;
    int res = poll(&fds, 1, time_out_);
    if (res <= 0) {
      if (res < 0) {
        printf("Recv poll-->error res:%d\n", res); 
      } else {
        printf("Recv poll-->time out res:%d\n", res); 
      }
      return false;
    } else if (1 == res) {
      int tmp = recv(sockfd_, recv_buf + pos, kRecvBufLen - pos, 0);
      if (-1 == tmp) {
        if (errno != EAGAIN) {
          printf("Recv recv error tmp = -1\n"); 
          return false;
        } else {
          continue;
        }
      } else if (0 == tmp) {
        printf("Recv recv error tmp = 0\n"); 
        return false;
      } else {
        if (kRecvBufLen - pos == tmp) {
          int data = GetData(recv_buf);
          if (data < 0) {
            printf("Recv data error data:%d\n", data);
          } else {
            printf("Recv data length data length:%d\n", data);
          }
          return true;    
        } else {
          pos = pos + tmp; 
          continue;
        }
      }
    } else {
      printf("Recv poll error res > 1\n"); 
      return false;
    }
  }
  return false;
}

int Client::GetData(const char* buf) {
  if (buf == NULL)
    return -1;
  SvrPack pack;
  pack.ParseFromString(buf);
  int length = pack.client_data_length();
  int crc = pack.crc();
  stringstream ss;
  ss << length;
  int compute_crc = Common::GetCrc32(ss.str().c_str(), strlen(ss.str().c_str()));
  if (compute_crc == crc) {
    if (length > 0)
      return length;
    else
      return -2;
  } 
  return -3;
}

int Client::Request(CliPack& pack) {
  if (!CheckConnect()) {
    close(sockfd_);
    connected_ = false;
    printf("Request CheckConnect error \n");
    return -1;
  }
  char send_buf[kSendBufLen]; 
  memset(send_buf, 0, kSendBufLen);
  string str; 
  pack.SerializeToString(&str);
  const char* tmp = str.c_str();
  memcpy(send_buf, tmp, kSendBufLen);
  if (!Send(send_buf, kSendBufLen)) {
    close(sockfd_);
    connected_ = false;
    printf("Request Send error \n");
    return -2;
  }
  
  if (!Recv()) {
    close(sockfd_);
    connected_ = false;
    printf("Request Recv error \n");
    return -3;
  }
  return 0;
}

 

main.cc:

#include "client.h"
#include "common.h"

using namespace std;

int main(int argc, char* argv[]) {
  CliPack pack;
  char* data = new char[kSendBufLen];
  memset(data, 0, kSendBufLen);
  memcpy(data, "123456", kSendBufLen);
  pack.set_length(kSendBufLen);
  pack.set_data(data);
  int crc = Common::GetCrc32(data, strlen(data));
  //printf("crc->%d\n", crc);
  pack.set_crc(crc);
  Client* c = new Client();
  c->Request(pack);
  delete [] data;
  return 0; 
}

 

服务端代码:

server.h

#ifndef _SERVER_H_
#define _SERVER_H_

#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <string>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include "common.h"
#include "package.pb.h"


const std::string kServerIp = "10.11.18.144";
const unsigned int kServerPort = 9876;
const unsigned int kTcpBufLen = 64*1024*1024;
const unsigned int kListenQueueSize = 1024;
const unsigned int kRecvBufferLen = 1152;
const unsigned int kSendBufferLen = 128;
const unsigned int kTimeOut = 50;
const unsigned int kRetryCount = 5;


class Server {
  public:
    explicit Server() {
      server_ip_ = kServerIp;
      server_port_ = kServerPort;
      time_out_ = kTimeOut;
      server_sockfd_ = -1;
    }
    virtual ~Server() {}
  private:
    explicit Server(const Server&);
    Server& operator = (const Server&);
  public:
    std::string const get_server_ip() const;
    void set_server_ip(std::string server_ip);
    unsigned int const get_server_port() const;
    void set_server_port(unsigned int server_port);
    void set_time_out(int timeout);
    int const get_time_out() const;
  public:
    virtual void Run() = 0;        
  protected:
    virtual int Init();
    virtual void SetSocketOpt();
    virtual int Send(const char* buf, int len, int fd);
    virtual int GetData(const char* buf); 
    
  protected:
    std::string server_ip_;
    unsigned int server_port_;
    int server_sockfd_;
    int time_out_;
};

class SimpleServer : public Server {
  public:
    explicit SimpleServer() {}
    ~SimpleServer() {}
  private:
    explicit SimpleServer(const SimpleServer&);
    SimpleServer& operator = (const SimpleServer&);
  private:
    int Process();
  public:
    void Run();
};

class ForkServer: public Server {
  public:
    explicit ForkServer() {}
    ~ForkServer() {}
  private:
    explicit ForkServer(const ForkServer&);
    ForkServer& operator = (const ForkServer&);
  private:
    int Process();
  public:
    void Run();
};

#endif

 

server.cc

 

#include "server.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>


unsigned int const Server::get_server_port() const {
  return server_port_;
}

void Server::set_server_port(unsigned int server_port) {
  server_port_ = server_port;
}

std::string const Server::get_server_ip() const {
  return server_ip_;
}

void Server::set_server_ip(std::string server_ip) {
  server_ip_ = server_ip;
}

void Server::set_time_out(int timeout) {
  time_out_ = timeout;
}

int const Server::get_time_out() const {
  return time_out_;
}

void Server::SetSocketOpt() {
  int res = setsockopt(server_sockfd_, SOL_SOCKET, SO_RCVBUF, &kTcpBufLen, sizeof(kTcpBufLen));        
  if (res != 0) {
    printf("Server::SetSocketOpt --> set recvbuf err, %s\n", strerror(errno));
    return;
  }
  res = setsockopt(server_sockfd_, SOL_SOCKET, SO_SNDBUF, &kTcpBufLen, sizeof(kTcpBufLen));        
  if (res != 0) {
    printf("Server::SetSocketOpt --> set sendbuf err, %s\n", strerror(errno));
    return;
  }
  
  int option = 1;
  setsockopt (server_sockfd_, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)) ;

  int flags = fcntl(server_sockfd_, F_GETFL, 0);
  res = fcntl(server_sockfd_, F_SETFL, flags | O_NONBLOCK);
  if (res < 0) {
    printf("Server::SetSocketOpt --> set fcntl err, %s\n", strerror(errno));
    return;
  }
  struct linger l;
  l.l_onoff = 1;
  l.l_linger = 0; 
  res = setsockopt(server_sockfd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
  if (res != 0) {
    printf("Server::SetSocketOpt --> set linger err, %s\n", strerror(errno));
    return;
  }
  int nodelay = 1;
  res = setsockopt(server_sockfd_, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
  if (res != 0) {
    printf("Server::SetSocketOpt --> set nodelay err, %s\n", strerror(errno));
    return;
  }
}

int Server::Send(const char* buf, int len, int fd) {
  if (buf == NULL || len <= 0)
    return -1;

  if (-1 == fd) {
    return -1;
  }

  int tmp = 0;
  int count = 0;
  while (count < kRetryCount) {
    int res = send(fd, buf+tmp, len-tmp, 0);
    if (res < 0) {
      printf("Server::Send --> err, %d,%s,%d\n", errno, server_ip_.c_str(), server_port_);
      return -1;
    }
    tmp += res;
    if (tmp == len) {
      return 0;
    }
    struct pollfd fds;
    fds.fd = fd;
    fds.events = POLLOUT;
    res = poll(&fds, 1, time_out_);
    if (res != 1) {
      printf("Server::Send --> %d, %s\n", res, strerror(errno));
      return -1;
    }
    count++;
  }
  return -1;
}

int Server::Init() {
  server_sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
  if (-1 == server_sockfd_) {
    printf("create socket error: %s(errno, %d)\n", strerror(errno), errno);
    return -1;
  }
  SetSocketOpt();
  struct sockaddr_in server_address;
  memset(&server_address, 0, sizeof(server_address));
  server_address.sin_family = AF_INET;
  server_address.sin_addr.s_addr = inet_addr(kServerIp.c_str());
  server_address.sin_port = htons(kServerPort);
  if (-1 == bind(server_sockfd_, (struct sockaddr*)&server_address, sizeof(struct sockaddr))) {
    printf("bind socket error: %s(errno, %d)\n", strerror(errno), errno);
    close(server_sockfd_);
    return -1;
  }

  if (-1 == listen(server_sockfd_, kListenQueueSize)) {
    printf("listen socket error: %s(errno, %d)\n", strerror(errno), errno);
    close(server_sockfd_);
    return -1; 
  }
  return 0;
}

int Server::GetData(const char* buf) {
  if (NULL == buf) 
    return -1;
  CliPack pack;
  pack.ParseFromString(buf);
  int length = pack.length();
  int crc = pack.crc();
  char* data = new char[kRecvBufferLen];
  memset(data, 0, kRecvBufferLen);
  memcpy(data, pack.data().c_str(), kRecvBufferLen);
  int compute_crc = Common::GetCrc32(data, strlen(data));
  delete [] data;
  if (compute_crc != crc)
    return -1;
  if (pack.data().c_str() == NULL)
    return -2;
  return strlen(pack.data().c_str());
}

int SimpleServer::Process() {
  while (1) {
    int conn_fd = -1;
    conn_fd = accept(server_sockfd_, (struct sockaddr*)NULL, NULL);
    if (-1 == conn_fd) {
      if (errno == EINTR || errno == EAGAIN) {
        continue;
      } else {
        printf("Recv accept socket error: %s(errno, %d)\n", strerror(errno), errno);
        continue;
      }
    }
    char buf[kRecvBufferLen];
    memset(buf, 0, kRecvBufferLen);
    int pos = 0;
    while (1) {
      struct pollfd fds;
      fds.fd = conn_fd;
      fds.events = POLLIN;
      int res = poll(&fds, 1, time_out_);
      if (res <= 0) {
        if (res < 0) {
          printf("Process poll-->error res:%d\n", res); 
        } else {
          printf("Process poll-->time out res:%d\n", res); 
        }
        close(conn_fd);
        break;
      } else if (1 == res) {
        int tmp = recv(conn_fd, buf + pos, kRecvBufferLen - pos, 0);
        if (-1 == tmp) {
          if (errno != EAGAIN) {
            printf("Process recv error tmp = -1\n"); 
            close(conn_fd);
            break;
          } else {
            continue;
          }
        } else if (0 == tmp) {
          close(conn_fd);
          printf("Process recv error tmp = 0\n"); 
          break;
        } else {
          if (kRecvBufferLen - pos == tmp) {
            int data = GetData(buf);
            if (data < 0) {
              printf("Process GetData error data = %d\n", data); 
              close(conn_fd);
            } else {
              char send_buf[kSendBufferLen];
              SvrPack pack;
              memset(send_buf, 0, kSendBufferLen);
              pack.set_client_data_length(data);
              stringstream ss;
              ss << data;
              int crc = Common::GetCrc32(ss.str().c_str(), strlen(ss.str().c_str()));
              pack.set_crc(crc);
              string str;
              pack.SerializeToString(&str);
              memcpy(send_buf, str.c_str(), kSendBufferLen);
              if (-1 == Send(send_buf, kSendBufferLen, conn_fd)) {
                printf("Process Send error\n"); 
                close(conn_fd);
              }
            }
            break;
          } else {
            pos = pos + tmp; 
            continue;
          }
        }
      } else {
        close(conn_fd);
        printf("Process poll error res > 1\n"); 
        break;
      }
    }
  }
  close(server_sockfd_);
  return 0;
}

void SimpleServer::Run() {
  if (-1 == Init()) {
    printf("SimpleServer::Run Init error\n");
    return;
  }  
  
  if (-1 == Process()) {
    printf("SimpleServer::Run Process error\n");
    return;
  }
}



int ForkServer::Process() {
  while (1) {
    int conn_fd = -1;
    conn_fd = accept(server_sockfd_, (struct sockaddr*)NULL, NULL);
    if (-1 == conn_fd) {
      if (errno == EINTR || errno == EAGAIN) {
        continue;
      } else {
        printf("Recv accept socket error: %s(errno, %d)\n", strerror(errno), errno);
        continue;
      }
    }
    pid_t pid; 
    pid = fork();
    if (0 == pid) {
      char buf[kRecvBufferLen];
      memset(buf, 0, kRecvBufferLen);
      int pos = 0;
      while (1) {
        struct pollfd fds;
        fds.fd = conn_fd;
        fds.events = POLLIN;
        int res = poll(&fds, 1, time_out_);
        if (res <= 0) {
          if (res < 0) {
            printf("Process poll-->error res:%d\n", res); 
          } else {
            printf("Process poll-->time out res:%d\n", res); 
          }
          close(conn_fd);
          exit(0);
        } else if (1 == res) {
          int tmp = recv(conn_fd, buf + pos, kRecvBufferLen - pos, 0);
          if (-1 == tmp) {
            if (errno != EAGAIN) {
              printf("Process recv error tmp = -1\n"); 
              close(conn_fd);
              exit(0);
            } else {
              continue;
            }
          } else if (0 == tmp) {
            close(conn_fd);
            printf("Process recv error tmp = 0\n"); 
            exit(0);
          } else {
            if (kRecvBufferLen - pos == tmp) {
              int data = GetData(buf);
              if (data < 0) {
                printf("Process GetData error data = %d\n", data); 
                close(conn_fd);
              } else {
                char send_buf[kSendBufferLen];
                SvrPack pack;
                memset(send_buf, 0, kSendBufferLen);
                pack.set_client_data_length(data);
                stringstream ss;
                ss << data;
                int crc = Common::GetCrc32(ss.str().c_str(), strlen(ss.str().c_str()));
                pack.set_crc(crc);
                string str;
                pack.SerializeToString(&str);
                memcpy(send_buf, str.c_str(), kSendBufferLen);
                if (-1 == Send(send_buf, kSendBufferLen, conn_fd)) {
                  printf("Process Send error\n"); 
                  close(conn_fd);
                }
              }
              exit(0);
            } else {
              pos = pos + tmp; 
              continue;
            }
          }
        } else {
          close(conn_fd);
          printf("Process poll error res > 1\n"); 
          exit(0);
        }
      }
    } else {
      close(conn_fd);
    }
  }
  close(server_sockfd_);
  return 0;
}


void ForkServer::Run() {
  if (-1 == Init()) {
    printf("SimpleServer::Run Init error\n");
    return;
  }  
  
  if (-1 == Process()) {
    printf("SimpleServer::Run Process error\n");
    return;
  }
}

 

main.cc

#include "server.h"

int main(int argc, char*argv[]) {
  //Server* server = new SimpleServer();
  //server->Run();
  Server* server = new ForkServer();
  server->Run();
}

 

整个代码没有进行过充分测试,超时时间设置也有些问题,有些重复代码封装的也不太好,不过就先这样吧

 

 

 

有趣有爱有价值:http://www.qihu100.com

 


                   

 

转载于:https://my.oschina.net/hejiula/blog/149209

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值