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;
}
                    
                
                
            
        
浙公网安备 33010602011771号