C语言UDP编程流程

C语言UDP编程流程

服务器:

​ 创建套接字socket()

​ 将服务器的ip地址、端口号与套接字进行绑定bind()

​ 接收数据recvfrom()

​ 发送数据sendto()

客户端:

​ 创建套接字socket()

​ 发送数据sendto()

​ 接收数据recvfrom()

​ 关闭套接字close()

一、创建UDP套接字

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
int main()
{
    //使用socket函数创建套接字
    //创建一个用于UDP网络编程的套接字

    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    printf("sockfd = %d\n",sockfd);

    return 0;
}

ipv4套接字地址结构

在网络编程中经常使用的结构体sockaddr_in:

struct in_addr
{
  in_addr_t s_addr;//IP 地址4字节
};
struct sockaddr_in
{
  sa_family_t sin_family;//协议族 2字节
  in_port_t sin_port;//端口号 2字节
  struct in_addr sin_addr;// IP地址 4字节
  char sin_zero[8]// 填充,不起什么作用 8字节
};

通用结构体 sockaddr

struct sockaddr
{
  sa_family_t sa_family; //2字节
  char sa_data[14] //14字节
}

在定义源地址时用struct sockaddt_in

struct sockaddr_in my_addr;

调用编程接口函数的时候需要强制类型转换为struct sockaddr类型

bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr));

二、发送数据——sendto函数

ssize_t sendto(int sockfd,const void*buf,seze_t nbytes,int flags,
               const struct sockaddr*to,socken_t addrlen);

功能

  • 向to结构体指针指定的ip,发送UDP数据

参数

  • sockfd: 套接字
  • buf: 发送数据缓冲区
  • nbytes:发送数据缓冲区大小
  • flags:一般为0
  • to:指向目的主机地址的结构体指针
  • addrlen:to所指向内容的长度

注意

  • 通过to和addrlen确定目的地址
  • 可以发送0长度的UDP数据包

返回值

  • 成功返回发送的字节数
  • 失败返回-1

sendto函数应用实例

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define N 128
int main(int argc,char *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr,"Usage: %s ip port\n",argv[0]);
        exit(1);
    }

    //第一步:使用socket函数创建套接字
    //创建一个用于UDP网络编程的套接字

    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    printf("sockfd = %d\n",sockfd);


    //第二部:填充服务器网络信息结构体 sockaddr_in
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);

    serveraddr.sin_family = AF_INET; //协议族,AF_INET:ipv4协议
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //IP地址
    serveraddr.sin_port = htons(atoi(argv[2]));
    //第三步:发送数据
    char buf[N] = "";
    while(1)
    {
        fgets(buf,N,stdin);
        buf[strlen(buf) - 1] = '\0';//把buf字符串中的\n转化为\0

        if(sendto(sockfd,buf,N,0,(struct sockaddr *)&serveraddr,addrlen) == -1)
        {
            perror("fail to sendto");
            exit(1);
        }
    }
    //第四步:关闭套接字文件描述符
    close(sockfd);

    return 0;
}

bind()函数的使用

int bind(int sockfd,const struct sockaddr *my_addr,socklen_t addrlen);

功能:

  • 将本地协议地址与sockfd绑定

参数

  • sockfd: 套接字
  • my_addr: 指向特定协议的地址结构指针
  • addrlen: 该地址结构的长度

返回值

  • 成功: 返回0
  • 失败: 其他

实例

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>

//bind示例,一般服务器都需要执行bind函数

int main(int argc,char* argv[])
{

    if(argc < 3)
    {
        fprintf(stderr,"Usage: %s ip port\n",argv[0]);
        exit(1);
    }
    //第一步:创建套接字
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) == -1)
    {
        perror("fail to socket");
        exit(1);
    }
    
    //第二步:将服务器的网络信息结构体绑定前填充
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    //第三步:将网络信息结构体与套接字绑定
    if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr)) == -1)
    {
        perror("fail to bind");
        exit(1);
    }

    return 0;
}

接收数据——recvfrom 函数

ssize_t recvfrom(int sockfd,void *buf,size_t nbytes,int flags,
                struct sockaddr *from,socklen_t *addrlen);

功能:

  • 接收UDP数据,并将源地址信息保存在from指向的结构中

参数:

  • sockfd:套接字
  • buf:接收数据缓冲区
  • nbytes:接收数据缓冲区大小
  • flags:套接字标志(常为0)
  • from:源地址结构体指针,用来保存数据的来源
  • addrlen:from所指向内容的长度

返回值:

  • 成功:返回实际接收的字节数
  • 失败:返回-1

实例

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define N 128
//bind示例,一般服务器都需要执行bind函数

int main(int argc,char* argv[])
{

    if(argc < 3)
    {
        fprintf(stderr,"Usage: %s ip port\n",argv[0]);
        exit(1);
    }
    //第一步:创建套接字
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) == -1)
    {
        perror("fail to socket");
        exit(1);
    }
    
    //第二步:将服务器的网络信息结构体绑定前填充
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//192.168.12.128
    serveraddr.sin_port = htons(atoi(argv[2]));//9999

    //第三步:将网络信息结构体与套接字绑定
    if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr)) == -1)
    {
        perror("fail to bind");
        exit(1);
    }

    //接收数据
    char buf[N] = "";
    struct sockaddr_in clientaddr;
    socklen_t addrlen = sizeof(struct sockaddr_in);

    while(1)
    {
        
    if(recvfrom(sockfd,buf,N,0,(struct sockaddr *)&clientaddr,&addrlen) == -1)
    {
        perror("fail to recvfrom");
        exit(1);
    }
        //打印数据
        //打印客户端的ip地址和端口号
        printf("ip:%s,port:%d\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
        //打印接收到的数据
        printf("from client: %s\n",buf);
    }
    return 0;
}
posted @ 2022-06-03 22:59  LiuTBaby  阅读(1256)  评论(0编辑  收藏  举报