前置知识
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
sockfd:表示要发送数据的套接字描述符。它是由socket函数创建的套接字返回的文件描述符。
buf:是一个指向要发送数据的缓冲区的指针。数据将从这个缓冲区复制到套接字发送。
len:表示要发送的数据的长度,以字节为单位。
flags:是一个可选的参数,用于控制发送操作的行为。它可以取以下标志之一或它们的组合:
0:没有特殊的标志,表示普通的发送操作。
MSG_DONTROUTE:指示数据包不应该被路由。
MSG_OOB:用于发送TCP数据(带外数据)。
MSG_NOSIGNAL:在发送数据时忽略SIGPIPE信号,如果连接已断开,则返回错误而不是导致进程终止。
该函数返回发送的字节数,如果出现错误,返回值为-1。以下是函数返回值的一些可能情况:
= 0:表示发送成功,并返回发送的字节数。
-1:表示发送过程中发生错误。可以通过检查全局变量errno来确定具体错误的类型。常见的错误包括:
EINTR:操作被信号中断。
EAGAIN 或 EWOULDBLOCK:套接字已设置为非阻塞模式,并且发送操作将阻塞。
EBADF:无效的文件描述符。
EFAULT:buf指针指向无效的内存地址。
ENOTCONN:套接字未连接。
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd:表示要接收数据的套接字描述符。它是由socket函数创建的套接字返回的文件描述符。
buf:是一个指向接收数据的缓冲区的指针。接收到的数据将被复制到这个缓冲区。
len:表示接收数据的最大长度,以字节为单位。如果接收到的数据长度超过len,则超出部分的数据将被截断。
flags:是一个可选的参数,用于控制接收操作的行为。它可以取以下标志之一或它们的组合:
0:没有特殊的标志,表示普通的接收操作。 MSG_DONTWAIT 或 MSG_NONBLOCK:以非阻塞模式接收数据。
MSG_OOB:接收带外数据。 MSG_PEEK:接收数据但不从接收队列中删除数据。
该函数返回接收到的字节数,如果出现错误,返回值为-1。以下是函数返回值的一些可能情况:
= 0:表示接收成功,并返回接收到的字节数。
0:表示连接已关闭,没有更多数据可接收。-1:表示接收过程中发生错误。可以通过检查全局变量errno来确定具体错误的类型。常见的错误包括:
EINTR:操作被信号中断。 EAGAIN 或 EWOULDBLOCK:套接字已设置为非阻塞模式,并且没有可用的数据。
EBADF:无效的文件描述符。 EFAULT:buf指针指向无效的内存地址。
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd:表示要发送数据的套接字描述符。它是由socket函数创建的套接字返回的文件描述符。
buf:是一个指向要发送数据的缓冲区的指针。数据将从这个缓冲区复制到套接字发送。
len:表示要发送的数据的长度,以字节为单位。
flags:是一个可选的参数,用于控制发送操作的行为。它可以取以下标志之一或它们的组合:
0:没有特殊的标志,表示普通的发送操作。 MSG_DONTROUTE:指示数据包不应该被路由。
MSG_OOB:用于发送紧急数据(带外数据)。
MSG_NOSIGNAL:在发送数据时忽略SIGPIPE信号,如果连接已断开,则返回错误而不是导致进程终止。
dest_addr:是一个指向目标地址结构的指针,其中包含要发送到的目标地址信息。该结构可以是sockaddr、sockaddr_in或sockaddr_in6,具体取决于套接字的地址类型。
addrlen:表示目标地址结构的长度,以字节为单位。对于IPv4地址,通常为sizeof(struct
sockaddr_in);对于IPv6地址,通常为sizeof(struct sockaddr_in6)。
该函数返回发送的字节数,如果出现错误,返回值为-1。以下是函数返回值的一些可能情况:
= 0:表示发送成功,并返回发送的字节数。
-1:表示发送过程中发生错误。可以通过检查全局变量errno来确定具体错误的类型。常见的错误包括:
EINTR:操作被信号中断。 EAGAIN 或 EWOULDBLOCK:套接字已设置为非阻塞模式,并且发送操作将阻塞。
EBADF:无效的文件描述符。
EFAULT:buf或dest_addr指针指向无效的内存地址。 ENOTCONN:套接字未连接。
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:表示要接收数据的套接字描述符。它是由socket函数创建的套接字返回的文件描述符。
buf:是一个指向接收数据的缓冲区的指针。接收到的数据将被复制到这个缓冲区。
len:表示接收数据的最大长度,以字节为单位。如果接收到的数据长度超过len,则超出部分的数据将被截断。
flags:是一个可选的参数,用于控制接收操作的行为。它可以取以下标志之一或它们的组合:
0:没有特殊的标志,表示普通的接收操作。 MSG_DONTWAIT 或 MSG_NONBLOCK:以非阻塞模式接收数据。
MSG_OOB:接收带外数据。
MSG_PEEK:接收数据但不从接收队列中删除数据。
src_addr:是一个指向发送者地址结构的指针,用于存储发送者的地址信息。在函数调用之前,需要将该结构初始化为目标地址结构的类型,例如sockaddr_in或sockaddr_in6。
addrlen:是一个指向存储发送者地址结构长度的变量的指针。在函数调用之前,需要将该变量初始化为src_addr指向的结构的大小。
该函数返回接收到的字节数,如果出现错误,返回值为-1。以下是函数返回值的一些可能情况:
= 0:表示接收成功,并返回接收到的字节数。
0:表示连接已关闭,没有更多数据可接收。
-1:表示接收过程中发生错误。可以通过检查全局变量errno来确定具体错误的类型。常见的错误包括:
EINTR:操作被信号中断。
EAGAIN 或 EWOULDBLOCK:套接字已设置为非阻塞模式,并且没有可用的数据。
EBADF:无效的文件描述符。
EFAULT:buf或src_addr指针指向无效的内存地址
UDP流程示意图
UDP(User Datagram Protocol,用户数据报协议)是一种网络传输协议,位于传输层,提供面向无连接的数据报传输服务。与TCP(Transmission Control Protocol,传输控制协议)相比,UDP更为简单、轻量级,但不提供可靠的数据传输和流控制机制
无连接性:UDP是一种无连接的协议,发送端和接收端在通信之前不需要建立连接。每个UDP数据报都是独立的单元,可以独立发送、接收和处理,没有固定的数据传输顺序。
不可靠性:UDP不提供可靠的数据传输机制。它不保证数据报的完整性、顺序性或可靠性。发送的数据报可能会丢失、重复、乱序或损坏。应用程序需要自行处理这些问题。
高效性:由于UDP没有建立连接和维护状态的开销,以及较小的首部开销,UDP具有较低的通信延迟和网络负载。它适用于实时应用程序或对传输延迟要求较低的场景。
支持广播和多播:UDP支持广播(向局域网内的所有主机发送数据)和多播(向特定组内的主机发送数据)功能,可以在局域网中有效地分发数据。
适用场景:UDP适用于一些特定的应用场景,如实时音视频传输(如语音通话、视频会议)、网络广播、域名解析(DNS)等。在这些场景下,速度和实时性比可靠性更重要。
服务器端代码
没有使用线程和进程,但是可以实现并发处理。
int udpserver()
{
printf("%s %d\n", __func__, __LINE__);
struct sockaddr_in addr_in;
int fd = -1;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
{
printf("%s %d fd<0\n", __func__, __LINE__);
return 0;
}
int b_reuse = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));
// 2、绑定
memset(&addr_in, 0, sizeof(addr_in)); // 将变量addr_in置0
addr_in.sin_port = htons(SERVER_PORT); // 将端口号从本地字节序转换为网络字节序
addr_in.sin_family = AF_INET;
addr_in.sin_addr.s_addr = htonl(INADDR_ANY); // 任意IP可以运行
int rec = bind(fd, (struct sockaddr *)&addr_in, sizeof(addr_in));
if (rec < 0)
{
printf("%s %d rec<0\n", __func__, __LINE__);
return 0;
}
struct sockaddr_in clientin;
bzero(&clientin, sizeof(clientin));
char buf[256];
bzero(buf, sizeof(buf));
socklen_t addr_len = sizeof(clientin);
int reclen = -1;
printf("%s %d bind ok \n", __func__, __LINE__);
while (1)
{
printf("%s %d 111111111111 \n", __func__, __LINE__);
bzero(buf, sizeof(buf));
reclen = 0;
reclen = recvfrom(fd, buf, 256 - 1, 0, (struct sockaddr *)&clientin, &addr_len);
printf("%s %d reclen = %d\n", __func__, __LINE__, reclen);
if (reclen < 0)
{
printf("%s %d reclen < 0\n", __func__, __LINE__);
continue;
}
else
{
printf("%s %d 11111reclen = %d\n", __func__, __LINE__, reclen);
}
printf("%s %d buf = %s\n", __func__, __LINE__, buf);
char ipv4_arr[16];
if (!inet_ntop(AF_INET, (void *)&clientin.sin_addr, ipv4_arr, sizeof(clientin)))
{
printf("%s %d err \n", __func__, __LINE__);
}
printf("Recived from(%s :%d),data:%s", ipv4_arr, ntohs(clientin.sin_port), buf);
if (!strncasecmp(buf, "exit", strlen("exit")))
{
printf("Recived from(%s :%d) exit", ipv4_arr, ntohs(clientin.sin_port));
return 0;
}
}
close(fd);
return 0;
}
客户端代码
执行方法:
/*
.client server_ip server_port
*/
void usage(char *s)
{
printf("%s %d %s serv_ip ser_port\n", __func__, __LINE__, s);
printf("%s %d serv_ip : server ip address\n", __func__, __LINE__);
printf("%s %d ser_port: server port (>5000)\n", __func__, __LINE__);
}
/*
.client server_ip server_port
*/
int udpclient(int argc, char *argv[])
{
printf("%s %d\n", __func__, __LINE__);
if (argc < 3)
{
usage(argv[0]);
}
int fd = -1;
int port = 4004;
// 1、创建套接字 UDP
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) //注意这个地方!!!
{
printf("%s %d socket < 0\n", __func__, __LINE__);
return 0;
}
port = atoi(argv[2]);
struct sockaddr_in clientin;
bzero(&clientin, sizeof(clientin));
clientin.sin_family = AF_INET; // 设置地址属性
clientin.sin_port = htons(port); // 设置端口号
int rec = inet_pton(AF_INET, argv[1], (void *)&clientin.sin_addr.s_addr);
if (rec != 1)
{
printf("%s %d rec!= 1\n", __func__, __LINE__);
return 0;
}
char buf[1024];
while (1)
{
fprintf(stderr,"pls input string:");
memset(buf, 0, 1024);
char *rec_p = fgets(buf, 1024 - 1, stdin);
if (rec_p == NULL)
{
printf("%s %d rec_p == NULL\n", __func__, __LINE__);
continue;
}
sendto(fd, buf, strlen(buf), 0, (struct sockaddr *)&clientin, sizeof(clientin));
int recstr = strncasecmp(buf, "exit", strlen("exit"));
if (0 == recstr)
{
printf("%s %d q======\n", __func__, __LINE__);
break;
}
}
close(fd);
return 0;
}