套接字编程:UDP通信程序

目录

一、服务端

1.编写流程

1.1 创建套接字

1.2 为套接字绑定地址信息

1.3 接收数据

1.4 发送数据

1.5 关闭套接字

2. 代码实现

二、客户端

1.编写流程

1.1 创建套接字

1.2 为套接字绑定地址信息(不推荐)

1.3 向服务器发送数据

1.4 接收数据

1.5 关闭套接字

2. 代码实现

2.1 客户端头文件-类封装

2.2 客户端实现


一、服务端

1.编写流程

1.1 创建套接字

        在内核中创建socket结构体,将进程与网卡关联起来。

1.2 为套接字绑定地址信息

        给创建的套接字socket结构体描述源端地址信息。

作用:

        1)告诉系统,网卡收到的那条数据应该交给我来处理;

        2)当发生数据时,使用绑定的地址信息作为源端地址信息。

1.3 接收数据

        从socket的接收缓冲区中取出数据。

1.4 发送数据

        把要发送的数据放到发送缓冲区中。

1.5 关闭套接字

2. 代码实现

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<netinet/in.h> //struct_sockaddr_in 结构
#include<arpa/inet.h> //字节序转换接口
#include<sys/socket.h> //socket接口头文件
#include<string.h>


int main() {
  //1.创建套接字
  int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  if (sockfd < 0) {
    perror("Create socket error!");
    return -1;
  }

  //2.为套接字绑定地址信息
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(8888);
  addr.sin_addr.s_addr = inet_addr("192.168.247.128");
  
  socklen_t len = sizeof(struct sockaddr_in);
  int ret = bind(sockfd, (struct sockaddr*)&addr, len);//绑定地址信息
  if (ret < 0) {
    perror("bind error!");
    return -1;
  }

  //接收&发送 数据
  while (1) {
    //3.接收数据
    char buf[1024] = {0};
    struct sockaddr_in client;//保存发送方的地址信息
    ret = recvfrom(sockfd, buf, 1023, 0, (struct sockaddr*)&client, &len);
    if (ret < 0) {
      perror("recvfrom error!");
      close(sockfd);
      return -1;
    }
    //打印客户端的信息
    printf("client:%s 端口:%d 数据:%s\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port), buf);
    
    //4.发送数据
    printf("server send: ");
    fflush(stdout);
    char data[1024] = {0};
    fgets(data, 1023, stdin);
    ret = sendto(sockfd, data, strlen(data), 0, (struct sockaddr*)&client, len);
    if (ret < 0) {
      perror("sendto error!");
      close(sockfd);
      return -1;
    }
  }

  //5.关闭套接字
  close(sockfd);

  return 0;
}

二、客户端

1.编写流程

1.1 创建套接字

1.2 为套接字绑定地址信息(不推荐)

        客户端并不推荐主动绑定地址。

原因:

        1)绑定之后,程序只能启动一个;

        2)客户端并不需要固定使用某个地址。

1.3 向服务器发送数据

        发送数据前,若socket没有绑定指定地址,则系统会自动选择一个合适的地址信息进行绑定。

1.4 接收数据

1.5 关闭套接字

2. 代码实现

        通过封装一个客户端类,可以更加便捷的创建多个客户端。

2.1 客户端头文件-类封装

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

//封装UDPsocket类
//通过类实例化对象,调用成员接口,简单的完成服务器与客户端搭建

class UDPsocket {
  private:
    int _sockfd;
  public:
    UDPsocket() : _sockfd(-1) {}
    ~UDPsocket() {if(_sockfd != -1) close(_sockfd);}

    //1.创建套接字
    bool Socket() {
      _sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      if (_sockfd < 0) {
        perror("create socket error!");
        return false;
      }
      return true;
    }

    //2.绑定地址信息
    bool Bind(const 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;
    }

    //3.发送数据
    bool Send(const std::string &data, const 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 = sendto(_sockfd, data.c_str(), data.size(), 0, (struct sockaddr*)&addr, len);

      if (ret < 0) {
        perror("sendto error!");
        return false;
      }
      return true;
    }

    //4.接收数据
    bool Recve(std::string *buf, std::string *ip = NULL, uint16_t *port = NULL) {
      struct sockaddr_in addr;
      socklen_t len = sizeof(struct sockaddr_in);

      char temp[4096] = {0};

      int ret = recvfrom(_sockfd, temp, 4095, 0, (struct sockaddr*)&addr, &len);

      if (ret < 0) {
        perror("recvfrom error!");
        return false;
      }

      if (ip != NULL) *ip = inet_ntoa(addr.sin_addr);
      if (port != NULL) *port = ntohs(addr.sin_port);
      *buf = temp;
      return true;
    }

    //5.关闭套接字
    bool Close() {
      if (_sockfd != -1) close(_sockfd);
      return true;
    }
};

2.2 客户端实现

#include "udp_socket.hpp"

#define CHECK_RETURN(x) if ((x) == false) {return -1;}

int main(int argc, char *argv[]) {

  if (argc != 3) {
    std::cout<<"Usage: ./udp_client ip port"<< std::endl;
    std::cout<<"Server address!"<< std::endl;
    return -1;
  }

  UDPsocket sock;//创建客户端对象
  std::string srv_ip = argv[1];
  int srv_port = std::stoi(argv[2]);


  //1.创建套接字
  CHECK_RETURN(sock.Socket());
  //2. 绑定地址信息----客户端不推荐
  
  while (1) {
    std::cout<<"client send:";
    fflush(stdout);
    std::string buf;
    std::cin>>buf;

    //3.发送数据
    CHECK_RETURN(sock.Send(buf, srv_ip, srv_port));

    //4.接收数据
    buf.clear();
    CHECK_RETURN(sock.Recve(&buf));
    std::cout<<"server reply:" << buf << std::endl;
  }
  
  //5.关闭套接字
  CHECK_RETURN(sock.Close());
  return 0;;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hey小孩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值