Linux网络编程 ——UDP 通信

news/2024/5/18 14:01:07 标签: 网络, linux, udp

Linux网络编程 ——UDP 通信

    • 1. UDP
      • 1.1 UDP 通信
      • 1.2 广播
      • 1.3 组播(多播)
    • 2. 本地套接字

1. UDP

1.1 UDP 通信

在这里插入图片描述

输入 man 2 sendto 查看说明文档

在这里插入图片描述

#include <sys/types.h>
#include <sys/socket.h>

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
	- 参数:
		- sockfd : 通信的fd
		- buf : 要发送的数据
		- len : 发送数据的长度
		- flags : 0
		- dest_addr : 通信的另外一端的地址信息
		- addrlen : 前面地址的内存大小
	- 返回值:
		- 成功:字节个数
		- 失败:-1

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
	- 参数:
		- sockfd : 通信的fd
		- buf : 接收数据的数组
		- len : 数组的大小
		- flags : 0
		- src_addr : 用来保存另外一端的地址信息,不需要可以指定为NULL
		- addrlen : 前面地址的内存大小
	- 返回值:
		- 成功:字节个数
		- 失败:-1

代码实现
(1)服务器端 udp_server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main() {

    // 1.创建一个通信的socket
    int fd = socket(PF_INET, SOCK_DGRAM, 0);
    
    if(fd == -1) {
        perror("socket");
        exit(-1);
    }   

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);
    addr.sin_addr.s_addr = INADDR_ANY;

    // 2.绑定
    int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 3.通信
    while(1) {
        char recvbuf[128];
        char ipbuf[16];

        struct sockaddr_in cliaddr;
        int len = sizeof(cliaddr);

        // 接收数据
        int num = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&cliaddr, &len);
        // (判断)

        printf("client IP : %s, Port : %d\n", 
            inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),
            ntohs(cliaddr.sin_port));

        printf("client say : %s\n", recvbuf);

        // 发送数据
        sendto(fd, recvbuf, strlen(recvbuf) + 1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));

    }

    close(fd);
    return 0;
}

(2)客户端 udp_client.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main() {

    // 1.创建一个通信的socket
    int fd = socket(PF_INET, SOCK_DGRAM, 0);
    
    if(fd == -1) {
        perror("socket");
        exit(-1);
    }   

    // 服务器的地址信息
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr.s_addr);

    int num = 0;
    // 3.通信
    while(1) {

        // 发送数据
        char sendBuf[128];
        sprintf(sendBuf, "hello , i am client %d \n", num++);
        sendto(fd, sendBuf, strlen(sendBuf) + 1, 0, (struct sockaddr *)&saddr, sizeof(saddr));

        // 接收数据
        int num = recvfrom(fd, sendBuf, sizeof(sendBuf), 0, NULL, NULL);
        printf("server say : %s\n", sendBuf);

        sleep(1);
    }

    close(fd);
    return 0;
}
  • 运行结果:
    在这里插入图片描述

1.2 广播

    向子网中多台计算机发送消息,并且子网中所有的计算机都可以接收到发送方发送的消息,每个广播消息都包含一个 特殊的IP地址,这个IP中子网内 主机标志部分的二进制 全部1

  1. 只能局域网 中使用。
  2. 客户端 需要 绑定 服务器广播使用的 端口,才可以接收到广播消息。

在这里插入图片描述

// 设置广播属性的函数
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
	- sockfd : 文件描述符
	- level : 级别,SOL_SOCKET
	- optname : SO_BROADCAST
	- optval : int类型的值,为1表示允许广播
	- optlen : optval的大小

代码实现
(1)服务器端 bro_server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main() {

    // 1.创建一个通信的socket
    int fd = socket(PF_INET, SOCK_DGRAM, 0);
    if(fd == -1) {
        perror("socket");
        exit(-1);
    }   

    // 2.设置广播属性
    int op = 1;
    setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &op, sizeof(op));
    
    // 3.创建一个广播的地址
    struct sockaddr_in cliaddr;
    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(9999);
    inet_pton(AF_INET, "192.168.216.255", &cliaddr.sin_addr.s_addr);

    // 3.通信
    int num = 0;
    while(1) {
       
        char sendBuf[128];
        sprintf(sendBuf, "hello, client....%d\n", num++);
        // 发送数据
        sendto(fd, sendBuf, strlen(sendBuf) + 1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
        printf("广播的数据:%s\n", sendBuf);
        sleep(1);
    }

    close(fd);
    return 0;
}

(2)客户端 bro_client.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main() {

    // 1.创建一个通信的socket
    int fd = socket(PF_INET, SOCK_DGRAM, 0);
    if(fd == -1) {
        perror("socket");
        exit(-1);
    }   

    struct in_addr in;

    // 2.客户端绑定本地的IP和端口
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);
    addr.sin_addr.s_addr = INADDR_ANY;

    int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 3.通信
    while(1) {
        
        char buf[128];
        // 接收数据
        int num = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
        printf("server say : %s\n", buf);

    }

    close(fd);
    return 0;
}
  • 运行结果:
    在这里插入图片描述
  • 产生丢包,客户端只能接收部分数据。

注意:

  • 同一个主机不能使用相同的端口号,一个端口号只能标识一个进程!
  • 测试的话可以克隆一个主机,设置其 ip 在同一个网段(局域网)。

1.3 组播(多播)

    单播地址 标识 单个 IP 接口广播地址 标识 某个子网的 所有 IP 接口多播地址 标识 一组 IP 接口单播广播 是寻址方案的两个极端(要么单个要么全部),多播则意在两者之间提供一种 折中方案。多播数据报只应该由对它感兴趣的接口接收,也就是说 由运行 相应多播会话应用系统的主机上的接口 接收。另外,广播 一般局限于 局域网 内使用,而 多播 则既可以用于 局域网,也可以 跨广域网(因特网) 使用。

  1. 组播 既可以用于局域网,也可以用于广域网
  2. 客户端 需要加入 多播组,才能接收到多播的数据

在这里插入图片描述
组播地址

  • IP 多播通信必须依赖于 IP 多播地址,在 IPv4 中它的范围从 224.0.0.0239.255.255.255 ,并被划分为 局部链接多播地址预留多播地址管理权限多播地址三类:

在这里插入图片描述

  • 设置组播
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
	// 服务器设置多播的信息,外出接口
	- level : IPPROTO_IP
	- optname : IP_MULTICAST_IF
	- optval : struct in_addr
	
	// 客户端加入到多播组:
	- level : IPPROTO_IP
	- optname : IP_ADD_MEMBERSHIP
	- optval : struct ip_mreq

struct ip_mreq
{
	/* IP multicast address of group. */
	struct in_addr imr_multiaddr; // 组播的IP地址
	
	/* Local IP address of interface. */
	struct in_addr imr_interface; // 本地的IP地址
};

typedef uint32_t in_addr_t;

struct in_addr
{
	in_addr_t s_addr;
};

代码实现
(1)服务器端 multi_server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main() {

    // 1.创建一个通信的socket
    int fd = socket(PF_INET, SOCK_DGRAM, 0);
    if(fd == -1) {
        perror("socket");
        exit(-1);
    }   

    // 2.设置多播的属性,设置外出接口
    struct in_addr imr_multiaddr;
    // 初始化多播地址
    inet_pton(AF_INET, "239.0.0.10", &imr_multiaddr.s_addr);
    setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &imr_multiaddr, sizeof(imr_multiaddr));
    
    // 3.初始化客户端的地址信息
    struct sockaddr_in cliaddr;
    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(9999);
    inet_pton(AF_INET, "239.0.0.10", &cliaddr.sin_addr.s_addr);

    // 3.通信
    int num = 0;
    while(1) {
       
        char sendBuf[128];
        sprintf(sendBuf, "hello, client....%d\n", num++);
        // 发送数据
        sendto(fd, sendBuf, strlen(sendBuf) + 1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
        printf("组播的数据:%s\n", sendBuf);
        sleep(1);
    }

    close(fd);
    return 0;
}

(2)客户端 multi_client.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main() {

    // 1.创建一个通信的socket
    int fd = socket(PF_INET, SOCK_DGRAM, 0);
    if(fd == -1) {
        perror("socket");
        exit(-1);
    }   

    struct in_addr in;
    // 2.客户端绑定本地的IP和端口
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);
    addr.sin_addr.s_addr = INADDR_ANY;

    int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    struct ip_mreq op;
    inet_pton(AF_INET, "239.0.0.10", &op.imr_multiaddr.s_addr);
    op.imr_interface.s_addr = INADDR_ANY;

    // 加入到多播组
    setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &op, sizeof(op));

    // 3.通信
    while(1) {
        
        char buf[128];
        // 接收数据
        int num = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
        printf("server say : %s\n", buf);

    }

    close(fd);
    return 0;
}
  • 运行结果:
    在这里插入图片描述

2. 本地套接字

本地套接字的作用:本地的 进程间通信

  • 有关系的进程间的通信
  • 没有关系的进程间的通信

本地套接字 实现流程和 网络套接字 类似,一般呢采用TCP的通信流程
在这里插入图片描述

// 本地套接字通信的流程 - tcp

// 服务器端
1. 创建监听的套接字
	int lfd = socket(AF_UNIX/AF_LOCAL, SOCK_STREAM, 0);
2. 监听的套接字绑定本地的套接字文件 -> server端
	struct sockaddr_un addr;
	// 绑定成功之后,指定的sun_path中的套接字文件会自动生成。
	bind(lfd, addr, len);
3. 监听
	listen(lfd, 100);
4. 等待并接受连接请求
	struct sockaddr_un cliaddr;
	int cfd = accept(lfd, &cliaddr, len);
5. 通信
	接收数据:read/recv
	发送数据:write/send
6. 关闭连接
	close();


// 客户端的流程
1. 创建通信的套接字
	int fd = socket(AF_UNIX/AF_LOCAL, SOCK_STREAM, 0);
2. 监听的套接字绑定本地的IP 端口
	struct sockaddr_un addr;
	// 绑定成功之后,指定的sun_path中的套接字文件会自动生成。
	bind(lfd, addr, len);
3. 连接服务器
	struct sockaddr_un serveraddr;
	connect(fd, &serveraddr, sizeof(serveraddr));
4. 通信
	接收数据:read/recv
	发送数据:write/send
5. 关闭连接
	close();
// 头文件: sys/un.h
#define UNIX_PATH_MAX 108

struct sockaddr_un {
	sa_family_t sun_family; // 地址族协议 af_local
	char sun_path[UNIX_PATH_MAX]; // 套接字文件的路径, 这是一个伪文件, 大小永远=0
};

代码实现
(1)服务器端 ipc_server.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/un.h>

int main() {

    unlink("server.sock");

    // 1.创建监听的套接字
    int lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if(lfd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.绑定本地套接字文件
    struct sockaddr_un addr;
    addr.sun_family = AF_LOCAL;
    strcpy(addr.sun_path, "server.sock");
    int ret = bind(lfd, (struct sockaddr *)&addr, sizeof(addr));
    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 3.监听
    ret = listen(lfd, 100);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }

    // 4.等待客户端连接
    struct sockaddr_un cliaddr;
    int len = sizeof(cliaddr);
    int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);
    if(cfd == -1) {
        perror("accept");
        exit(-1);
    }

    printf("client socket filename: %s\n", cliaddr.sun_path);

    // 5.通信
    while(1) {

        char buf[128];
        int len = recv(cfd, buf, sizeof(buf), 0);

        if(len == -1) {
            perror("recv");
            exit(-1);
        } else if(len == 0) {
            printf("client closed....\n");
            break;
        } else if(len > 0) {
            printf("client say : %s\n", buf);
            send(cfd, buf, len, 0);
        }

    }

    close(cfd);
    close(lfd);

    return 0;
}

(2)客户端 ipc_client.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/un.h>

int main() {

    unlink("client.sock");

    // 1.创建套接字
    int cfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if(cfd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.绑定本地套接字文件
    struct sockaddr_un addr;
    addr.sun_family = AF_LOCAL;
    strcpy(addr.sun_path, "client.sock");
    int ret = bind(cfd, (struct sockaddr *)&addr, sizeof(addr));
    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 3.连接服务器
    struct sockaddr_un seraddr;
    seraddr.sun_family = AF_LOCAL;
    strcpy(seraddr.sun_path, "server.sock");
    ret = connect(cfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if(ret == -1) {
        perror("connect");
        exit(-1);
    }

    // 4.通信
    int num = 0;
    while(1) {

        // 发送数据
        char buf[128];
        sprintf(buf, "hello, i am client %d\n", num++);
        send(cfd, buf, strlen(buf) + 1, 0);
        printf("client say : %s\n", buf);

        // 接收数据
        int len = recv(cfd, buf, sizeof(buf), 0);

        if(len == -1) {
            perror("recv");
            exit(-1);
        } else if(len == 0) {
            printf("server closed....\n");
            break;
        } else if(len > 0) {
            printf("server say : %s\n", buf);
        }

        sleep(1);

    }

    close(cfd);
    return 0;
}

注:仅供学习参考,如有不足,欢迎指正!


http://www.niftyadmin.cn/n/5407250.html

相关文章

linux kernel物理内存概述(三)

目录 物理内存空间划分 物理内存初始化 查看当前页面分配状态 页块 页面如何添加到伙伴系统中&#xff1f; 物理内存空间划分 32位系统 4GB 用户空间和内核空间划分 3&#xff1a;1 ARM64架构处理器虚拟地址空间划分方式&#xff1b; 内核中使用PAGE_OFFSET宏计算 内核线…

javaWebssh酒店客房管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh酒店客房管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0…

Elasticsearch:向量相似度计算 - 可笑的速度

作者&#xff1a;Chris Hegarty 任何向量数据库的核心都是距离函数&#xff0c;它确定两个向量的接近程度。 这些距离函数在索引和搜索期间执行多次。 当合并段或在图表中导航最近邻居时&#xff0c;大部分执行时间都花在比较向量的相似性上。 对这些距离函数进行微观优化是值…

uniapp 项目 浏览器chrome使用vue devtool 识别不了 in not detect

问题 uniapp的项目&#xff0c;vue2&#xff0c; chrome 分析 添加了运行时&#xff0c;指定模板h5.html 指定的h5.html重置了运行根目录&#xff0c;导致了vue dev tool在运行时&#xff0c;chrome上识别不了。 解决&#xff1a; 方法1&#xff1a; 只能调试的时候,不加sati…

华为OD机试 - 数字排列 - 深度优先搜索dfs算法(Java 2024 C卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述1、输入2、输出3、说明 四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&a…

改造muduo,不依赖boost,用C++11重构

组件的实现 1. 序 1.1. 总述 muduo库是基于多Reactor-多线程模型实现的TCP网络编程库&#xff0c;性能良好。如libev作者&#xff1a;“One loop per thread is usually a good model”&#xff0c;muduo库的作者陈硕在其《Linux多线程服务端编程》中也力荐这种“One loop pe…

如何压缩excel中图片大小?快速改变图片大小的方法

在日常工作和学习中&#xff0c;我们经常使用Excel表格来处理数据和信息&#xff0c;有时候&#xff0c;我们可能需要在Excel中插入图片以便更好地展示和说明数据。然而&#xff0c;随着图片的增加&#xff0c;Excel文件的大小也会相应增加&#xff0c;这可能会导致文件过大、加…

Java多线程导入Excel示例

在导入Excel的时候&#xff0c;如果文件比较大&#xff0c;行数很多&#xff0c;一行行读往往速度比较慢&#xff0c;为了加快导入速度&#xff0c;我们可以采用多线程的方式 话不多说直接上代码 首先是Controller import com.sakura.base.service.ExcelService; import com.s…