linux网络--简单udp代码

本文详细介绍了如何在本地实现一个UDP服务器端,包括创建套接字、绑定端口与IP、接收和发送数据,以及处理大小端转换和IP地址解析的相关步骤。
摘要由CSDN通过智能技术生成

在本地端模拟实现一个udp通信的代码

1.服务器端--初始化

    我们运行程序的时候,希望的运行方式为 ./serve 127.0.0.1 8080

所以就需要我们在main函数接收命令行传递过来的参数

创建步骤以及注意事项

1.1  创建套接字

函数原型:

int socket(int domain, int type, int protocol);

参数

domian:域  ,说明了套接字是用来网络通讯还是本地通讯

红圈圈出来的三个为最常用的。

type 类型---选择在网络中的通讯是流式通讯还是数据报式的通讯.

这里使用的是udp通讯,所以这里选择使用数据报式

数据报套接字提供一种无连接、不可靠的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。SOCK——DGRAM

protocol :protocol: 一般忽略即可 即为0

返回值 返回值类型为Int,返回值为-1时代表创建套接字失败,同时设置error,成功后返回sock

1.2绑定端口与ip

函数原型:

int bind(int socket, const struct sockaddr *address,socklen_t address_len);

参数:

socket:套接字  ,传递我们上方创建的套接字,将端口与ip与其绑定

const struct sockaddr *address :

结构体内有如下成员

sin_port 表示了端口号

sin_addr表示了ip地址 由定义可以发现ip地址为32位的

我们要将此作为参数传递,那么就需要为该结构体赋值。不过在使用之前,最好将其清零

可以使用memset、bzero等函数

        在网络传输中一切以大端进行传输,所以在初始化该结构体时,需要转为大端,系统为我们提供了一个接口:htons(端口)

        主机转网络字节序因为我们的_port是我们自己定义的,有大小端之分,所以就需要转为大端   htons-----主机序列转网络序列,又因为port为短整型,为2字节,所以使用htons即可。

sin_addr  :  平常使用的ip为诸如192.168.1.1 这些为点分十进制风格的。

ip地址是一个32位的整数,正好可以使用4字节(32位)表示。原理:

        因为ip地址在我们看来是192.188.1.1 是点分十进制风格的,

        IP地址是一个32位的二进制数,通常被分割为4个“8位二进制数”。

        每段8位二进制数之间用点(.)隔开,以点分十进制的形式书写,

        所以ip地址占4个字节。

        那ip地址是给人看的,我们在网络中传输的话,就需要相应的转换

        1.把它转为二进制

        2.再使用主机转网络,修正其大小端问题。

        系统为我们提供了一个函数,可以同时完成上述两个任务。

        inet_addr();

注意:接收的参数类型位struct sockaddr *

而我们传递的类型位struct sockaddr_in * 需要强制类型转换?

为什么不使用void*? -->因为这一套流程出来的时候,c语言还不支持void*

返回值:返回值小于0时表示绑定失败,同时设置error

socklen_t address_len: 结构体的大小

2.服务器端--接收数据recvfrom

接收数据使用的常用的接口为

ssize_t recvfrom(int socket, void *restrict buffer, size_t length,
              int flags, struct sockaddr *restrict address,
              socklen_t *restrict address_len);

socket:套接字,使用上面创建出来的套接字即可

buffer:接收到的数据存放的buffer 

length 该buffer的大小

flags 默认设置为0

重点介绍 struct sockaddr *restrict address

在这里,它是输出型参数,用于获取向我发送信息的ip地址与端口号。

address_len 是输入输出形参数

        输入输出型参数   做输入型参数时,传递的是buffer的大小

        做输出型参数时,返回实际接收的字节大小

        socklen_t len = sizeof(buffer);

返回值 : 返回接收到的数据个数

 3.发送数据sendto

ssize_t sendto(int socket, const void *message, size_t length,
              int flags, const struct sockaddr *dest_addr,
              socklen_t dest_len);

 

#ifndef __UDP_SERVE__H_
#define __UDP_SERVE__H_
#include <iostream>
#include "log.hpp"
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
class serve
{
public:
    serve(uint16_t port=8080) : _port(port), _sock(-1)
    {
    }
    void serveInit()
    {
        // 1.创建套接字
        _sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sock == -1)
        {
            message(FATAL, "%d-%s", errno, strerror(errno));
        }
        // 2.绑定端口与ip
        struct sockaddr_in local;
        bzero(&local, sizeof(local)); // 将local内存设置为0
        local.sin_family = AF_INET;
        // 2.1 绑定端口 --需要主机转网络字节序
        /*
        主机转网络字节序
        因为我们的_port是我们自己定义的,有大小端之分,所以就需要转为大端
        htons-----主机序列转网络序列,又因为port为短整型,为2字节,所以使用htons即可
        */
        local.sin_port = htons(_port);
        // 2.2 绑定Ip地址
        /*
        因为ip地址在我们看来是192.188.1.1 是点分十进制风格的,
        IP地址是一个32位的二进制数,通常被分割为4个“8位二进制数”。
        每段8位二进制数之间用点(.)隔开,以点分十进制的形式书写,
        所以ip地址占4个字节。
        那ip地址是给人看的,我们在网络中传输的话,是不是要1.把它转为二进制
        2.再使用主机转网络,修正其大小端问题。
        系统为我们提供了一个函数,可以同时完成上述两个任务。
        inet_addr
        */
        local.sin_addr.s_addr =_ip.empty()? INADDR_ANY : inet_addr(_ip.c_str());
        if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            message(FATAL, "[%d-%s]", errno, strerror(errno));
            exit(2);
        }
        message(NORMAL, "[%s-%u-%s]", _ip.c_str(), _port, "success!");
    }

    void start()
    {
        // 1.读取数据
        char buffer[1024];
        while (1)
        {
            // 输出型参数,用于获取向我发送信息的ip地址与端口号
            struct sockaddr_in peer;
            bzero(&peer, sizeof(peer));
            // 输入输出型参数   做输入型参数时,传递的是buffer的大小
            // 做输出型参数时,返回实际接收的字节大小
            socklen_t len = sizeof(buffer);

            ssize_t s = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)(&peer), &len);
            if (s > 0)
            {
                buffer[s]=0;
                // 获取端口号
                // 因为从网络中来,大小端字节序需要重新更正---
                // 网络变主机
                uint16_t cliport = ntohs(peer.sin_port);
                std::string cliip = inet_ntoa(peer.sin_addr);
                // 分析数据
                // 写回数据
                std::cout<<"ip: "<<cliip<<"port: "<<cliport<<": "<<buffer<<std::endl;
                sendto(_sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,sizeof(peer));
            }
        }
    }

private:
    std::string _ip;
    uint16_t _port; // port为16位的整数
    int _sock;
};

#endif

 

需要重点注意的是  

 std::cout<<"ip: "<<cliip<<"port: "<<cliport<<": "<<buffer<<std::endl;

如果我们在向控制台输出时,不加后面的end,不会刷新缓冲区

所以需要使用end 或者fflush(stdout)。

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个简单Linux 网络编程私聊程序示例,使用 C 语言编写: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #define PORT 8888 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in server_addr, client_addr; char buf[1024]; char nickname[20]; socklen_t client_len; if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(EXIT_FAILURE); } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("bind"); exit(EXIT_FAILURE); } printf("Enter your nickname: "); fgets(nickname, 20, stdin); nickname[strcspn(nickname, "\n")] = 0; printf("Waiting for incoming messages...\n"); while (1) { memset(&buf, 0, sizeof(buf)); client_len = sizeof(client_addr); if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_len) == -1) { perror("recvfrom"); exit(EXIT_FAILURE); } printf("%s says: %s\n", nickname, buf); printf("Enter your message: "); memset(&buf, 0, sizeof(buf)); fgets(buf, 1024, stdin); buf[strcspn(buf, "\n")] = 0; if (sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&client_addr, client_len) == -1) { perror("sendto"); exit(EXIT_FAILURE); } } close(sockfd); return 0; } ``` 这个程序使用了 UDP 协议进行通信,通过绑定本地 IP 地址和端口号,等待客户端的连接请求。当接收到客户端发送的消息后,程序会将消息显示在屏幕上,并且等待用户输入回复消息。回复消息会通过 sendto 函数发送给客户端。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蠢 愚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值