TCP模擬實現

實現一個TCPSocket類

/*封裝TCPSocket類,向外提供更加輕便的tcp套接字接口
 * 1.創建套接字     Socket()
 * 2.綁定地址信息   Bind(std::string &ip,uint16_t port)
 * 3.服務端開始監聽,客戶端向服務端發起連接請求   
    Listen(int backlog=5)   Connet(std::string& server_ip,uint16_t server_port)
 * 4.服務端獲取已完成連接的客戶端新socket     
    Accept(TCPSocket &clientsock,std::string &client_ip,uint16_t port)
 * 5.接受數據   Recv(std::string& buf)
 * 6.發送數據   Send(std::string& buf)
 * 7.關閉套接字   Close() 
 * */
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define CHECK_RET(q) if(q==false){return -1}  
class TCPSocket
{
public:
  TCPSocket():_sockfd(-1)
  {}
  ~TCPSocket()
  {}
  bool Socket()
  {
    _sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(_sockfd<0)
    {
      perror("socket error");
      return false;
    }
    return true;
  }
  bool Bind(std::string &ip,uint16_t port)
  {
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(port);
    addr.sin_addr.s_addr=inet_addr(ip.c_str());
    
    socklen_t len=sizeof(struct sockaddr_in);
    int ret=bind(_sockfd,(struct sockaddr*)&addr,len);
    if(ret<0)
    {
      perror("bind error");
      return false;
    }
    return true;
  }
  bool Listen(int backlog=5)
  {
    //int listen(int sockfd, int backlog);
    //backlog:最大併發連接數
    int ret=listen(_sockfd,backlog);
    if(ret<0)
    {
      perror("listen error");
      return false;
    }
    return true;
  }
  bool Accept(TCPSocket &client,std::string &client_ip,uint16_t &client_port)
  {
    //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    //sockfd:套接字描述符
    //addr:新建連接的客戶端地址信息
    //addrlen:新建客戶端的地址信息長度
    //返回值:返回新建客戶端socket的描述符
    struct sockaddr_in addr;
    socklen_t len=sizeof(struct sockaddr_in);
    int sockfd=accept(_sockfd,(struct sockaddr*)&addr,&len);
    if(sockfd<0)
    {
      perror("accept error");
      return false;
    }
    client.SetFd(sockfd);
    client_ip=inet_ntoa(addr.sin_addr);
    client_port=ntohs(addr.sin_port);
    return true;
  }
  void SetFd(int sockfd)
  {
    _sockfd=sockfd;
  }
  bool Connect(std::string &server_ip,uint16_t server_port)
  {
    //int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    //sockfd:套接字描述符
    //addr:服務端地址信息
    //addrlen:地址信息長度
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(server_port);
    addr.sin_addr.s_addr=inet_addr(server_ip.c_str());
    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 Recv(std::string &buf)
  {
    //ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    //sockfd:服務端爲新客戶端新建的socket描述符
    //flags:0-默認阻塞接收,沒有數據就一直等待
    //    MSG_PEEK  接受數據,但是數據並不從接收緩衝區移除,常用於探測性數據接收
    //返回值:實際接收字節長度;  出錯返回-1; 若連接斷開則返回0
    //recv默認是阻塞的,意味着沒有數據則一直等,不會返回0
    //返回0只有一種情況,就是連接斷開,不能再繼續通信了
    char tmp[4096];
    int ret=recv(_sockfd,tmp,4096,0);
    if(ret<0)
    {
      perror("recv error");
      return false;
    }
    else if(ret==0)
    {
      printf("peer shutdown\n");
      return false;
    }
    buf.assign(tmp,ret);
    return true;
  }
  bool Send(std::string &buf)
  {
    //ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    int ret=send(_sockfd,buf.c_str(),buf.size(),0);
    if(ret<0)
    {
      perror("send error");
      return false;
    }
    return true;
  }
  bool Close()
  {
    close(_sockfd);
    return true;
  }
private:
  int _sockfd;
};
int main()
{
  return 0;
}

TCP服務端程序

/*基於封裝的TCPSocket,實現tcp服務端程序
 * 1.創建套接字
 * 2.爲套接字綁定信息
 * 3.開始監聽
 * while(1)
 * {
 *    4.從已完成連接隊列,獲取新建的客戶端連接socket
 *    5.通過新建的客戶端連接socket與指定的客戶端進行通信,recv
 *    6.send
 * }
 * 7.關閉套接字*/
#include "tcpsocket.hpp"

int main(int argc,char* argv[])
{
  if(argc!=3)
  {
    std::cout<<"./tcp_server 192.168.32.130 9000\n";
    return -1;
  }
  std::string ip=argv[1];
  uint16_t port=atoi(argv[2]);

  TCPSocket sock;

  CHECK_RET(sock.Socket());
  CHECK_RET(sock.Bind(ip,port));
  CHECK_RET(sock.Listen());
  while(1)
  {
    TCPSocket clientsock;
    std::string client_ip;
    uint16_t client_port;
    if(sock.Accept(clientsock,client_ip,client_port)==false)
    {
      continue;
    }
    std::cout<<"new client:"<<client_ip<<":"<<client_port<<std::endl;
    std::string buf;
    clientsock.Recv(buf);
    std::cout<<"client say:"<<buf<<std::endl;
    buf.clear();
    std::cout<<"server say:";
    fflush(stdout);
    std::cin>>buf;
    clientsock.Send(buf);
  }
  sock.Close();
  return 0;
}


TCP客戶端程序

/*基於封裝的TCPSocket實現tcp客戶端程序
 * 1.創建套接字
 * 2.爲套接字綁定地址信息(不推薦用戶手動綁定)
 * 3.向服務端發起連接請求
 * while(1)
 * {
 *    4.發送數據
 *    5.接受數據
 * }
 * 6.關閉套接字*/
#include "tcpsocket.hpp"
#include <signal.h>

void sigcb(int signo)
{
  printf("connection closed\n");
}
int main(int argc,char* argv[])
{
  if(argc!=3)
  {
    std::cout<<"./tcp_client 192.168.32.130 9000\n";
    return -1;
  }
  std::string ip=argv[1];
  uint16_t port=atoi(argv[2]);
  
  signal(SIGPIPE,sigcb);
  TCPSocket sock;
  CHECK_RET(sock.Socket());
  CHECK_RET(sock.Connect(ip,port));
  while(1)
  {
    std::string buf;
    std::cout<<"client say:";
    fflush(stdout);
    std::cin>>buf;
    sock.Send(buf);

    buf.clear();
    sock.Recv(buf);
    std::cout<<"server say:"<<buf<<std::endl;
  }
  sock.Close();
  return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章