UDP SOCKET最全解析 ——linux 系统搭建

本文详细讲解了在Linux环境下,如何使用UDP协议进行客户端与服务器之间的通信。从创建套接字、绑定地址到收发数据,文章深入浅出地介绍了每个步骤的实现方法及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先先说一下我搭建的配置环境,我是在win10系统的linux 虚拟机的环境下搭建的,在vim界面中进行编程

…省略一系列虚拟机环境以及vim尝试的过程开始正式的讲解:

UDP适合小数据的传输
先看一下整体的流程图:
在这里插入图片描述
其实UPD的搭建就是函数调用,函数是包里已经弄好的函数,我们只需要自己配置一下端口和网络地址均可;
现在开始客户端和接收端函数的讲述:
接收端:

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol);

套字节创建所需要的头文件,socket()函数的返回值为int类型,内有四个参数位置;

server_fd=socket(AF_INET, SOCK_DGRAM, 0);

这是第一个函数,创造套字节的函数,其中server_fd是我们自己定义的一个int类型,也可以根据的返回值来盘算创建套字节是否成功,其实很多出问题的宝贝这步还是不会有问题的,因为这句话就直接粘贴复制就好了,这不会是伪代码,这就是源码。解释一下:socket()中
第一个参数是协议族,是个地址类型,目前仅支持AF_INET格式。
第二个参数指定socket类型,新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)还有其他。
第三个是就是指定协议。套接口所用的协议。如调用者不想指定,可用0。
到此一个函数创建完成,朋友们可以打印出server_fd的值来判断是否创建成功,我的返回值是3,我觉得如果是-1或者0时候就是创建失败了,因为server_fd初始化就是0;

  server_fd=socket(AF_INET, SOCK_DGRAM, 0);
  if(server_fd<0)
     std::cout<<"create socket fail!"<<endl;
  memset(&ser_addr, 0, sizeof(ser_addr));
  ser_addr.sin_family = AF_INET;
  ser_addr.sin_addr.s_addr = htonl(INADDR_ANY); //IP地址,需要进行网络序转换,INADDR_ANY:本地地址
  ser_addr.sin_port = htons(SERVER_PORT);  //端口号,需要网络序转换

menmset函数与bzero函数都可以实现清空初始化的功能,这里用的memset函数是将&ser_add地址的前sizeof(ser_addr)个字节置零。下面是对结构体的成员进行赋值,对于htonl与htons是将本地地址与网络序地址进项转换的过程,其中网络地址是就是按照网络字节顺序储存的,它的特点是将地址先转换成二进制,然后对其高位到低位储存(好像是低位存在高位地址,高位存低位地址),转换过如果打印的话应该是和INADDR_ANY不同的,INADDR_ANY这个代码其实是指电脑自己获取可以接受的ip地址,一般就是为0,代表可以接受所有地址发过来的消息。言归正传,为什么要将地址转换成网络地址呢,因为本机地址受不同的cpu影响,不同的CPU的本地地址储存顺序与CPU的型号有关,为了更好的通信统一化,所以大家一致采用网络地址。注意,SERVER_PORT端口号,并不是随意定义的,不是你自己随意想一个数字,是有约束的,具体你要查看自己电脑和支持UDP传输端口进行设置,千万不要随意想一个数字,不然会导致下面绑定失败。

下面开始第二个函数:bind() 将套字节与本机的地址进行绑定,很多宝贝在这里就开始报错了。

#include <sys/types.h>
#include <sys/socket.h>
/****
*  sockfd:标识一未捆绑套接口的描述字。
*  my_addr:赋予套接口的地址。sockaddr结构定义如下:
*  struct sockaddr{
*    u_short sa_family;
*    char sa_data[14];
*  };
*  addrlen:name名字的长度。
*  返回值:成功返回0,失败返回-1.
****/
int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen);
ret=bind(server_fd,(struct sockaddr*)&ser_addr, sizeof(sockaddr));

如代码可见,第一个参数就是创建套字节函数的返回值,第二个参数是一个地址,具体是指什么我没有查,其中前半部分是固定的,ser_addr 是定义的一个结构体,&ser_addr是取结构体的地址。第三个参数是sockaddr这个结构体的长度。

struct sockaddr_in ser_addr;

接下来是第三个函数recvform()函数:

 count = recvfrom(serve_fd, buf, BUFF_LEN, 0, (struct sockaddr*)&clent_addr, &len);

这是里面的初始化变量信息:

char buf[BUFF_LEN];  //接收缓冲区,1024字节
     socklen_t len;
     int count;
     struct sockaddr_in clent_addr;  //clent_addr用于记录发送方的地址信息
     memset(buf, 0, BUFF_LEN);
     len = sizeof(clent_addr);

我们用一个字符数组作为信息接收的变量,socklen_t其实可以理解成 int , 0处的位置是一个 flag,(struct sockaddr*)&clent_addr, &len)一个是结构体指针,一个是结构体长度指针。

其实客户端和接收端是一致的,在这里不再做重复的介绍了。

server:

#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <fstream>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#define SERVER_PORT 8908
#define BUFF_LEN 100
#include <unistd.h>
using namespace std;

void handle_udp_msg(int fd)

{
     char buf[BUFF_LEN];  //接收缓冲区
     char sendline[BUFF_LEN];
     socklen_t len;
     int count;
     struct sockaddr_in clent_addr;  //clent_addr用于记录发送方的地址信息
     while(1)
     {
         memset(buf, 0, BUFF_LEN);
         len = sizeof(clent_addr);
         count = recvfrom(fd, buf, BUFF_LEN, 0, (struct sockaddr*)&clent_addr, &len);
      //recvfrom是拥塞函数,没有数据就一直拥塞
         std::cout<<count<<endl;
         if(count == -1)
         {
             printf("recieve data fail!\n");
             return;
         }
     switch(buf[0]){
         case '1' :system("./1");
                     break;
         case '2' :system("./2");
                  break;
         default: std::cout<<"the message is not our permission"<<endl;
         }
         printf("client:%s\n",buf);  //打印client发过来的信息
         memset(buf, 0, BUFF_LEN);
         sprintf(buf, "I have recieved %d bytes data!\n", count);  //回复client
         printf("server:%s\n",buf);  //打印自己发送的信息给
         sendto(fd, buf, BUFF_LEN, 0, (struct sockaddr*)&clent_addr, len);  //发
//送信息给client,注意使用了clent_addr结构体指针
     }
 }
int main(){
    int server_fd;
    int ret;
    struct sockaddr_in ser_addr;
    server_fd=socket(AF_INET, SOCK_DGRAM, 0);
    if(server_fd<0)
          std::cout<<"create socket fail!"<<endl;
    memset(&ser_addr, 0, sizeof(ser_addr));
     ser_addr.sin_family = AF_INET;
  ser_addr.sin_addr.s_addr = htonl(INADDR_ANY); //IP地址,需要进行网络序转换,INADDR_ANY:本地地址
     ser_addr.sin_port = htons(SERVER_PORT);  //端口号,需要网络序转换
     ret=bind(server_fd,(struct sockaddr*)&ser_addr, sizeof(sockaddr));
     //std::cout<<ret<<endl;
     if(ret!=0)
        std::cout<<"blid failed,please cheak the UPD port"<<endl;
     handle_udp_msg(server_fd);   //处理接收到的数据

     close(server_fd);
     return 0;
}

作用介绍:通过接收信息来调用exe1或者exe2;

client端:

#include <arpa/inet.h>
#include <stdio.h>
#include <string>
#include <fstream>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#define SERVER_PORT 8908
#define BUFF_LEN 100
#include <unistd.h>
using namespace std;
//************************************************
//This is the client code, the port and ip address need to match the test computer and the r**************************************
int main(){
    int Client_fd;
    int ret;
    char buf[BUFF_LEN]="1";
    char ip[20];
    struct sockaddr_in ser_addr;
    unsigned int addr_length;
    Client_fd=socket(AF_INET, SOCK_DGRAM, 0);
    if(Client_fd<0)
     std::cout<<"create socket fail!"<<endl;
       // 以读模式打开文件
    ifstream infile;
    infile.open("ipaddress.dat");
    infile>>ip;
    //     std::cout<<Client_fd<<endl;
    // std::cout<<ip<<endl;
    infile.close();
    bzero(&ser_addr, sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(SERVER_PORT);  //端口号,需要网络序转换
    ser_addr.sin_addr.s_addr = inet_addr(ip);
 //IP地址,需要进行网络序转换,INADDR_ANY:本>地地址
    addr_length=sizeof(sockaddr);
    ret = sendto(Client_fd, buf, BUFF_LEN, 0, (struct sockaddr*)&ser_addr, sizeof(sockaddr));
 // std::cout<<addr_length<<endl;
 // std::cout<<ret<<endl;
  if (ret < 0)
  {
    std::cout<<"fail to send"<<endl;
   }
  else   std::cout<<"send successfully"<<endl;
  // close(server_fd);
  close(Client_fd);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值