一、基于socket的UDP通信
1.udp的通信流程
服务器:
创建套接字(socket)
-->绑定IP地址和端口号(bind) man 7 ip
-->接收(recvfrom)/发送(sendto)
-->关闭套接字(close)
客户端:
创建套接字(socket)
-->接收(recvfrom)/发送(sendto)
-->关闭套接字(close)
2.UDP发送和接收
(1)发送数据—sendto
对比:
普通:
ssize_t write(int fd, const void *buf, size_t count);
TCP:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
返回值:成功返回发送的字节数
参数:
sockfd:套接字
buf:发送内容缓冲区的首地址
len:请求发送的字节数
flags:默认选项0
UDP:
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
参数:
sockfd:套接字
buf:发送内容缓冲区的首地址
flags:0
dest_addr:接收端的地址就够信息首地址 struct sockaddr_in caddr存入
(struct sockaddr*)&caddr强转
addrlen:接收端地址结构的大小
(2)接收数据—recvfrom
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
参数:
sockfd:套接字
buf:缓冲区结构体首地址
len:buf长度
flags:0
src_addr:发送端的地址结构信息首地址
addrlen:发送端地址结构长度的地址
练习
练习1:搭建一个UDP发送端和接收端,发送端向接收发送信息,接收端打印信息
服务器:
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = INADDR_ANY;
int s_len = sizeof(saddr);
int ret = bind(sockfd, (struct sockaddr *)&saddr, s_len);
if(ret < 0)
{
perror("bind");
exit(-1);
}
char buf[N] = {0};
struct sockaddr_in caddr;
memset(&caddr, 0, sizeof(caddr));
int c_len = sizeof(caddr);
while(1)
{
memset(buf, 0, N);
ret = recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&caddr, &c_len);
if(ret < 0)
{
perror("recvfrom");
exit(-1);
}
printf("%s:%s\n", inet_ntoa(caddr.sin_addr), buf);
}
close(sockfd);
return 0;
}
客户端:
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = inet_addr("192.168.16.163");
int s_len = sizeof(saddr);
int ret;
char buf[N] = {0};
while(1)
{
fgets(buf, N, stdin);
ret = sendto(sockfd, buf, N, 0, (struct sockaddr *)&saddr, s_len);
if(ret < 0)
{
perror("sendto");
exit(-1);
}
}
close(sockfd);
return 0;
}
练习2:设计一个时间服务器,发送端向服务器发送时间请求,接收方回传当前时间
服务器:
#define N 64
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = INADDR_ANY;
int s_len = sizeof(saddr);
int ret = bind(sockfd, (struct sockaddr *)&saddr, s_len);
if(ret < 0)
{
perror("bind");
exit(-1);
}
char buf[N] = {0};
struct sockaddr_in caddr;
memset(&caddr, 0, sizeof(caddr));
int c_len = sizeof(caddr);
time_t t;
struct tm *my_t = NULL;
while(1)
{
memset(buf, 0, N);
ret = recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&caddr, &c_len);
if(ret < 0)
{
perror("recvfrom");
exit(-1);
}
if(strcmp(buf, "time\n") == 0)
{
memset(buf, 0, N);
time(&t);
my_t = localtime(&t);
sprintf(buf, "%d-%d-%d %d:%d:%d", my_t->tm_year+1900,
my_t->tm_mon+1, my_t->tm_mday, my_t->tm_hour, my_t->tm_min,
my_t->tm_sec);
sendto(sockfd, buf, N, 0, (struct sockaddr *)&caddr, c_len);
}
else
{
strcpy(buf, "cmd error!");
sendto(sockfd, buf, N, 0, (struct sockaddr *)&caddr, c_len);
}
}
close(sockfd);
return 0;
}
客户端:
#define N 64
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in saddr, caddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = inet_addr("192.168.16.163");
int s_len = sizeof(saddr);
int c_len = sizeof(caddr);
memset(&caddr, 0, sizeof(caddr));
int ret;
char buf[N] = {0};
while(1)
{
fgets(buf, N, stdin);
ret = sendto(sockfd, buf, N, 0, (struct sockaddr *)&saddr, s_len);
if(ret < 0)
{
perror("sendto");
exit(-1);
}
memset(buf, 0, N);
ret = recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&caddr, &c_len);
printf("recv %dbytes:%s\n", ret, buf);
}
close(sockfd);
return 0;
}