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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值