Linux网络编程 udp套接字代码

时间:2022-05-06 10:18:48

udp部分的网络编程,主要分为3个部分,套接字的创建,端口号和ip地址的绑定,数据的读取和发送,我们一般创建两个文件,分别用来建立服务器和客户端。我们主要来看一下服务器的代码。

UDP服务器代码

服务器的编写可以分为:udp套接字的创建,绑定端口号和ip地址,等待接受数据并处理。这里我们简单的做一个回显服务器,就是服务器接收到客户端发来的数据后不进行什么处理,直接将原数据返回给客户端。

  • udp套接字创建

说到网络编程,不得不提的就是网络套接字socket,无论你想要做什么,只要是涉及到网路编程的,都必须使用套接字,可以说套接字是整个网络编程的基础。

套接字的创建调用的是socket()函数,函数原型如下:

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

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

参数domain指的的通行领域,主要包括AF_INET IPV4和AF_INET6IPV6。

参数type 表示协议种类,因为这里我们编写的是udp套接字,因此使用SOCK_DGRAM(即支持数据包,无连接,不可靠,固定最大长度消息)。

参数protocol目前可先不考虑,填0即可,即将协议指定为0;

因此我们的udp套接字创建时非常简单的,具体代码如下:

int sock = socket(AF_INET,SOCK_DGRAM, 0);
if(sock == -1){
    perror("socket");
    return 1;
}

socket的返回值:成功时返回套接字的文件描述符,失败是返回-1,并且会适当的设置错误码(errno)。因此我们在创建完套接字后,要人为的检测返回值,常看是否出错。


绑定端口号和IP地址

绑定端口的API是bind()函数,函数原型为:

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

要绑定端口号和ip地址,就得有一个套接字,因此第一个参数就是套接字的文件描述符。第二个参数就是要绑定的端口号和ip地址,只不过是按照结构体的形式给出;第三个参数就是结构体的大小。

我们先来看一下这个结构体struct sockaddr,它是我们网络编程中的一个通用结构体,我们一般定义struct sockaddr_in这个结构体,它包括了16位的端口号,和32位的IP地址。只不过我们在使用的时候强制类型转换为struct sockaddr

它的返回值如大多数函数一样,成功返回零,失败返回-1;

绑定的里程如下:


struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(atoi(argv[2]));
local.sin_addr.s_addr = inet_addr(argv[1]);

if( bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0 ){
    perror("bind");
    return 3;
}

我们先定义一个struct sockaddr_in的变量local,然后选择它的地址类型local.sin_familyAF_INETAF_INET为IPv4类型,AF_INET6为IPv6类型的网络)。local.sin_port为端口号, local.sin_addr.s_addr为IP地址。argv[2]argv[1]分别存放端口号,和IP地址。

需要主要的是,端口号和IP地址,并不是简单的赋值就可以了,因为这个argv数组里面存放的是字符串,因此在赋值时要特别注意。

在对端口号赋值时因为网络字节序是大端,而你的主机不指定是大端,因此有个通用的函数htons(),如果你的主机的大端那它什么都不做,如果是小端,那他会转成大端,因此,一般的端口号的赋值为:local.sin_port = htons(PortNum);如果你给的字符串形式,那就需要调用atoi()将其转化成整形。

在对ip地址赋值时,我们所给的一般地址为点分十进制形式的,因此我们需要将其转换的网络字节序,这里就用到inet_addr()函数。

我们在来看一下这个网络结构体的结构:
Linux网络编程 udp套接字代码

Linux网络编程 udp套接字代码

虽然socket api的接口是sockaddr,单是我们真正在基于IPv4编程时,使用的数据结构是sockaddr_in;这个结构里面主要有三部分:地址类型,端口号,ip地址。

Linux网络编程 udp套接字代码

in_addr结构体对ip的地址进行了一次封装,真正的IP地址存放在 s_addr里面。


接受数据并处理

接受数据就是从sock套接字中读取数据,调用函数recvfrom()可以进行读取,函数原型:

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

总共包含6个参数,前4个参数是普通类型的参数,而后面两个是输出型参数,因为我们作为服务器,肯定要知道数据是从哪里发过来的,因此将对端的信息保存在后面两个参数中。

要套接字读取数据,却少不了的就是套接字的文件描述符了,第二个参数是你自己定义的缓存区的地址,即读来的信息存放到哪里;第三个参数是缓存区的大小,第四个参数我们可以先不关注,填0即可。后面的两个参数一个是用来存放对端的ip地址和端口号,另外一个是这个结构体的大小,以指针的方式传递。

因为是回显服务器,所做的处理就是将从哪里得到的数据在发回到哪里。

向套接字发送数据使用的sendto()函数,函数原型:

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                     const struct sockaddr *dest_addr, socklen_t addrlen);

前面的和读取函数类似,后面的两个就是服务器的地址和端口号,和结构体的长度。