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. 运行结果
服务器端
客户端