UDP 网络程序

UDP 网络程序

接口

// UDP专用,发送数据到指定的IP地址和端口
int sendto(int sockfd, const void * msg, int len, 
unsigned int flags, const struct sockaddr * dst_addr, int addrlen);
// UDP专用,接收数据,返回数据远端的IP地址和端口
int recvfrom(int sockfd, void * buf, size_t len, 
int flags, struct sockaddr * src_addr, int * addrlen);

从网络中接收数据,把收到数据在本地打印出来,再经拼凑,返回给客户端,完成一个简单的echo server。

  • udpServer.hpp
#pragma once

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

class udpServer{
    private:
        int port;
        int sock;
    public:
        udpServer(int _port = 8080)
            : port(_port)
        {}
        void initServer()
        {
            // 创建套接字,AF_INET:所创建的套接字 底层通信标准使用的是IPV4
            sock = socket(AF_INET, SOCK_DGRAM, 0);// 默认AF_INET, UDP SOCK_DGRAM
            std::cout<< "sock: "<< sock <<std::endl;
            struct sockaddr_in local;

            //填入
            local.sin_family = AF_INET;
            local.sin_port = htons(port); // 主机序列转换为网络序列
            local.sin_addr.s_addr = INADDR_ANY; // 这个宏表示本地的任意IP地址

            // 为什么需要绑定?创建完套接字本质是创建了文件,并没有和网络信息关联,是需要填入ip和端口号的。
            // 绑定,把内存文件和网络信息关联起来,最重要的两个关键信息:ip和端口号,绑定的目的:要让别人能看到我
            // 1、填充进当前服务器的ip地址和端口号
            // 2、不同套接字的操作方法不一样,要初始化底层的函数指针,让它指向不同的方法
            if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
            {
                std::cerr<< "bind error \n";
                exit(1);
            }
        }

        //echo server
         void start()
         {
            char msg[64];
            for(;;) // 从网络中接收数据,把收到数据在本地打印出来,再经拼凑,返回给客户端,完成一个简单的echo server
            {
                msg[0] = '\0'; // 清空字符串
                struct sockaddr_in end_point; // 对端
                socklen_t len = sizeof(end_point);
                ssize_t s = recvfrom(sock, msg, sizeof(msg)-1, 0, (struct sockaddr*)&end_point, &len); // 接收数据
                if(s>0)
                {
                    // 想知道是哪个ip/port连的服务器
                    char buf[16];
                    sprintf(buf, "%d", ntohs(end_point.sin_port));// 网络序列转主机序列,并把整数转字符串

                    // 从网络中读出来的sin_addr是四字节ip,要把它转换成点分十进制ip;同时在转换过程中,把网络序列转主机
                    std::string cli = inet_ntoa(end_point.sin_addr);
                    cli += ":";
                    cli += buf;

                    msg[s] = '\0';
                    std::cout << "ip:port为 " << cli << "发来消息: "<<msg<<std::endl;
                    std::string echo_string = msg;
                    echo_string += "[服务器已收到";
                    echo_string += cli; 
                    echo_string += "发来的消息]";
                    sendto(sock, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&end_point, len); //发送数据
                }
            }
         }
        ~udpServer()
        {
            close(sock);                                            
        }
};
  • udpServer.cc
#include"udpServer.hpp"

void Usage(std::string proc)
{
    std::cout << "输入格式:" << proc << " local_port" << std::endl;
}
int main(int argc, char* argv[])
{
    if(argc != 2){
        Usage(argv[0]);
        exit(1);
    }
    udpServer* up = new udpServer(atoi(argv[1]));
    up->initServer();
    up->start();

    delete up;
    return 0;
}

  • udpClient.hpp
#pragma once

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

class udpClient{
    private:
        std::string ip;
        int port;
        int sock;
    public:
        // ip, port 用的是server的ip port
        udpClient(std::string _ip = "127.0.0.1", int _port = 8080) 
            :ip(_ip), port(_port)
        {}
        void initClient()
        {
            // 客户端不需要绑定,不代表客户端不需要ip和端口
            // 服务器是被动的,需要被客户端寻找,而不是主动寻找客户端。绑定的意义就是让别人找到我~
            // 原因是:服务器对应的ip和port不可以轻易被更改,必须是确定的,众所周知的;如果改了,客户端就找不到了,无法访问。
            // 客户端在进行bind的时候,很容易冲突,进而导致客户端无法启动!
            // 客户端需要唯一性,但不需要明确必须是哪个端口号;在udp下,系统会自动对client进行ip和端口号的绑定
            // 系统最清楚哪个ip号和端口号还没有被占,因此它来分配不会出现冲突

            sock = socket(AF_INET, SOCK_DGRAM, 0);
            std::cout<<"sock: "<< sock << std::endl; 
        }

        //echo server
        void start()
        {
            std::string msg;

            //远端服务器 ——填的是服务器的端口IP,表明给服务器发送数据
            struct sockaddr_in peer;
            peer.sin_family = AF_INET;
            peer.sin_port = htons(port);
            peer.sin_addr.s_addr = inet_addr(ip.c_str());

            for(;;)
            {
                std::cout << "please enter# :";
                std::cin >> msg;
                if(msg == "quit")
                {
                    break;
                }

                sendto(sock, msg.c_str(), msg.size(), 0, (struct sockaddr*)&peer, sizeof(peer)); // 往服务器里发数据

                char echo[128];
                ssize_t s = recvfrom(sock, echo, sizeof(echo)-1, 0, nullptr, nullptr); // 接收数据
                if(s>0)
                {
                    echo[s] = 0;
                    std::cout<< "server# "<< echo << std::endl;
                }
            }
        }
        ~udpClient()
        {
            close(sock);
        }
};
  • udpClient.cc
#include"udpClient.hpp"

void Usage(std::string proc)
{
    std::cout << "输入格式:" << proc << " server_ip server_port" << std::endl;
}
int main(int argc, char* argv[])
{
    if(argc != 3){
        Usage(argv[0]);
        exit(1);
    }
    udpClient* uc = new udpClient(argv[1], atoi(argv[2]));
    uc->initClient();
    uc->start();

    delete uc;
}
  • Makefile
.PHONY:all
all:udpClient udpServer

udpClient:udpClient.cc
	g++ -o $@ $^ -std=c++11
udpServer:udpServer.cc
	g++ -o $@ $^
.PHONY:clean
clean:
	rm -f udpClient udpServer

效果 ↓
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值