网络编程套接字(二)

网络编程套接字(二)

一、简单的UDP网络程序

  • 封装udp_socket
#pragma once
#include <cstdio>
#include <cstring>
#include <string>
#include <cassert>
#include <cstdlib>

#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

class UdpSocket
{
  public:
    UdpSocket()
      :fd_(-1)
    {}

    bool Socket()
    {
      //创建socket操作句柄fd
      fd_ = socket(AF_INET,SOCK_DGRAM,0);
      if(fd_ < 0)
      {
        perror("socket error");
        return false;
      }

      return true;
    }

    //关闭套节字
    bool Close()
    {
      close(fd_);
      return true;
    }

    //绑定端口、IP
    bool Bind(const std::string& ip,uint16_t port)
    {
      sockaddr_in addr;
      addr.sin_family = AF_INET;
      addr.sin_addr.s_addr = inet_addr(ip.c_str());//把点分十进制类型的ip字符串转化为32位的IP地址
      addr.sin_port = htons(port);//把主机字节序转化为网络字节序

      int ret  = bind(fd_,(sockaddr*)&addr,sizeof(addr));
      if(ret < 0)
      {
        perror("bind is error");
        return false;
      }

      return true;

    }

    //接收数据
    //出参
    bool RecvFrom(std::string* buf,std::string* ip = nullptr,uint16_t* port = nullptr)
    {
      //出参,由函数调用时填充
      char temp[1024 * 10] = {0};
      sockaddr_in peer;
      socklen_t len = sizeof(peer);

      ssize_t read_size = recvfrom(fd_,temp,sizeof(temp) -1,0,(sockaddr*)&peer,&len);
      if(read_size < 0)
      {
        perror("recvfrom is error");
        return false;
      }

      buf->assign(temp,read_size);

      if(ip != nullptr)
      {
        *ip = inet_ntoa(peer.sin_addr);
      }
      if(port != nullptr)
      {
        *port = ntohs(peer.sin_port);
      }

      return true;
    }

    //发送数据
    bool SendTo(const std::string& buf,const std::string& ip,uint16_t port)
    {
      sockaddr_in addr;

      addr.sin_family = AF_INET;
      addr.sin_addr.s_addr = inet_addr(ip.c_str());
      addr.sin_port = htons(port);

      ssize_t write_size = sendto(fd_,buf.data(),buf.size(),0,(sockaddr*)&addr,sizeof(addr));
      if(write_size < 0)
      {
        perror("sendto is error");
        return false;
      }

      return true;
    }
  private:
    int fd_;
};

  • udp_server.hpp
#pragma once
#include <functional>
#include "udp_socket.hpp"

typedef std::function<void(const std::string&, std::string* resp)> Handler;

class UdpServer
{
  public:
    UdpServer()
    {
      //在构造函数内部创建sock
      assert(sock_.Socket());
    }

    ~UdpServer()
    {
      sock_.Close();
    }

    bool Start(const std::string& ip, uint16_t port,Handler handler)
    {
      printf("server is start!\n");

      //绑定端口号
      bool ret = sock_.Bind(ip,port);
      if(!ret)
      {
        return false;
      }

      //进入死循环,循环处理客户的请求,并计算响应返回结果
      while(1)
      {
        //读取客户端的请求
        std::string req;
        std::string remote_ip;
        uint16_t remote_port;

        bool ret = sock_.RecvFrom(&req,&remote_ip,&remote_port);
        if(!ret )
        {
          //如果读取失败尝试循环继续读取,不能直接退出
          continue;
        }
        
        //根据请求经过Handler计算响应,并返回结果
        std::string resp;
        handler(req,&resp);

        ret = sock_.SendTo(resp,remote_ip,remote_port);
        printf("[%s : %d] req: %s, resp: %s\n",remote_ip.c_str(),remote_port,req.c_str(),resp.c_str());
      }

      sock_.Close();
      return true;
    }

  private:
    UdpSocket sock_;
};

  • udp_client.hpp
#pragma once
#include "udp_socket.hpp"

class UdpClient
{
  public:
    UdpClient(const std::string& ip,uint16_t port)
      :ip_(ip)
       ,port_(port)
    {
      assert(sock_.Socket());
    }

    ~UdpClient()
    {
      sock_.Close();
    }

    bool RecvFrom(std::string* buf)
    {
      return sock_.RecvFrom(buf);
    }

    bool Sendto(const std::string& buf)
    {
      return sock_.SendTo(buf,ip_,port_);
    }

  private:
    UdpSocket sock_;
    
    //服务器端ip、port
    std::string ip_;
    uint16_t port_;
};

  • dict_server.cc
#include "udp_server.hpp"
#include <iostream>
#include <unordered_map>

std::unordered_map<std::string,std::string> g_dict;

void Translate(const std::string& req,std::string* resp)
{
  auto it = g_dict.find(req);
  if(it == g_dict.end())
  {
    *resp = "未查到";
    return;
  }
  *resp = it->second;
}

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

  g_dict.insert(std::make_pair("hello", "你好"));
  g_dict.insert(std::make_pair("world", "世界"));
  g_dict.insert(std::make_pair("c++", "最好的编程语言"));
  g_dict.insert(std::make_pair("bit", "特别NB"));

  UdpServer server;
  server.Start(argv[1],atoi(argv[2]),Translate);

  return 0;
}
#endif

int main()
{
  g_dict.insert(std::make_pair("world", "世界"));
  g_dict.insert(std::make_pair("c++", "最好的编程语言"));
  g_dict.insert(std::make_pair("bit", "特别NB"));
  g_dict.insert(std::make_pair("hello", "您好"));

  UdpServer server;
  server.Start("0.0.0.0",9090,Translate);

  return 0;
}

  • dict_client.cc
#include "udp_client.hpp"
#include <iostream>

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

  UdpClient client(argv[1], atoi(argv[2]));

  for (;;)
  {
    std::string word;
    std::cout << "请输入您要查的单词: ";
    std::cin >> word;

    client.Sendto(word);

    std::string result;
    client.RecvFrom(&result);
    std::cout << word << " 意思是 " << result << std::endl;
  }
  return 0;
}

在这里插入图片描述
在这里插入图片描述

已标记关键词 清除标记
<p> 这是一门linux下c++通讯架构实战课程,针对c/c++语言已经掌握的很熟并希望进一步深造以将来用c++在linux下从事网络通讯领域/网络服务器的开发和架构工作。<br /> <br /> 这门课程学习难度颇高但也有着极其优渥的薪水(最少30K月薪,最高可达60-80K月薪),这门课程,会先从nginx源码的分析和讲解开始,逐步开始书写属于自己的高性能服务器框架代码,完善个人代码库,这些,将会是您日后能取得高薪的重要筹码。 </p> <p> <br /> </p> <p> <span style="color:#E53333;">本课程原计划带着大家逐行写代码,但因为代码实在过于复杂和精细,带着写代码可能会造成每节课至少要4~5小时的超长时间,所以老师会在课前先写好代码,主要的时间花费在逐行讲解这些代码上,这一点望同学们周知。如果你觉得非要老师领着写代码才行的话,老师会觉得你当前可能学习本门课程会比较吃力,请不要购买本课程,以免听不懂课程并给老师差评,差评也会非常影响老师课程的销售并造成其他同学的误解。</span> </p> <p> <br /> </p> <p> 这门课程要求您具备下面的技能:<br /> (1)对c/c++语言掌握的非常熟练,语言本身已经不是继续学习的障碍,并不要求您一定熟悉网络或者linux;<br /> (2)对网络通讯架构领域有兴趣、勇于挑战这个高难度的开发领域并期望用大量的付出换取高薪;<br /> <br /> 在这门课程中,实现了一个完整的项目,其中包括通讯框架和业务逻辑框架,浓缩总结起来包括如下几点:<br /> (1)项目本身是一个极完整的多线程高并发的服务器程序;<br /> (2)按照包头包体格式正确的接收客户端发送过来的数据包, 完美解决收包时的数据粘包问题;<br /> (3)根据收到的包的不同来执行不同的业务处理逻辑;<br /> (4)把业务处理产生的结果数据包正确返回给客户端;<br /> <br /> 本项目用到的主要开发技术和特色包括:<br /> (1)epoll高并发通讯技术,用到的触发模式是epoll中的水平触发模式【LT】;<br /> (2)自己写了一套线程池来处理业务逻辑,调用适当的业务逻辑处理函数处理业务并返回给客户端处理结果;<br /> (3)线程之间的同步技术包括互斥量,信号量等等;<br /> (4)连接池中连接的延迟回收技术,这是整个项目中的精华技术,极大程度上消除诸多导致服务器程序工作不稳定的因素;<br /> (5)专门处理数据发送的一整套数据发送逻辑以及对应的发送线程;<br /> (6)其他次要技术,包括信号、日志打印、fork()子进程、守护进程等等; </p> <div> <br /> </div>
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页