socket编程—UDP套接字

一、UDP套接字

IP是标识互联网当中的唯一一台主机
端口号是标识一台主机内的唯一一个进程
两者相加就是标识互联网当中唯一一个进程

整个互联网可以看作一个大的OS,所有在网络上的行为,基本都是在一个大的OS内,进行进程间通信!

所以后续我们IP+端口号=socket

要进行通信,本质
1、先找到目标主机
2、在找到该主机上的服务(进程)
互联网世界,是一个进程间通信的世界

进程具有独立性,进程间通信的前提工作:先让不同的进程,看到同一份资源。这里的同一份资源是网络!

socket函数的参数

在这里插入图片描述
套接字类型:用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数。
struct sockaddr_in结构体
在这里插入图片描述

socket()函数返回值

返回一个文件描述符
在这里插入图片描述

1、服务端

创建套接字

在这里插入图片描述
作为一个服务器,要不要让客户知道,对应的服务器的地址(ip+port)?必须知道!
服务器的socket信息(ip+port),必须得让客户知道!
一般的服务器port,必须是众所周知(不仅是被人,也可以被各种软件等知道的)的,并且轻易不改变!

绑定端口

bind()函数
在这里插入图片描述
在这里插入图片描述

提供服务

recvfrom()函数接收对端发来的数据

在这里插入图片描述

返回值:
在这里插入图片描述
sendto()返回给对端数据

在这里插入图片描述

server代码:

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

//const uint16_t port = 8081;
void Usage(std::string proc)
{
    std::cout<<"Usage:"<<proc<<"proc"<<std::endl;
}
int main(int argc,char *argv[])
{
    if(argc != 2){
        Usage(argv[0]);
        return -1;
    }
    uint16_t port = atoi(argv[1]);
    //AF_INET 表示 IPv4 地址   OCK_DGRAM(数据报套接字/无连接的套接字)
    int sock = socket(AF_INET,SOCK_DGRAM,0);//1、创建套接字
    if(sock < 0)//如果创建失败
    {
        std::cerr << "socket create error:" << errno <<std::endl;
        return 1;
    }
    //2、给该服务器绑定端口和ip(需要特殊处理,因为大小端序不一样)

    struct sockaddr_in local;//创建一个struct sockaddr_in结构的对象
    //开始填充该对象的字段
    local.sin_family = AF_INET;
    local.sin_port = htons(port);//此处的端口号,是我们计算机上的变量,是主机序列,可能需要主机转网络:htons函数
    //a、需要将人识别的点分十进制,字符串风格IP地址,转化为4字节整数IP
    //b、也考虑大小端
    //in_addr_t inet_addr(const char *cp) ;这个函数能完成ab两个工作
    //坑:
    //云服务器:不允许用户直接bind公网IP,另外,实际正常编写的时候,我们也不会指明IP
    //local.sin_addr.s_addr = inet_addr("175.178.47.247");//"175.178.47.247"为点分十进制,字符串风格
    //INADDR_ANY:如果你bind的是明确的IP(主机),意味只有发到该ip主机上的数据
    //才会交给你的网络进程,但是,一般服务器可能有多张网卡,配置多个ip,我们需要的不是
    //某个ip上面的数据,我们需要的是,所有发送到该主机,发送到该端口的数据!
    local.sin_addr.s_addr = INADDR_ANY;//INADDR_ANY零值,相当于默认

    if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0){//(struct sockaddr*)&local需要强转
        std::cerr << "bind error :" <<errno << std::endl;
    }

    //3、提供服务
    bool quit = false;
    #define NUM 1024
    char buffer[NUM];//接收数据缓冲区
    while(!quit)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);

        //注意:我们默认认为通信的数据是双方在互发字符串
        ssize_t cnt = recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);//接收客户端发来的数据,并确认对端是谁
        if(cnt > 0){
        buffer[cnt] = 0;
        std::cout<<"client# " << buffer <<std::endl;

        //根据用户输入,构建一个新的返回字符串
        std::string echo_hello = buffer;
        echo_hello += "....";
        sendto(sock,echo_hello.c_str(),echo_hello.size(),0,(struct sockaddr*)&peer,len);//返回(发送)数据给对端服务器
        }
        else{

        }
    }
    return 0;
    
}

2、客户端

创建套接字

在这里插入图片描述

//客户端需要显示的bind嘛?不需要
//a、首先,客户端必须也要有ip和port
//b、但是,客户端不需要显示的bind!一旦显示bind,就必须明确,client要和哪一个port关联
//client指明的端口号,在client端一定有嘛?有可能被占用,被占用导致client无法使用
//server要的是port必须明确,而且不变,但是client只要有就行!一般是由OS自动给你bind()
//就是client正常发送数据的时候,OS会自动给你bind,采用的是随机端口的方式!

在这里插入图片描述
client端代码:

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

void Usage(std::string proc)
{
    std::cout<<"Usage: \n\t" <<proc<<"server_ip server_port"<<std::endl;
}
int main(int argc,char *argv[])
{
    if(argc != 3){
        Usage(argv[0]);
        return 0;
    }
    //1、创建套接字。打开网咯文件
    int sock = socket(AF_INET,SOCK_DGRAM,0);
    if(sock < 0){
        std::cerr<<"socket error" <<errno<<std::endl;
        return 1;
    }

    //客户端需要显示的bind嘛?不需要
    //a、首先,客户端必须也要有ip和port
    //b、但是,客户端不需要显示的bind!一旦显示bind,就必须明确,client要和哪一个port关联
    //client指明的端口号,在client端一定有嘛?有可能被占用,被占用导致client无法使用
    //server要的是port必须明确,而且不变,但是client只要有就行!一般是由OS自动给你bind()
    //就是client正常发送数据的时候,OS会自动给你bind,采用的是随机端口的方式!

    struct sockaddr_in server;//填充服务端数据
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[2]));//存储端口号
    server.sin_addr.s_addr = inet_addr(argv[1]);//存储IP地址
    //2、使用服务
    while(1){
        //a、你的数据从哪里来?
        std::string message;
        std::cout<<"输入#";
        std::cin >> message;

        //b.你要发给谁?
        sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));//发送数据给对端

        //此处tmp就是一个“占位符”
        struct sockaddr_in tmp;
        socklen_t len = sizeof(tmp);
        char buffer[1024];
        ssize_t cnt = recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr*)&tmp,&len);
        if(cnt > 0){
            //在网络通信中,只有报文大小,或者是字节流中字节的个数,没有C/C++字符串这样的概念
            buffer[cnt] = 0;
            std::cout<<"server# " << buffer <<std::endl;
        }
        else{

        }
    }
    return 0;
}

注:一些函数的解析为网上截图,如有侵权请联系删除;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

清晨白米稀饭.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值