socket套接字編程之tcp協議封裝

1.tcp特點
①傳輸層協議
②有連接
③可靠傳輸
④面向字節流

2.幾個常用接口

// 創建 socket 文件描述符 (TCP/UDP, 客戶端 + 服務器)
int socket(int domain, int type, int protocol);
// 綁定端口號 (TCP/UDP, 服務器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 開始監聽socket (TCP, 服務器)
int listen(int socket, int backlog);
// 接收請求 (TCP, 服務器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
// 建立連接 (TCP, 客戶端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

tcp通用服務端(.hpp)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>



class TcpSvr
{
public:
  TcpSvr()
  {
    _sock = -1;
  }
  ~TcpSvr()
  {
    _sock = -1;
  }
  //創建套接字
  bool CreateSock()
  {
    _sock = socket(AF_INET, SOCK_STREAM, 6);
    if (_sock < 0) {
      perror("create");
      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());
    ssize_t ret = bind(_sock, (struct sockaddr*)&addr, sizeof(addr));
    if (ret < 0) {
      perror("bind");
      return false;
    }
    return true;
  }
//監聽(服務端)
  bool Listen(int Backlog)
  {
    ssize_t ret = listen(_sock, Backlog);
    if (ret < 0) {
      perror("listen");
      return false;
    }
    return true;
  }
//接收請求(服務端)
  bool Accept(TcpSvr& ts, struct sockaddr_in* addr = NULL)
  {
    struct sockaddr_in peeraddr;
    socklen_t len = sizeof(struct sockaddr_in);
   int ret = accept(_sock, (struct sockaddr*)&peeraddr, &len);
   if (ret < 0) {
     perror("accept");
     return false;
   }
   ts._sock = ret;
   if (addr != NULL){
     memcpy(addr, &peeraddr, len);
   }
   return true;
  }
//建立連接(客戶端調用)
  bool Connect(std::string& ip, uint16_t port)
  {

    struct sockaddr_in destaddr;
    destaddr.sin_family = AF_INET;
    destaddr.sin_port = htons(port);
    destaddr.sin_addr.s_addr = inet_addr(ip.c_str());
    int ret = connect(_sock, (struct sockaddr*)&destaddr, sizeof(destaddr));
    if (ret < 0) {
      perror("connect");
      return 0;
    }
    return true;
  }
//發送數據
  bool Send(std::string& buf)
  {

    int sendSize = send(_sock, buf.c_str(), buf.size(), 0);
    if (sendSize < 0) {
      perror("send");
      return false;
    }
    return true;
  }
//接收數據
  bool Recv(std::string& buffer)
  {
    char buf[1024] = {0};
    //0:阻塞接收
    //MSG_PEEK:探測接收
    int recvSize = recv(_sock, buf, sizeof(buf) - 1, 0);
    if (recvSize < 0) {
      perror("recv");
    }
    else if (recvSize == 0) {
      //如果recvSize等於0,表示對端將連接關閉了
      printf("connect error\n");
      return false;
    }
    buffer.assign(buf, recvSize);
    return true;
  }

  void Close()
  {
    close(_sock);
    _sock = -1;
  }
private:
    int _sock;
};

服務端(.cpp)

#include "tcpsvr.hpp"




int main(int argc, char* argv[])
{
  if (argc != 3) {
    printf("./ser [ip] [port]");
    return 0;
  }

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

  TcpSvr ts;
  if (!ts.CreateSock()) {
    return 0;
  }
  if (!ts.Bind(ip, port)) {
    return 0;
  }
  if (!ts.Listen(5)) {
    return 0;
  }

  //while (1) {
    TcpSvr peerts;
    if (!ts.Accept(peerts)) {
      return 0;
    }
  while (1){
    std::string buf;
    peerts.Recv(buf);
    printf("cli say: %s\n", buf.c_str());

    printf("svr say: ");
    fflush(stdout);
    std::cin >> buf;

    peerts.Send(buf);
  }
    peerts.Close();
  return 0;
}

客戶端

#include "tcpsvr.hpp"

int main(int argc, char* argv[])
{
  if (argc != 3){
    printf ("./cli [ip] [port]");
    return 0;
  }

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

  TcpSvr ts;
  if (!ts.CreateSock()) {
    return 0;
  }

  if (!ts.Connect(ip, port)) {
    return 0;
  }

  while (1) {
    printf("cli say: ");
    fflush(stdout);
    std::string buf;
    std::cin >> buf;
    ts.Send(buf);
    ts.Recv(buf);
    printf ("svr say: %s\n", buf.c_str());
  }
  ts.Close();
  return 0;
}

上面的服務端只能實現同時與單個客戶端進行通信,不能與多個客戶端通信,因爲他是單進程單線程的。

下面在服務端添加多進程,實現與多個客戶端同時通信

#include "tcpsvr.hpp"
#include <sys/wait.h>
#include <signal.h>

//回收子進程
void sigcd(int signo)
{
  while (1) {
    (void)signo;
    int ret = waitpid(-1, NULL, WNOHANG);
    if (ret == 0) {
      break;
    }
  }
}

int main(int argc, char* argv[])
{
  if (argc != 3) {
    printf("./svr [ip] [port]\n");
    return 0;
  }

  signal(SIGCHLD, sigcd);  
  std::string ip = argv[1];
  uint16_t port = atoi(argv[2]);

  TcpSvr ts;
  if (!ts.CreateSock()) {
    return 0;
  }
  if(!ts.Bind(ip, port)) {
    return 0;
  }
  if (!ts.Listen(5)) {
    return 0;
  }

  while (1) {
    //接收連接
    TcpSvr peerts;
    struct sockaddr_in peeraddr;
    if (!ts.Accept(peerts, &peeraddr))
    {
      continue;
    }
    printf("new connect: %s:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
    //創建子進程
    pid_t pid = fork();
    if (pid < 0) {
      perror("fork");
      exit(1);
    }
    else if (pid == 0) {
      //子進程服務
      std::string buf;
      while (1) {

        peerts.Recv(buf);
        printf("cli say: %s\n", buf.c_str());

        printf("svr say:");
        fflush(stdout);
        std::cin >> buf;
        peerts.Send(buf);
      }
      peerts.Close();
      exit(1);
    }
    peerts.Close();
  }
  return 0;
}


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