网络编程复习3tcp通信代码实现(多进程版本+多线程版本)

文章展示了如何使用C++实现一个TCP套接字类,包括创建、绑定、监听、连接、接受、发送和接收数据。接着,通过tcp_srv.cpp展示了一个简单的服务器处理客户端连接的过程,然后逐步演进到多进程(process_tcp_srv.cpp)和多线程(pthread_tcp_srv.cpp)版本,以处理并发连接。
摘要由CSDN通过智能技术生成

tcpsocket.hpp

#include<cstdio>
#include<iostream>
#include<string>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<cstring>

#define MAX_LISTEN 5
#define CHECK_RET(q) if((q)==false){return false;}

typedef struct
{
 int num1;
 int num2;
 char op;
}tmp_t;

class TcpSocket
{
  public:
    TcpSocket():_sockfd(-1){}

    bool Socket()
    {
      this->_sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
      if(this->_sockfd <0)
      {
        perror("socket perror");
        return false;
      }

      return true;
    }

    int GetFd()
    {
      return _sockfd;
    }

    void Addr(struct sockaddr_in* addr, const std::string& ip, uint16_t port)
    {
      addr->sin_family = AF_INET;
      addr->sin_port = htons(port);
      addr->sin_addr.s_addr = inet_addr(ip.c_str());
      return;
    }

    bool Bind(const std::string &ip, uint16_t port)
    {
      struct sockaddr_in addr;
      Addr(&addr,ip,port);
      socklen_t len = sizeof(struct sockaddr_in);
      int ret = bind(_sockfd, (sockaddr*)&addr, len);
      if(ret<0)
      {
        perror("bind error");
        return false;
      }
        return true;
    }

    bool Listen(int backlog = MAX_LISTEN)
    {
      //int listen(描述符,同一时间最大连接数)
      int ret =listen(_sockfd,backlog);
      if(ret<0)
      {
        perror("listen error");
        return false;
      }
      return true;
    }

    bool Connect(const std::string &ip, u_int16_t port)
    {
      //connect(描述符,服务端地址,服务端地址长度)
      struct sockaddr_in addr;
      Addr(&addr,ip,port);

      socklen_t  len = sizeof(struct sockaddr_in); 
      
      int ret = connect(_sockfd,(struct sockaddr*)&addr, len);
      if(ret<0)
       {
        perror("connect error");
        return false;
      }
      return true;
    }

    bool Accpet(TcpSocket* sock, std::string *ip=NULL, uint16_t *port = NULL)
    {
      //accept(监听描述符,客户端地址,地址长度)
     struct sockaddr_in addr;
     
     socklen_t len = sizeof(struct sockaddr_in);
    int newfd = accept(_sockfd,(struct sockaddr*)&addr, &len);

    if(newfd<0)
    {
      perror("accept error");
      return false;
    }

    sock->_sockfd = newfd;
   
    if(ip != NULL)
    {
      *ip = inet_ntoa(addr.sin_addr);
    }

    if(port != NULL)
    {
      *port = ntohs(addr.sin_port);
    }

    return true;
    }

    bool Send(const std::string &data)
    {
      //ssize_t send(描述符,数据,长度,标志)
      int total =0;
      while(total <(int)data.size())
      {
      int ret = send(_sockfd, &data[0]+total,data.size()-total,0); 

      if(ret <0)
      {
        printf("发送失败\n");
        perror("send error");
        return false;
      }
      total+=ret;
      }
      return true;

    }

    bool Recv(std::string *buf)
    {
     //ssize_t recv(描述符,缓冲区,长度,标志);
     
     char tmp[4096]={0};
     int ret = recv(_sockfd,tmp,4096,0);
     if(ret<0)
     {
       perror("recv error");
       return false;
     }

     if(ret ==0)
     {
       std::cout<<"peer shutdown"<<std::endl;
       return false;
     }

      buf->assign(tmp,ret);//申请空间拷贝指定长度数据
      return true;
    }

    bool Close()
    {
      if(_sockfd != -1)
      {
        close(_sockfd);
      }
      return true;
    }

 private:
   int _sockfd;
};

tcp_srv.cpp(存在accept阻塞和recv阻塞,功能不完善)

#include"tcpsocket.hpp"
#include<string>
#include<cstring>

int main(int argc, char *argv[])
{
  //argc是程序运行参数个数
  //argv存放程序运行参数 ./tcp_srv 10.0.4.7 9000
  if(argc !=3)
  {
    printf("useage: ./tcp_srv 10.0.4.7 9000");
    return -1;
  }

  std::string ip = argv[1];
  uint16_t port = std::stoi(argv[2]);

  TcpSocket lst_sock;

  //1.创建套接字
  CHECK_RET(lst_sock.Socket());
  //2.绑定地址信息
  CHECK_RET(lst_sock.Bind(ip,port));
  //3.开始监听
  CHECK_RET(lst_sock.Listen());
  while(1)
  {
    //4.获取新建连接
    TcpSocket new_sock;
    bool ret = lst_sock.Accpet(&new_sock,&ip,&port);
    if(ret ==false)
    {
      usleep(1000);
      continue;
    }
    std::cout<<"new conn- "<<ip<<":"<<port<<std::endl;
    //5.通过新建链接与客户端通信
    std::string buf;
    ret = new_sock.Recv(&buf);
    if(ret ==false)
    {
      new_sock.Close();
      continue;
    }
    std::cout<<"client say: "<<buf<<std::endl;

    buf.clear();
    std::cout<<"server say: ";
    std::cin>>buf;
    ret = new_sock.Send(buf);
    if(ret ==false)
    {
      new_sock.Close();
      continue;

    }
    //6.关闭套接字
  }
    lst_sock.Close();
    return 0;
}

process_tcp_srv.cpp(多进程版本)

#include"tcpsocket.hpp"
#include<string>
#include<cstring>
#include<signal.h>

void worker(TcpSocket &new_sock)
{
  bool ret;
  while(1)
  {
    //5.通过新建链接与客户端通信
    std::string buf;
    ret = new_sock.Recv(&buf);
    if(ret ==false)
    {
      new_sock.Close();
      return;
    }
    std::cout<<"client say: "<<buf<<std::endl;

    buf.clear();
    std::cout<<"server say: ";
    std::cin>>buf;
    ret = new_sock.Send(buf);
    if(ret ==false)
    {
      new_sock.Close();
      return;
    }
  }
}
  int main(int argc, char *argv[])
  {
    //argc是程序运行参数个数
    //argv存放程序运行参数 ./tcp_srv 10.0.4.7 9000
    if(argc !=3)
    {
      printf("useage: ./tcp_srv 10.0.4.7 9000");
      return -1;
    }

    signal(SIGCHLD, SIG_IGN);//避免产生僵尸进程

    std::string ip = argv[1];
    uint16_t port = std::stoi(argv[2]);

    TcpSocket lst_sock;

    //1.创建套接字
    CHECK_RET(lst_sock.Socket());
    //2.绑定地址信息
    CHECK_RET(lst_sock.Bind(ip,port));
    //3.开始监听
    CHECK_RET(lst_sock.Listen());
    while(1)
    {
      //4.获取新建连接
      TcpSocket new_sock;
      bool ret = lst_sock.Accpet(&new_sock,&ip,&port);
      if(ret ==false)
      {
        usleep(1000);
        continue;
      }
      std::cout<<"new conn- "<<ip<<":"<<port<<std::endl;
      //创建一个新的子进程与客户端进行通信
      pid_t pid = fork();
      if(pid ==0)
      {
        worker(new_sock);
       new_sock.Close();
        exit(0);
      }
      //反而如果不关闭,会造成资源泄露
      new_sock.Close();//这个关闭不会影响客户端-父子进程数据独有

    }
    //6.关闭套接字
    lst_sock.Close();
    return 0;
  }

pthread_tcp_srv.cpp(多线程版本)

#include"tcpsocket.hpp"
#include<string>
#include<cstring>
#include<pthread.h>

void* worker(void* arg)
{
  TcpSocket* new_sock = (TcpSocket*)arg;
  while(1)
  {
   int ret =0;
    //5.通过新建链接与客户端通信
    std::string buf;
    ret = new_sock->Recv(&buf);
    if(ret ==false)
    {
      new_sock->Close();
      continue;
    }
    std::cout<<"client say: "<<buf<<std::endl;

    buf.clear();
    std::cout<<"server say: ";
    std::cin>>buf;
    ret = new_sock->Send(buf);
    if(ret ==false)
    {
      new_sock->Close();
     delete new_sock;
     return NULL;
    }    
  }
  return NULL;
}
int main(int argc, char *argv[])
{
  //argc是程序运行参数个数
  //argv存放程序运行参数 ./tcp_srv 10.0.4.7 9000
  if(argc !=3)
  {
    printf("useage: ./tcp_srv 10.0.4.7 9000");
    return -1;
  }

  std::string ip = argv[1];
  uint16_t port = std::stoi(argv[2]);

  TcpSocket lst_sock;

  //1.创建套接字
  CHECK_RET(lst_sock.Socket());
  //2.绑定地址信息
  CHECK_RET(lst_sock.Bind(ip,port));
  //3.开始监听
  CHECK_RET(lst_sock.Listen());
  while(1)
  {
    //4.获取新建连接
    TcpSocket* new_sock=new TcpSocket();
    bool ret = lst_sock.Accpet(new_sock,&ip,&port);
    if(ret ==false)
    {
      usleep(1000);
      continue;
    }
    std::cout<<"new conn- "<<ip<<":"<<port<<std::endl;
    pthread_t tid;
    pthread_create(&tid,NULL,worker,(void*)new_sock);
    pthread_detach(tid);
    //new通_sock.Close();//线程之间共享文件描述符表
    //6.关闭套接字
  }
    lst_sock.Close();
    return 0;
}

tcp_cli.cpp

#include"tcpsocket.hpp"
#include<string>

int main(int argc, char* argv[])
{
  if(argc !=3)
  {
    printf("useage: ./tcp_cli srvip srvport\n");
    return -1;
  }

  std::string srv_ip = argv[1];
  int16_t srv_port = std::stoi(argv[2]);

  TcpSocket cli_sock;

  //1.创建套接字
  CHECK_RET(cli_sock.Socket());
  //2.绑定地址信息
  //3.向服务端发起连接请求
  CHECK_RET(cli_sock.Connect(srv_ip,srv_port));
  while(1)
  {
  //4.与服务端进行通信
  std::string buf;
  std::cout<<"client say:";
  std::cin>>buf;
  CHECK_RET(cli_sock.Send(buf));

  buf.clear();
  CHECK_RET(cli_sock.Recv(&buf));
  std::cout<<"server say: "<<buf<<std::endl;
  }
  //5.关闭套接字
  cli_sock.Close();
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值