C++项目实战-UDP服务器

本文涉及的产品
数据传输服务 DTS,数据同步 small 3个月
推荐场景:
数据库上云
数据传输服务 DTS,数据迁移 small 3个月
推荐场景:
MySQL数据库上云
数据传输服务 DTS,数据同步 1个月
简介: C++项目实战-UDP服务器

TCP与UDP

       传输层主要应用的协议模型有两种,一种是TCP协议,另外一种则是UDP协议。TCP协议在网络通信中占主导地位,绝大多数的网络通信借助TCP协议完成数据传输。但UDP也是网络通信中不可或缺的重要通信手段。

       相较于TCP而言,UDP通信的形式更像是发短信。不需要在数据传输之前建立、维护连接。只专心获取数据就好。省去了三次握手的过程,通信速度可以大大提高,但与之伴随的通信的稳定性和正确率便得不到保证。因此,我们称UDP为“无连接的不可靠报文传递”。

       那么与我们熟知的TCP相比,UDP有哪些优点和不足呢?由于无需创建连接,所以UDP开销较小,数据传输速度快,实时性强。多用于对实时性要求较高的通信场合,如视频会议、电话会议等。但随之也伴随着数据传输不可靠,传输数据的正确率、传输顺序和流量都得不到控制和保证。所以,通常情况下,使用UDP协议进行数据传输,为保证数据的正确性,我们需要在应用层添加辅助校验协议来弥补UDP的不足,以达到数据可靠传输的目的。

       与TCP类似,UDP也有可能出现缓冲区被填满后,再接收数据是丢包的现象。由于它没有TCP滑动窗口的机制,通常采用如下两种方法:

       1)服务器应用层设计流量控制,控制发送数据速度

       2)借助setsockopt函数改变接收缓冲区大小

#include <sys/socket.h>
int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t optlen);
    int n = 220x1024;
    setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&n,sizeof(n));

C/S模型UDP

       由于UDP不需要维护连接,程序逻辑简单,但是UDP协议是不可靠的,保证通讯可靠性的机制需要在应用层实现。

代码案例:

 

#include <string.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>
#define MAXLINE     80
#define SERV_PORT   8080
int main(void)
{
    int n,i;
    struct sockaddr_in servaddr,cliaddr;
    socklen_t cliaddr_len;
    int sockfd;
    char str[INET_ADDRSTRLEN];
    char buf[MAXLINE];
    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);
    bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
    while(1)
    {
        cliaddr_len = sizeof(cliaddr);
        n = recvfrom(sockfd,buf,MAXLINE,0,(struct sockaddr *)&cliaddr,&cliaddr_len);
        if(n == -1)
        {
            perror("recvfrom error");
        }
        printf("连接来自 %s 在端口 %d\n",
                inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));
        for(i=0;i<n;++i)
        {
            buf[i] = toupper(buf[i]);
        }
        n = sendto(sockfd,buf,n,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));
        if(n == -1)
        {
            perror("sendto error");
        }
    }
    close(sockfd);
    return 0;
}
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <ctype.h>
#define MAXLINE     80
#define SERV_PORT   8080
int main(void)
{
    int n;
    struct sockaddr_in servaddr;
    char str[INET_ADDRSTRLEN];
    char buf[MAXLINE];
    int sockfd;
    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr);
    while(fgets(buf,MAXLINE,stdin) != NULL)
    {
        n = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&servaddr,sizeof(servaddr));
        if(n == -1)
        {
            perror("sendto error");
        }
        n = recvfrom(sockfd,buf,MAXLINE,0,NULL,0);
        if(n == -1)
        {
            perror("recvfrom error");
        }
        write(STDOUT_FILENO,buf,n);
    }
    close(sockfd);
    return 0;
}

广播

int setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&flag,sizeof(flag));  //修改sock属性

给sockfd开放广播权限

组播

       组播可以是永久的也可以是临时的。组播地址中,有一部分由官方分配,称为永久组播组。永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化。永久组播组中成员可以是任意的,甚至可以为0。那些没有保留下来供永久组播使用的ip组播地址,可以被临时组播组利用。

224.0.0.0 ~ 224.0.0.255  为预留的组播地址(永久地址),地址224.0.0.0保留不做分配,其它供路由协议使用

224.0.1.0 ~ 224.0.1.255  为公用组播地址,可以用于Internet,欲使用需申请

224.0.2.0 ~ 238.255.255.255 为用户可用的组播地址(临时地址,全网范围内有效)

239.0.0.0 ~ 239.255.255.255 为本地管理组播地址,仅在特定的本地范围内有效

可使用 ip ad命令查看网卡编号  ip adress

使用命令启动/关闭网卡 sudo ifconfig etho up/down

使用 if_nametoindex 函数命令可以根据网卡名,获取网卡序号

#include <net/if.h>

unsigned int if_nametoindex(const char *ifname);

代码案例:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <unistd.h>
#include <arpa/inet.h>
#define  SERVER_PORT  8080
#define  CLIENT_PORT  9000
#define  MAXLINE      1500
#define  GROUP        "239.0.0.2"   //本地组播地址
/*
struct ip_mreqn
{
    struct in_addr imr_multiaddr; 组播组的IP
    struct in_addr imr_address;   本地IP
    int imr_ifindex;          网卡编号
};
*/
int main(void)
{
    int sockfd,i;
    struct sockaddr_in  serveraddr,clientaddr;
    struct ip_mreqn     group;
    char   buf[MAXLINE]  = "itcast\n";
    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    bzero(&serveraddr,sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(SERVER_PORT);
    bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
    //设置组地址
    inet_pton(AF_INET,GROUP,&group.imr_multiaddr);
    //本地任意 IP
    inet_pton(AF_INET,"0.0.0.0",&group.imr_address);
    //eth0
    group.imr_ifindex = if_nametoindex("eth0");
    setsockopt(sockfd,IPPROTO_IP,IP_MULTICAST_IF,&group,sizeof(group));
    //构造 client 地址和端口
    bzero(&clientaddr,sizeof(clientaddr));
    clientaddr.sin_family = AF_INET;
    inet_pton(AF_INET,GROUP,&clientaddr.sin_addr.s_addr);
    clientaddr.sin_port = htons(CLIENT_PORT);
    while(1)
    {
        sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&clientaddr,sizeof(clientaddr));
        sleep(1);
    }
    close(sockfd);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#define SERVER_PORT     8080
#define MAXLINE         4096
#define CLIENT_PORT     9000
#define  GROUP        "239.0.0.2"   //本地组播地址
int main()
{
    struct sockaddr_in serveraddr,localaddr;
    int confd;
    ssize_t len;
    char buf[MAXLINE];
    //定义组播结构体
    struct ip_mreqn group;
    confd = socket(AF_INET,SOCK_DGRAM,0);
    //初始化本地端地址
    bzero(&localaddr,sizeof(localaddr));
    localaddr.sin_family = AF_INET;
    inet_pton(AF_INET,"0.0.0.0",&localaddr.sin_addr.s_addr);
    localaddr.sin_port = htons(CLIENT_PORT);
    bind(confd,(struct sockaddr *)&localaddr,sizeof(localaddr));
    //设置组地址
    inet_pton(AF_INET,GROUP,&group.imr_multiaddr);
    //本地任意
    inet_pton(AF_INET,"0.0.0.0",&group.imr_address);
    //eth0
    group.imr_ifindex = if_nametoindex("eth0");
    setsockopt(confd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&group,sizeof(group));
    while(1)
    {
        len = recvfrom(confd,buf,sizeof(buf),0,NULL,0);
        write(STDERR_FILENO,buf,len);
    }
    close(confd);
    return 0;
}

setsockopt作用

1.端口复用

2.设置缓冲区大小

3.开放广播权限

4.开放组播权限

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
Sqoop 企业级大数据迁移方案实战
Sqoop是一个用于在Hadoop和关系数据库服务器之间传输数据的工具。它用于从关系数据库(如MySQL,Oracle)导入数据到Hadoop HDFS,并从Hadoop文件系统导出到关系数据库。 本课程主要讲解了Sqoop的设计思想及原理、部署安装及配置、详细具体的使用方法技巧与实操案例、企业级任务管理等。结合日常工作实践,培养解决实际问题的能力。本课程由黑马程序员提供。
相关文章
|
1月前
|
网络协议 Linux 网络性能优化
Linux C/C++之TCP / UDP通信
这篇文章详细介绍了Linux下C/C++语言实现TCP和UDP通信的方法,包括网络基础、通信模型、编程示例以及TCP和UDP的优缺点比较。
37 0
Linux C/C++之TCP / UDP通信
|
1月前
|
存储 网络协议 Java
【网络】UDP回显服务器和客户端的构造,以及连接流程
【网络】UDP回显服务器和客户端的构造,以及连接流程
52 2
|
1月前
|
存储 网络协议 Java
【网络】UDP和TCP之间的差别和回显服务器
【网络】UDP和TCP之间的差别和回显服务器
65 1
|
1月前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
241 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
1月前
|
存储 监控 NoSQL
Redis的实现二: c、c++的网络通信编程技术,让服务器处理多个client
本文讨论了在C/C++中实现服务器处理多个客户端的技术,重点介绍了事件循环和非阻塞IO的概念,以及如何在Linux上使用epoll来高效地监控和管理多个文件描述符。
28 0
|
4月前
|
网络协议 网络架构
【网络编程入门】TCP与UDP通信实战:从零构建服务器与客户端对话(附简易源码,新手友好!)
在了解他们之前我们首先要知道网络模型,它分为两种,一种是OSI,一种是TCP/IP,当然他们的模型图是不同的,如下
199 1
|
5月前
|
网络协议
UDP服务器的并发方案
UDP服务器的并发方案
69 0
|
5天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
25 5
|
11天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
40 4
|
12天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
35 4
下一篇
无影云桌面