Linux编程(通信协议---udp)

时间:2024-07-17 09:35:12

    UDP(用户数据报协议)是一种无连接的网络协议,主要用于快速传输数据。以下是UDP协议的一些主要特点:

1. **无连接**:UDP是无连接的协议,这意味着在数据传输之前不需要建立连接。每个UDP数据包都是独立的,不需要前一个数据包的确认。

2. **简单性**:UDP的协议结构相对简单,只有8字节的UDP头部,包括源端口、目的端口、长度和校验和。

3. **快速性**:由于UDP不需要建立连接和进行数据确认,因此它的数据传输速度通常比TCP快。

4. **不保证可靠性**:UDP不保证数据的可靠传输,也就是说,数据包可能会丢失、重复或乱序到达。

5. **无拥塞控制**:UDP没有拥塞控制机制,这意味着它不会根据网络的拥堵情况调整数据传输速率。

6. **支持多播和广播**:UDP支持多播和广播数据传输,这使得它可以用于发送数据到多个接收者。

7. **校验和**:UDP包含一个校验和字段,用于检测数据在传输过程中是否出现错误。如果检测到错误,接收方可以选择丢弃该数据包。

8. **应用场景**:UDP通常用于那些对实时性要求高但可以容忍一定数据丢失的应用,如视频会议、在线游戏、DNS查询等。

9. **端口号**:UDP使用端口号来区分不同的服务或进程,每个UDP数据包都包含源端口和目的端口。

10. **数据报长度**:UDP数据报的长度可以是任意的,但最大长度受限于IP数据报的最大长度,通常是65507字节。
 


2、框架: C/S模式 

  服务器端  server:socket() ===>bind()===>recvfrom()===>close()
   客户端  client:socket() ===>bind()===>sendto() ===>close()


注意:socket()的参数需要调整。

      socket(PF_INET,SOCK_DGRAM,0);

  bind() 客户端是可选的,服务器端是必须选的。

1、int socket(int domain, int type, int protocol);
功能:程序向内核提出创建一个基于内存的套接字描述符

参数:domain  地址族,PF_INET == AF_INET ==>互联网程序
  PF_UNIX == AF_UNIX ==>单机程序
  type    套接字类型:
     SOCK_STREAM  流式套接字 ===》TCP   
  SOCK_DGRAM   用户数据报套接字===>UDP
  SOCK_RAW     原始套接字  ===》IP
  protocol 协议 ==》0 表示自动适应应用层协议。

返回值:成功 返回申请的套接字id
失败  -1;

2、int bind(int sockfd, struct sockaddr *my_addr, 
             socklen_t addrlen);
功能:如果该函数在服务器端调用,则表示将参数1相关
  的文件描述符文件与参数2 指定的接口地址关联,
  用于从该接口接受数据。

  如果该函数在客户端调用,则表示要将数据从
  参数1所在的描述符中取出并从参数2所在的接口
  设备上发送出去。

  注意:如果是客户端,则该函数可以省略,由默认
        接口发送数据。
参数:sockfd 之前通过socket函数创建的文件描述符,套接字id
  my_addr 是物理接口的结构体指针。表示该接口的信息。

  struct sockaddr      通用地址结构
  {
  u_short sa_family;  地址族
  char sa_data[14];   地址信息
  };

  转换成网络地址结构如下:
  struct _sockaddr_in    ///网络地址结构
  {
  u_short    sin_family; 地址族
  u_short    sin_port;   ///地址端口
  struct in_addr  sin_addr;   ///地址IP
  char    sin_zero[8]; 占位
  };

  struct in_addr
  {
  in_addr_t s_addr;
  }

  socklen_t addrlen: 参数2 的长度。
返回值:成功 0
失败  -1;

发送接收函数:
3、ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
         const struct sockaddr *dest_addr, socklen_t addrlen);

功能:用于UDP协议中向对方发送数据。
参数:sockfd  本地的套接字id
  buff    本地的数据存储,一般是要发送的数据。
  len     要发送的数据长度
  flags   要发送数据方式,0 表示阻塞发送。

  dest_addr: 必选,表示要发送到的目标主机信息结构体。
  addrlen :目标地址长度。

返回值:成功  发送的数据长度
    失败   -1;


4、ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                  struct sockaddr *src_addr, socklen_t *addrlen);

功能:用于UDP协议中获取对方发送的数据。
参数:sockfd 本地的套接字id
  buff   要存储数据的内存区,一般是数组或者动态内存。
  len    要获取的数据长度,一般是buff的大小。
  flags  获取方式,0 阻塞

  src_addr 可选,表示对方的地址信息结构体,
   如果为NULL,表示不关心对方地址。
  addrlen  对方地址信息结构体大小。
   如果对方地址是NULL,则该值也为NULL。
返回值:成功 接收到的数据长度
失败  -1;

《1》实现udp通信示例代码:

服务器端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);
int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }

    // man 7 ip 
    struct sockaddr_in ser,cli;
    bzero(&ser,sizeof(ser));
    bzero(&cli,sizeof(cli));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);
    ser.sin_addr.s_addr = inet_addr("192.168.203.128");
    int ret = bind(sockfd,(SA)&ser,sizeof(ser));
    if(-1 == ret)
    {
        perror("bind");
        exit(1);
    }
    socklen_t len = sizeof(cli);
    while(1)
    {
        char buf[512]={0};
        recvfrom(sockfd,buf,sizeof(buf),0,(SA)&cli,&len);
        time_t tm;
        time(&tm);
        sprintf(buf,"%s %s",buf,ctime(&tm));
        sendto(sockfd,buf,strlen(buf),0,(SA)&cli,len);
    }
    close(sockfd);
    return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);


int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }
    struct sockaddr_in ser;
    bzero(&ser,sizeof(ser));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);
    ser.sin_addr.s_addr = inet_addr("192.168.203.128");

    while(1)
    {
        char buf[512]="hello,this is udp test";
        sendto(sockfd,buf,strlen(buf),0,(SA)&ser,sizeof(ser));
        bzero(buf,sizeof(buf));
        recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
        printf("buf is %s\n",buf);
        sleep(1);
    }
    close(sockfd);
    return 0;
}

《2》udp实现文件传输

服务器端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
#include <fcntl.h>
typedef struct sockaddr * (SA);
int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }

    // man 7 ip 
    struct sockaddr_in ser,cli;
    bzero(&ser,sizeof(ser));
    bzero(&cli,sizeof(cli));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);
    //ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    ser.sin_addr.s_addr = INADDR_ANY;
    int ret = bind(sockfd,(SA)&ser,sizeof(ser));
    if(-1 == ret)
    {
        perror("bind");
        exit(1);
    }
    socklen_t len = sizeof(cli);
    int fd = open("2.png",O_WRONLY|O_CREAT|O_TRUNC,0666);
    if(-1 == fd)
    {
        perror("open");
        exit(1);
    }
    while(1)
    {
        char buf[512]={0};
        int rd_ret = recvfrom(sockfd,buf,sizeof(buf),0,(SA)&cli,&len);
        if(0 == strcmp(buf,"^_^"))
        {
            break;
        }
        write(fd,buf,rd_ret);
        bzero(buf,sizeof(buf));
        strcpy(buf,"go on");
        sendto(sockfd,buf,strlen(buf),0,(SA)&cli,len);
    }
    close(sockfd);
    close(fd);
    return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
#include <fcntl.h>
typedef struct sockaddr * (SA);

int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }
    struct sockaddr_in ser;
    bzero(&ser,sizeof(ser));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);
    ser.sin_addr.s_addr = inet_addr("192.168.203.128");
    int fd = open("/home/linux/1.png",O_RDONLY);
    if(-1 == fd)
    {
        perror("open");
        exit(1);
    }
    char buf[512]={0};
    while(1)
    {
        bzero(buf,sizeof(buf));
        int rd_ret = read(fd,buf,sizeof(buf));
        if(0==rd_ret)
        {
            break;
        }
        sendto(sockfd,buf,rd_ret,0,(SA)&ser,sizeof(ser));
        bzero(buf,sizeof(buf));
        recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
    }
    
    bzero(buf,sizeof(buf));
    strcpy(buf,"^_^");
    sendto(sockfd,buf,3,0,(SA)&ser,sizeof(ser));
    close(sockfd);
    close(fd);
    return 0;
}

《3》udp实现小聊天室功能

服务器端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);
typedef enum {CMD_LOGIN,CMD_CHAT,CMD_LOGOUT}TYPE;
typedef struct 
{
    TYPE type;
    char name[50];
    char context[128];

}MSG;
typedef struct 
{
    struct sockaddr_in cli;
    int flag; // 0  free 1 occu
}LIST;
#define MAX 10
LIST list[MAX]={0};
int do_login(int sockfd,MSG* msg,struct sockaddr_in* cli)
{
    int i = 0 ;
    for(i=0;i<MAX;i++)
    {
        if(1 == list[i].flag )
        {
            sendto(sockfd,msg,sizeof(MSG),0,(SA)&list[i].cli,sizeof(list[i].cli));
        }
    }
    for(i=0;i<MAX;i++)
    {
        if(0 == list[i].flag )
        {
            list[i].flag =1;
            //list[i].cli = *cli;
            memcpy(&list[i].cli,cli,sizeof(*cli));
            break;
        }
    }
    return 0;
}

int do_chat(int sockfd, MSG* msg,struct sockaddr_in*cli)
{
     int i = 0 ;
    for(i=0;i<MAX;i++)
    {
        if(1 == list[i].flag && 0!=memcmp(&list[i].cli,cli,sizeof(*cli)) )
        {
            sendto(sockfd,msg,sizeof(MSG),0,(SA)&list[i].cli,sizeof(list[i].cli));
        }
    }
}
int do_logout(int sockfd, MSG* msg,struct sockaddr_in*cli)
{
    return 0;
}
int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }

    // man 7 ip 
    struct sockaddr_in ser,cli;
    bzero(&ser,sizeof(ser));
    bzero(&cli,sizeof(cli));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);
    ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    int ret = bind(sockfd,(SA)&ser,sizeof(ser));
    if(-1 == ret)
    {
        perror("bind");
        exit(1);
    }
    socklen_t len = sizeof(cli);
    MSG msg;
    while(1)
    {
        bzero(&msg,sizeof(msg));
        recvfrom(sockfd,&msg,sizeof(msg),0,(SA)&cli,&len);
        switch(msg.type)
        {
            case CMD_LOGIN:
                do_login(sockfd,&msg,&cli);
                break;
            case CMD_LOGOUT:
                do_logout(sockfd,&msg,&cli);
                break;
            case CMD_CHAT:
                do_chat(sockfd,&msg,&cli);
                break;
        
        }
    }
    close(sockfd);
    return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);
typedef enum {CMD_LOGIN,CMD_CHAT,CMD_LOGOUT}TYPE;
typedef struct 
{
    TYPE type;
    char name[50];
    char context[128];

}MSG;
int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }

    // man 7 ip 
    struct sockaddr_in ser,cli;
    bzero(&ser,sizeof(ser));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);
    ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    socklen_t len = sizeof(cli);
    MSG msg;
    char name[50]={0};
    printf("input name:");
    fgets(name,sizeof(name),stdin);
    name[strlen(name)-1]='\0';

    msg.type = CMD_LOGIN;
    strcpy(msg.name ,name);
    strcpy(msg.context,"login");

    sendto(sockfd,&msg,sizeof(msg),0,(SA)&ser,sizeof(ser));

    pid_t pid = fork();
    if(pid>0)
    {
        while(1)
        {
            bzero(&msg,sizeof(msg));
            recvfrom(sockfd,&msg,sizeof(msg),0,NULL,NULL);    
            printf("%s:%s\n",msg.name,msg.context);
        }
    }
    else if(0==pid)
    {
        while(1)
        {
            printf("to all");    
            char buf[128]={0};
            strcpy(msg.name,name);
            msg.type = CMD_CHAT;
            fgets(msg.context,sizeof(msg.context),stdin);//#quit
            msg.context[strlen(msg.context)-1]='\0';
            if(0==strcmp(msg.context,"#quit"))
            {
                msg.type = CMD_LOGOUT;
                strcpy(msg.context,"CMD_LOGOUT");
            }
            sendto(sockfd,&msg,sizeof(msg),0,(SA)&ser,sizeof(ser));
        }
    }
    else 
    {
        perror("fork");
        exit(1);
    }
    close(sockfd);
    return 0;
}