网络-UDP通信

1. UDP

全双工通信、面向无连接、不可靠

UDP(User Datagram Protocol)用户数据报协议,是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。

适用场景

发送小尺寸数据(如对DNS服务器进行IP地址查询时)

适合于广播/组播式通信中。

MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通讯通常采用UDP协议

2. UDP通信过程

服务器:

1. 创建数据报套接字 socket

2. 填充网络信息

3. 绑定 bind

4. 发送接收消息 sendto/recvfrom

5. 关闭套接字 close

客户端:

1. 创建数据报套接字 socket

2. 指定网络(服务器)信息

3. 发送接收消息 sendto/recvfrom

4. 关闭套接字 close

3.服务器端

要求:客户端向服务器发送消息,然后服务器向客户端回发一条一样的消息

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

#define N 128 // 宏定义数组大小

// 服务器:
// 1. 创建数据报套接字	socket
// 2. 填充网络信息
// 3. 绑定	bind
// 4. 发送接收消息	sendto/recvfrom
// 5. 关闭套接字	close

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

    // 1. 创建数据报套接字	socket
    int serverfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (serverfd < 0)
    {
        perror("serverfd socket err");
        return -1;
    }
    printf("socket scuess\n");

    // 2. 填充网络信息
    struct sockaddr_in myaddr;   // 服务器结构体变量
    myaddr.sin_family = AF_INET; // IPV4协议
    // 结构体成员变量是sin_addr ,sin_addr是struct in_addr 结构体类型的,成员变量为 s_addr,
    // 网络ip地址需要转成机器识别的类型;
    myaddr.sin_addr.s_addr = INADDR_ANY;
    // 端口
    myaddr.sin_port = htons(atoi(argv[1]));

    // 3. 绑定	bind
    // 绑定自己的地址
    // 需要强转成通用结构体类型的地址
    socklen_t addrlen = sizeof(myaddr);
    if (bind(serverfd, (struct sockaddr *)&myaddr, addrlen) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf("bind scuess\n");

    // 4. 发送接收消息	sendto/recvfrom
    ssize_t len = -1; // 接收recv返回值
    char buf[N] = "";
    struct sockaddr_in cliaddr; // 存放客户端结构体变量

    while (1)
    {
        // 接收来自客户端的消息
        // 清空数组
        memset(buf, 0, N);
        // recvfrom最后两个参数为发送端的网络地址信息
        len = recvfrom(serverfd, buf, N, 0,
                       (struct sockaddr *)&cliaddr, &addrlen);
        if (len < 0)
        {
            perror("recv err");
            break;
        }

        // 接收到quit时退出
        if (buf[strlen(buf) - 1] == '\n')
        {
            buf[strlen(buf) - 1] = '\0';
        }
        if (strcmp(buf, "quit") == 0)
        {
            break;
        }

        // 服务器获取的ip地址与端口号都是网络字节序,输出的的时候需要转成主机字节序
        printf("ip = %s, port = %d\n", inet_ntoa(cliaddr.sin_addr),
               ntohs(cliaddr.sin_port));
        printf("recv data = %s\n", buf);

        // 向客户端回发消息
        // sendto最后两个参数为接收端的网络地址信息
        len = sendto(serverfd, buf, len, 0,
                     (struct sockaddr *)&cliaddr, sizeof(cliaddr));
        if (len < 0)
        {
            printf("send error\n");
            return -1;
        }
    }

    close(serverfd);

    return 0;
}

4. 客户端

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

#define N 128 // 宏定义数组大小

// 客户端:
// 1. 创建数据报套接字	socket
// 2. 指定网络(服务器)信息
// 3. 发送接收消息 	sendto/recvfrom
// 4. 关闭套接字	close

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

    // 1. 创建数据报套接字	socket
    int clientfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (clientfd < 0)
    {
        perror("serverfd socket err");
        return -1;
    }

    // 2. 指定网络(服务器)信息
    struct sockaddr_in server_addr;   // 服务器结构体变量
    server_addr.sin_family = AF_INET; // IPV4协议
    // 结构体成员变量是sin_addr ,sin_addr是struct in_addr 结构体类型的,成员变量为 s_addr,
    // 网络ip地址需要转成机器识别的类型;
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    // 端口
    server_addr.sin_port = htons(atoi(argv[2]));
    // 结构体大小
    socklen_t addrlen = sizeof(server_addr);
    // 3. 发送接收消息 	sendto/recvfrom
    ssize_t len = -1; // 接收send返回值
    char buf[N] = "";
    char buff[N] = "";

    while (1)
    {
        // 向服务器端发送消息
        // 清空数组
        memset(buf, 0, N);

        // 从终端读取内容
        len = read(1, buf, N);
        if (buf[strlen(buf) - 1] == '\n')
        {
            buf[strlen(buf) - 1] = '\0';
        }

        // sendto最后两个参数为接收端的网络地址信息
        len = sendto(clientfd, buf, len, 0,
                     (struct sockaddr *)&server_addr, addrlen);
        if (len < 0)
        {
            printf("send error\n");
            return -1;
        }

        if (strcmp(buf, "quit") == 0)
        {
            break;
        }

        // 接收服务器端回发的消息
        // 清空数组
        memset(buf, 0, N);
        // recvfrom最后两个参数为发送端的网络地址信息
        len = recvfrom(clientfd, buf, N, 0,
                       (struct sockaddr *)&server_addr, &addrlen);
        if (len < 0)
        {
            perror("recv err");
            break;
        }
        // 格式化输出
        printf("recv:%s!!!!\n", buf);

        fprintf(stdout, "recv:%s!!!!", buf);

        sprintf(buff, "recv:%s!!!!", buf);
        printf("%s\n", buff);
    }

    close(clientfd);

    return 0;
}

5. 运行结果

服务器端

客户端

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值