UDP 编程
#include <sys/socket.h>
// 可以发送或接收一个长度为0的数据报
ssize_t recvfrom(int sockfd,
void *buf, size_t nbytes,
int flags, // 常与 recv send recvmsg sendmsg 配合
struct sockaddr *from, socklen_t *addrlen); // 可用空指针表示不关心发送者
ssize_t sendto(int sockdf,
const void *buf, size_t nbytes,
int flags,
const struct sockaddr *to, socklen_t *addrlen);
服务端
// 包含所有的头文件
#include <arpa/inet.h>
#include <bits/stdc++.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
const int SERV_PORT = 60000;
int main(int argc, char const *argv[]) {
int sockfd{0};
struct sockaddr_in serv_addr;
struct sockaddr_in client_addr;
char buf_msg[BUFSIZ]{0};
char buf_addr[BUFSIZ]{0};
int n{0};
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_PORT);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket");
exit(1);
}
if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {
perror("bind");
exit(1);
}
while (true) {
socklen_t client_addr_len = sizeof(client_addr);
bzero(buf_msg, sizeof(buf_msg));
n = recvfrom(sockfd, buf_msg, BUFSIZ, 0, (struct sockaddr *)&client_addr,
&client_addr_len);
if (n == -1) {
perror("recvfrom");
exit(1);
}
printf(
"received from %s at PORT %d: %s\n",
inet_ntop(AF_INET, &client_addr.sin_addr, buf_addr, sizeof(buf_addr)),
ntohs(client_addr.sin_port), buf_msg);
sendto(sockfd, buf_msg, n, 0, (struct sockaddr *)&client_addr,
sizeof(client_addr));
}
return 0;
}
客户端
#include <arpa/inet.h>
#include <bits/stdc++.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
// const int SERV_PORT = 60000;
// const char *SERV_ADDR = "127.0.0.1";
int main(int argc, char const *argv[]) {
int sockfd{0};
struct sockaddr_in serv_addr;
struct sockaddr_in recv_addr;
char buf_addr[BUFSIZ]{0};
char buf_msg[BUFSIZ]{0};
char SERV_ADDR[] = "127.0.0.1";
int SERV_PORT = 60000;
if (argc >= 2) {
strncpy(SERV_ADDR, argv[1], strlen(argv[1]));
}
if (argc == 3) {
int port_num = atoi(argv[2]);
if (port_num > 0 && port_num < 65536) {
SERV_PORT = port_num;
}
}
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
if (inet_pton(AF_INET, SERV_ADDR, &serv_addr.sin_addr) != 1) {
perror("inet_pton");
exit(1);
}
serv_addr.sin_port = htons(SERV_PORT);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket");
exit(1);
}
// // 客户端也可绑定端口,也可以由系统自动分配。设置本地ip和端口号
// if (bind(sockfd, (struct sockaddr *)&client_addr, sizeof(client_addr))) {
// perror("bind");
// exit(1);
// }
// connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
while (printf("input >> "), scanf("%s%*c", buf_msg) > 0) {
sendto(sockfd, buf_msg, strlen(buf_msg), 0, (struct sockaddr *)&serv_addr,
sizeof(serv_addr));
socklen_t recv_addr_len = sizeof(recv_addr);
int n = recvfrom(sockfd, buf_msg, BUFSIZ, 0, (struct sockaddr *)&recv_addr,
&recv_addr_len);
if (n == -1) {
perror("recvfrom");
exit(1);
}
inet_ntop(AF_INET, &recv_addr.sin_addr, buf_addr, sizeof(buf_addr));
int recv_port = ntohs(recv_addr.sin_port);
// 忽略来自其他地址的数据报
if (strlen(buf_addr) != strlen(SERV_ADDR) ||
strncmp(buf_addr, SERV_ADDR, strlen(SERV_ADDR)) != 0 ||
recv_port != SERV_PORT) {
printf("Datagram from %s:%d was ignored.\n", buf_addr, recv_port);
continue;
}
printf("received from %s at PORT %d: %s\n", buf_addr,
ntohs(recv_addr.sin_port), buf_msg);
}
return 0;
}
异步错误
当服务器进程退出后,客户端的recvfrom请求会永远阻塞。本地网络上,客户端发送udp数据报前进行一次ARP请求和应答,服务器返回一个ICMP端口不可达,但这个ICMP错误不会返回给客户进程。这个ICMP错误称为一个异步错误。
对于一个UDP套接字,它引发的异步错误不会发送给他,除非他使用 connect 进行了连接。如果不进行绑定,recvfrom仅能返回errno,不能返回IP和端口。
connect
使用connect绑定后,可以返回异步错误。实现记录对端iP和端口号 之后发送数据不可以再用 sentdo 指定目的地址,而是使用 write sendmsg 或 第五个参数为空的sendto 接收数据不可以再用 recvfrom 指定源地址,而是使用 read recvmsg 或 第五个参数为空的recvfrom可以多次调用 connect 指定新的IP和端口号,将套接字地址结构体的 sin_family 指定为 AF_UNSPEC 可以断开连接。
使用 connect 显示指定目的地后会提升效率,类似的UDP也可以使用 bind 函数指定本地套接字IP和端口
标签:serv,SERV,UDP,addr,int,编程,sockfd,buf From: https://www.cnblogs.com/zhh567/p/17607437.html