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


    目录

    TCP与UDP

    C/S模型UDP 

    广播

    组播 

    setsockopt作用


    TCP与UDP

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

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

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

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

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

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

    1. #include
    2. int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t optlen);
    3. int n = 220x1024;
    4. setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&n,sizeof(n));

    C/S模型UDP 

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

    代码案例:
     

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #define MAXLINE 80
    9. #define SERV_PORT 8080
    10. int main(void)
    11. {
    12. int n,i;
    13. struct sockaddr_in servaddr,cliaddr;
    14. socklen_t cliaddr_len;
    15. int sockfd;
    16. char str[INET_ADDRSTRLEN];
    17. char buf[MAXLINE];
    18. sockfd = socket(AF_INET,SOCK_DGRAM,0);
    19. bzero(&servaddr,sizeof(servaddr));
    20. servaddr.sin_family = AF_INET;
    21. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    22. servaddr.sin_port = htons(SERV_PORT);
    23. bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
    24. while(1)
    25. {
    26. cliaddr_len = sizeof(cliaddr);
    27. n = recvfrom(sockfd,buf,MAXLINE,0,(struct sockaddr *)&cliaddr,&cliaddr_len);
    28. if(n == -1)
    29. {
    30. perror("recvfrom error");
    31. }
    32. printf("连接来自 %s 在端口 %d\n",
    33. inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));
    34. for(i=0;i
    35. {
    36. buf[i] = toupper(buf[i]);
    37. }
    38. n = sendto(sockfd,buf,n,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));
    39. if(n == -1)
    40. {
    41. perror("sendto error");
    42. }
    43. }
    44. close(sockfd);
    45. return 0;
    46. }
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #define MAXLINE 80
    8. #define SERV_PORT 8080
    9. int main(void)
    10. {
    11. int n;
    12. struct sockaddr_in servaddr;
    13. char str[INET_ADDRSTRLEN];
    14. char buf[MAXLINE];
    15. int sockfd;
    16. sockfd = socket(AF_INET,SOCK_DGRAM,0);
    17. bzero(&servaddr,sizeof(servaddr));
    18. servaddr.sin_family = AF_INET;
    19. servaddr.sin_port = htons(SERV_PORT);
    20. inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr);
    21. while(fgets(buf,MAXLINE,stdin) != NULL)
    22. {
    23. n = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&servaddr,sizeof(servaddr));
    24. if(n == -1)
    25. {
    26. perror("sendto error");
    27. }
    28. n = recvfrom(sockfd,buf,MAXLINE,0,NULL,0);
    29. if(n == -1)
    30. {
    31. perror("recvfrom error");
    32. }
    33. write(STDOUT_FILENO,buf,n);
    34. }
    35. close(sockfd);
    36. return 0;
    37. }

    广播

    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

    unsigned int if_nametoindex(const char *ifname);

    代码案例:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #define SERVER_PORT 8080
    9. #define CLIENT_PORT 9000
    10. #define MAXLINE 1500
    11. #define GROUP "239.0.0.2" //本地组播地址
    12. /*
    13. struct ip_mreqn
    14. {
    15. struct in_addr imr_multiaddr; 组播组的IP
    16. struct in_addr imr_address; 本地IP
    17. int imr_ifindex; 网卡编号
    18. };
    19. */
    20. int main(void)
    21. {
    22. int sockfd,i;
    23. struct sockaddr_in serveraddr,clientaddr;
    24. struct ip_mreqn group;
    25. char buf[MAXLINE] = "itcast\n";
    26. sockfd = socket(AF_INET,SOCK_DGRAM,0);
    27. bzero(&serveraddr,sizeof(serveraddr));
    28. serveraddr.sin_family = AF_INET;
    29. serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    30. serveraddr.sin_port = htons(SERVER_PORT);
    31. bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
    32. //设置组地址
    33. inet_pton(AF_INET,GROUP,&group.imr_multiaddr);
    34. //本地任意 IP
    35. inet_pton(AF_INET,"0.0.0.0",&group.imr_address);
    36. //eth0
    37. group.imr_ifindex = if_nametoindex("eth0");
    38. setsockopt(sockfd,IPPROTO_IP,IP_MULTICAST_IF,&group,sizeof(group));
    39. //构造 client 地址和端口
    40. bzero(&clientaddr,sizeof(clientaddr));
    41. clientaddr.sin_family = AF_INET;
    42. inet_pton(AF_INET,GROUP,&clientaddr.sin_addr.s_addr);
    43. clientaddr.sin_port = htons(CLIENT_PORT);
    44. while(1)
    45. {
    46. sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&clientaddr,sizeof(clientaddr));
    47. sleep(1);
    48. }
    49. close(sockfd);
    50. return 0;
    51. }
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #define SERVER_PORT 8080
    10. #define MAXLINE 4096
    11. #define CLIENT_PORT 9000
    12. #define GROUP "239.0.0.2" //本地组播地址
    13. int main()
    14. {
    15. struct sockaddr_in serveraddr,localaddr;
    16. int confd;
    17. ssize_t len;
    18. char buf[MAXLINE];
    19. //定义组播结构体
    20. struct ip_mreqn group;
    21. confd = socket(AF_INET,SOCK_DGRAM,0);
    22. //初始化本地端地址
    23. bzero(&localaddr,sizeof(localaddr));
    24. localaddr.sin_family = AF_INET;
    25. inet_pton(AF_INET,"0.0.0.0",&localaddr.sin_addr.s_addr);
    26. localaddr.sin_port = htons(CLIENT_PORT);
    27. bind(confd,(struct sockaddr *)&localaddr,sizeof(localaddr));
    28. //设置组地址
    29. inet_pton(AF_INET,GROUP,&group.imr_multiaddr);
    30. //本地任意
    31. inet_pton(AF_INET,"0.0.0.0",&group.imr_address);
    32. //eth0
    33. group.imr_ifindex = if_nametoindex("eth0");
    34. setsockopt(confd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&group,sizeof(group));
    35. while(1)
    36. {
    37. len = recvfrom(confd,buf,sizeof(buf),0,NULL,0);
    38. write(STDERR_FILENO,buf,len);
    39. }
    40. close(confd);
    41. return 0;
    42. }

    setsockopt作用

    1.端口复用

    2.设置缓冲区大小

    3.开放广播权限

    4.开放组播权限

     

  • 相关阅读:
    689. 三个无重叠子数组的最大和(dp)
    cpu性能分析工具
    手机上玩 PC 游戏的开源项目「GitHub 热点速览」
    Python之第十章 IO及对象列化
    DNA脱氧核糖核酸修饰金属铂纳米颗粒PtNPS-DNA|科研试剂
    【计算机网络】UDP/TCP协议
    服务拆分(案例Demo)
    RSA公钥密码算法和Diffie-Hellman密钥交换
    golang中一种不常见的switch语句写法
    YYDS!由浅入深学习阿里JDK源码,已在阿里内部疯拿3个金奖
  • 原文地址:https://blog.csdn.net/weixin_46120107/article/details/126716494