day30

一、套接字属性


1.1 套接字属性的获取和设置

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
 
       int getsockopt(int sockfd, int level, int optname,
 void *optval, socklen_t *optlen);
       int setsockopt(int sockfd, int level, int optname,
 const void *optval, socklen_t optlen);
       功能:设置套接字在不同层上的属性
       参数1:套接字文件描述符
       参数2:要设置的层
               应用层:SOL_SOCKET
               传输层:tcp传输:IPPROTO_TCP
                       udp传输:IPPROTO_UDP
               网络层: IPPROTO_IP           
        参数3:要设置当前层的属性名称 ,常用每层属性见下表
        参数4:要设置或者获取属性的值    ,一般为int类型
        参数5:参数4的大小
        返回值:成功     返回0,失败返回-1并置位错误码                
 
 

1.2 案例实现

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
    //1、创建一个套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sfd == -1)
    {
        perror("socket error");
        return -1;
    }
 
 
    //2、获取套接字地址重用的值
    int get_val = 1;
    socklen_t size = sizeof(get_val);       //获取属性值的大小
    if(getsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &get_val, &size) == -1)
    {
        perror("getsockopt error");
        return -1;
    }
    printf("get_val = %d\n", get_val);              //如果结果为0,表示套接字默认不允许端口号快速重用
 
    //3、设置允许端口号快速重用
    int set_val = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &set_val, sizeof(set_val)) ==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");
 
    //4、验证是否设置成功了
    get_val = 0;
    if(getsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &get_val, &size) == -1)
    {
        perror("getsockopt error");
        return -1;
    }
    printf("get_val = %d\n", get_val);      //如果结果为1,表示套接字端口号快速重用设置成功
 
 
 
 
 
    return 0;
}
 

 
二、网络通信方式


1>    单播:发送端和接收端完成一对一的通信方式。目前的通信模型都是单播


2>    广播:发送端和接收端完成一对多的通信方式,网络将发送端的数据,全部复制一遍发送给每个接收端一份


3>    组播:发送端和接收端完成一对多的通信方式,但是仅仅只限于加入多播组的成员。
 


三、广播


1>    广播是实现网络通信中一对多的通信方式,发送端用于发送数据,每个接收端都可以收到消息
2>    对于套接字而言,一般是不允许发送广播消息的,需要对发送端套接字进行设置允许广播
        setsockopt     --->    SOL_SOCKET    ---->   SO_BROADCAST      ---->  int
3>    广播的发送端需要绑定广播地址
广播地址:网络号 + 255    
当前网络中,主机号为255的那个ip地址
4>    广播消息不允许穿过路由器,广播地址只对当前局域网中的所有主机进行消息的转发
5>    广播分为发送端和接收端,发送端用于发送数据,接收端用于接收数据
6>    广播只能使用UDP实现,接收端无论愿不愿意接收,发送端都正常发送消息
7>    广播的发送端流程    ---> UDP的客户端

1、socket();          //创建用于通信的套接字文件描述符
2、bind();             //可选,可以绑定也可以不绑定
3、setsockopt();           //设置当前套接字允许广播
4、sendto();               //向广播地址发送消息
5、close();                //关闭套接字
 

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
    //1、创建用于通信的套接字文件描述符
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("sfd = %d\n", sfd);
    
    //2、绑定可选
    
    //3、设置允许消息广播
    int broad = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_BROADCAST, &broad, sizeof(broad)) == -1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("设置广播成功\n");
    
    //4、向广播地址发送消息
    //4.1 封装广播的地址信息
    struct sockaddr_in bin;
    bin.sin_family = AF_INET;
    bin.sin_port = htons(5555);
    bin.sin_addr.s_addr = inet_addr("192.168.0.255");
 
    char wbuf[128] = "";
    while(1)
    {
        printf("请输入>>>");
        fgets(wbuf, sizeof(wbuf), stdin);
        wbuf[strlen(wbuf)-1] = 0;
 
        //发送消息
    sendto(sfd, wbuf, strlen(wbuf), 0, (struct sockaddr*)&bin, sizeof(bin));
        printf("发送成功\n");
    }
    
    //5、关闭套接字
    close(sfd);
 
    return 0;
}
 
 


8>    广播的接收端流程    ----> UDP的服务器端
 

1、socket();          //创建用于通信的套接字文件描述符
2、bind();             //必须进行,但是,绑定的广播地址和端口

3、recv();             //向广播地址发送消息
4、close();                //关闭套接字


#include<myhead.h>
 
int main(int argc, const char *argv[])
{
    //1、创建用于通信的套接字文件描述符
    int rfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(rfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("rfd = %d\n", rfd);             //3
    
 
    //2、绑定广播地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in rin;
    rin.sin_family = AF_INET;        //通信域
    rin.sin_port = htons(5555);        //端口号
    rin.sin_addr.s_addr = inet_addr("192.168.0.255");   //广播地址
 
    //2.2 绑定工作
    if(bind(rfd, (struct sockaddr*)&rin, sizeof(rin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");
    
    //3、接受数据
    char rbuf[128] = "";
    while(1)
    {
        //清空容器
        bzero(rbuf, sizeof(rbuf));
 
        //接受广播消息
        recv(rfd, rbuf, sizeof(rbuf), 0);
 
        printf("收到广播消息为:%s\n", rbuf);
    }
    
 
    //4、关闭套接字
    close(rfd);
 
    return 0;
}
 
 


四、组播


1>    组播也是实现一对多的通信方式,对于广播而言,网络需要对每个消息进行复制转发,会占用大量的带宽,导致网络拥塞
2>    组播可以实现小范围的数据传播:将需要接收数据的接收端加入多播组,发送端向多播组中发送消息,每个组内成员都能接收到消息
3>    需要对接收端进行设置,将接收端加入多播组

1、需要使用setsockopt函数实现
2、需要对网络层设置:IPPROTO_IP
3、需要对加入多播组属性设置:IP_ADD_MEMBERSHIP
4、属性值的类型
           struct ip_mreqn {
               struct in_addr imr_multiaddr; /* 组播地址:D类网络(224.0.0.0 --- 239.255.255.255) */
               struct in_addr imr_address;   /* 当前主机IP地址 */
               int            imr_ifindex;   /* 网卡编号: 通过指令 ip ad查看 */
           };
 
4>    组播也是使用UDP实现的,无论接收端是否愿意接收数据,发送端都可以正常向多播组中发送数据
5>    组播也分为发送端和接收端流程
6>    组播发送端流程    ----->   类似于UDP的客户端流程

1、socket();          //创建用于通信的套接字文件描述符
2、bind();             //可选,可以绑定也可以不绑定
3、sendto();               //向多播组地址发送消息
4、close();                //关闭套接字
 

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
    //1、socket();          //创建用于通信的套接字文件描述符
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sfd == -1)
    {
        perror("socket error");
        return -1;
    }
 
 
    //2、bind();             //可选,可以绑定也可以不绑定
    
    //3、sendto();               //向多播组地址发送消息
    struct sockaddr_in min;
    min.sin_family = AF_INET;
    min.sin_port = htons(9999);
    min.sin_addr.s_addr = inet_addr("224.1.2.3");
 
    char wbuf[128] = "";
    while(1)
    {
        printf("请输入>>>>");
        fgets(wbuf, sizeof(wbuf), stdin);
        wbuf[strlen(wbuf)-1] = 0;
 
        sendto(sfd, wbuf, strlen(wbuf), 0, (struct sockaddr*)&min, sizeof(min));
        printf("发送成功\n");
    }
 
    //4、close();                //关闭套接字
    close(sfd);
 
    return 0;
}
 


组播的接收端流程    ------> 类似于UDP的服务器端流程


1、socket();          //创建用于通信的套接字文件描述符
2、bind();             //必须进行,但是,绑定的组播地址和端口号
3、setsockopt();          //加入多播组
4、recv();               //向广播地址发送消息
5、close();                //关闭套接字

#include<myhead.h>
 
int main(int argc, const char *argv[])
{
    //1、socket();          //创建用于通信的套接字文件描述符
    int rfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(rfd == -1)
    {
        perror("socket error");
        return -1;
    }
 
    //3、setsockopt();          //加入多播组
    //准备要设置的数据
    struct ip_mreqn imr;        
    imr.imr_multiaddr.s_addr = inet_addr("224.1.2.3");   //组播ip
    imr.imr_address.s_addr = inet_addr("192.168.0.143");   //主机ip
    imr.imr_ifindex = 2;                        //网卡编号
    if(setsockopt(rfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) == -1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("加入多播组成功\n");
 
 
    //2、bind();             //必须进行,但是,绑定的组播地址和端口号
    struct sockaddr_in rin;
    rin.sin_family = AF_INET;
    rin.sin_port = htons(9999);
    rin.sin_addr.s_addr = inet_addr("224.1.2.3");     //组播地址
    if(bind(rfd, (struct sockaddr*)&rin, sizeof(rin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");
 
 
    
    //4、recv();               //从组播地址中接受消息
    char rbuf[128] = "";
    while(1)
    {
        //清空容器
        bzero(rbuf, sizeof(rbuf));
 
        //接受广播消息
        recv(rfd, rbuf, sizeof(rbuf), 0);
 
        printf("收到组播消息为:%s\n", rbuf);
    }
 
    
    //5、close();                //关闭套接字
    close(rfd);
 
    return 0;
}

  通过w(红色臂角度增大)s(红色臂角度减小)d(蓝色臂角度增大)a(蓝色臂角度减小)按键控制机械臂

#include <myhead.h>
#include <termios.h>
#define SER_PORT 8888          //与服务器保持一致
#define SER_IP "192.168.0.109" //服务器ip地址
#define CLI_PORT 6666          //客户端端口号
#define CLI_IP "192.168.0.145" //客户端ip地址

int main(int argc, const char *argv[])
{
    //准备数据
    char rbuf[5] = {0xff, 0x02, 0x00, 0x00, 0xff};
    char bbuf[5] = {0xff, 0x02, 0x01, 0x20, 0xff};
    char a;
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if (cfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("cfd = %d\n", cfd); //3
    //3、连接到服务器
    //3.1 填充服务器地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;                //通信域
    sin.sin_port = htons(SER_PORT);          //服务器端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP); //服务器ip地址

    //3.2 连接服务器
    if (connect(cfd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
    {
        perror("connect error");
        return -1;
    }
    printf("连接服务器成功\n");
    printf("通过w(红色臂角度增大)\ns(红色臂角度减小\nd(蓝色臂角度增大\na(蓝色臂角度减小)按键控制机械臂\n");
    printf("请输入内容(按Ctrl+C退出):\n");
    struct termios old_tio, new_tio;
    // 获取当前终端属性
    tcgetattr(STDIN_FILENO, &old_tio);
    new_tio = old_tio;

    // 修改终端属性,使其不需要回车键即可读取字符
    new_tio.c_lflag &= (~ICANON & ~ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);

    while (1)
    {
        read(STDIN_FILENO, &a, 1);
        usleep(50000);
        switch (a)
        {
        case 'w':
        {
            rbuf[3]++;
            if(rbuf[3]>90)
            {
                rbuf[3]=0;
            }
            send(cfd, rbuf, sizeof(rbuf), 0);
            break;
        }
        case 's':
        {
            rbuf[3]--;
             if(rbuf[3]<-90)
            {
                rbuf[3]=0;
            }
            send(cfd, rbuf, sizeof(rbuf), 0);
            break;
        }
        case 'd':
        {
            bbuf[3]++;
             if(bbuf[3]>180)
            {
                rbuf[3]=90;
            }
            send(cfd, bbuf, sizeof(bbuf), 0);
            break;
        }
        case 'a':
        {
            bbuf[3]--;
             if(bbuf[3]<0)
            {
                bbuf[3]=90;
            }
            send(cfd, bbuf, sizeof(bbuf), 0);
            break;
        }
        default:
            break;
        }
        if (a == '\x03')//ctrl +c退出
        {
            break;
        }
    }
    //5、关闭套接字
    close(cfd);
    tcsetattr(STDIN_FILENO, TCSANOW, &old_tio);//还原终端属性
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值