UDP网络编程实例

Linux网络编程:UDP网络编程实例。

问题描述

在Linux系统下,采用C语言编写一个UDP服务器程序和一个UDP客户端程序。客户端向服务器发送字符串后,服务器能把该字符串返回给客户端,客户端再把字符串显示出来。如果客户端向服务器端发送GetTime字符串时,客户机端能接收并显示当前服务器端的系统时间。

UDP协议

什么是UDP协议

  • UDP(User Datagram Protocol),即用户数据报协议,是一种面向非连接的协议。面向非连接指的是在正式通信前不必与对方先建立连接,不管对方状态就直接发送。至于对方是否可以接收到这些数据内容,UDP协议无法控制,因此UDP协议是一种不可靠的协议。

  • UDP适用于一次只传送少量数据、对可靠性要求不高的应用和环境。由于没有建立连接的过程,所以它的通信效率高。使用UDP传输,需要发送端和接收端。

  • UDP特点:无连接的,不可靠的,数据报包小于等于64k,效率高。

UDP编程框架

UDP网络编程可以分为客户端和服务器端两部分。服务器端主要包含建立套接字、将套接字与地址结构进行绑定、读写数据、关闭套接字几个过程。客户端包括建立套接字、读写数据、关闭套接字几个过程。服务器端和客户端两个流程之间的主要差别在于对地址的绑定(bind())函数,客户端可以不用进行地址和端口的绑定操作。

代码实现

头文件

1
// udp.h
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <string.h>
5
#include <netinet/in.h>
6
#include <ctype.h>
7
#include <sys/types.h>
8
#include <sys/socket.h>
9
#include <arpa/inet.h>
10
#include <errno.h>
11
#include <signal.h>
12
#include <unistd.h>
13
#include <time.h>

服务器端程序

1
// udp_server.c
2
#include "udp.h"	// UDP编程所需头文件
3
#define MAXLINE 80	// 数据缓冲区大小
4
#define SERV_PORT 8000	// 服务器端口
5
char data_buf[MAXLINE]; // 数据缓冲区
6
int main(void) {
7
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // socket()建立套接字文件描述符
8
    struct sockaddr_in server_address; // 服务端网络地址结构信息
9
    struct sockaddr_in client_address; // 存放数据发送方的地址信息
10
    socklen_t client_address_len; // client_address所指内容的长度
11
    char str[INET_ADDRSTRLEN];
12
    bzero(&server_address, sizeof(server_address));
13
    server_address.sin_family = AF_INET; // 地址类型为AF_INET
14
    server_address.sin_addr.s_addr = htonl(INADDR_ANY); // 任意本地地址
15
    server_address.sin_port = htons(SERV_PORT); // 服务器端口
16
    bind(sockfd, (struct sockaddr *)&server_address, sizeof(server_address)); // bind()函数绑定侦听端口
17
18
    printf("Accepting connections ...\n");
19
    while (1) {
20
        client_address_len = sizeof(client_address);
21
        // recvfrom()函数接收客户端的网络数据
22
        int n = recvfrom(sockfd, data_buf, MAXLINE, 0, (struct sockaddr *)&client_address, &client_address_len);
23
        if (n == -1)
24
            perror("Recvfrom error");
25
        printf("Received from %s at PORT %d\n", inet_ntop(AF_INET, &client_address.sin_addr, str, sizeof(str)), ntohs(client_address.sin_port));
26
        // 如果需要获取时间
27
        char Get[MAXLINE] = "GetTime";
28
        int flag = 1;
29
        for(int i=0; i<7; i++) {
30
            if(data_buf[i] == Get[i])
31
                continue;
32
            else flag = 0;
33
        }
34
        if(flag == 1) {
35
            time_t times = time(NULL);
36
            struct tm* utcTime = gmtime(&times);
37
            sprintf(data_buf, "%04d.%02d.%02d %02d:%02d:%02d\n", utcTime->tm_year+1900, utcTime->tm_mon+1, utcTime->tm_mday, utcTime->tm_hour+8, utcTime->tm_min, utcTime->tm_sec);
38
            n = sizeof(data_buf);
39
        }
40
        // sendto()函数向服务器主机发送数据
41
        n = sendto(sockfd, data_buf, n, 0, (struct sockaddr*)&client_address, sizeof(client_address));
42
        if (n == -1)
43
            perror("Sendto error");
44
    }
45
    return 0;
46
}

客户端程序

1
// udp_client.c
2
#include "udp.h" // UDP编程所需头文件
3
#define MAXLINE 80 // 数据缓冲区大小
4
#define SERV_PORT 8000 // 服务器端口
5
int main(int argc, char *argv[]) {
6
    int sockfd = sockfd = socket(AF_INET, SOCK_DGRAM, 0); // Socket()函数建立套接字文件描述符 
7
    struct sockaddr_in server_address; // 服务端网络地址结构信息
8
    socklen_t server_address_len; // server_address所指内容的长度
9
    char data_buf[MAXLINE]; // 数据缓冲区
10
    char str[INET_ADDRSTRLEN];
11
    bzero(&server_address, sizeof(server_address));
12
    server_address.sin_family = AF_INET; // 地址类型为AF_INET
13
    inet_pton(AF_INET, "127.0.0.1", &server_address.sin_addr);
14
    server_address.sin_port = htons(SERV_PORT); // 服务器端口
15
    
16
    while (fgets(data_buf, MAXLINE, stdin) != NULL) {
17
        // sendto()向服务器发送数据
18
        int n = sendto(sockfd, data_buf, strlen(data_buf), 0, (struct sockaddr *)&server_address, sizeof(server_address));
19
        if (n == -1)
20
            perror("Sendto error");
21
        // recvfrom()接收服务器的数据
22
        n = recvfrom(sockfd, data_buf, MAXLINE, 0, NULL, 0);
23
        if (n == -1)
24
            perror("Recvfrom error");
25
        write(STDOUT_FILENO, data_buf, n);
26
    }
27
    // 关闭套接字
28
    close(sockfd);
29
    return 0;
30
}

测试程序

编译并运行服务器端程序

1
gcc -o udp_server udp_server.c
2
./udp_server

编译并运行客户端程序

1
gcc -o udp_client udp_client.c
2
./udp_client