由于UDP是无连接、尽力传输的,所以Server端绑定完IP、端口号后,使用recvfrom可以阻塞等待客户端的数据,而且Client端通过sendto发送的数据包直接发送到互联网(也是基于IP、端口号)这种操作是不担保Server端是否收到的,适用于实时音视频传输、DNS的域名解析
一、Sendto
实际上,sendto和recvfrom都是对于send和recv的扩展-----增加参数(用于绑定IP、端口号)
因为UDP是无连接的,想要传输信息实际上还是需要对象的信息,只不过它的信息不用像TCP编程中实际地对sock_fd进行绑定,而是直接通过参数绑定
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags
struct sockaddr *dest_addr, socklen_t addrlen
)
params: 前三个参数同write
flags 一般填0---此时同write
MSG_DONTWAIT 非阻塞版本
MSG_OOB 用于发送TCP类型的带外数据(out-of-band)
dest_addr 目标的结构体指针(配置IP、端口号)
addrlen 目标的结构体大小
二、Recvfrom
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, const void *buf, size_t len, int flags
struct sockaddr *src_addr, socklen_t *addrlen
)
params: 前三个参数同read
flags 一般填0---此时同read
MSG_DONTWAIT 非阻塞版本
MSG_OOB 用于发送TCP类型的带外数据(out-of-band)
src_addr 发送方的结构体指针(配置IP、端口号)
addrlen 发送方的结构体大小的指针----注意和sendto中的区别!!
return: 成功返回收到字节数,失败返回-1
三、服务器端
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h> //exit
#include <string.h> //bzero
#include <strings.h> //strncasecmp 忽略大小写比较
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERV_PORT (5001)
#define SERV_IP "127.0.0.1"
#define BUFSIZE (128)
#define QUIT_STR "quit"
int main(void)
{
int sock_fd;
struct sockaddr_in sin,cin;
//1.创建 注意套接字类型变化---数据报套接字
sock_fd = socket(AF_INET,SOCK_DGRAM,0);
if( sock_fd == -1)
{
perror("socket");
exit(1);
}
//优化:允许绑定地址快速重用
int b_reuse = 1;
setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));
//2.绑定
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);
sin.sin_addr.s_addr = htonl(INADDR_ANY);
if( (bind(sock_fd,(struct sockaddr*)&sin,sizeof(sin))) < 0 )
{
perror("bind");
exit(1);
}
//3.循环服务器模型
char rd_buf[BUFSIZ];
socklen_t cin_len = sizeof(cin);
while(1)
{
bzero(rd_buf,BUFSIZ);
if( recvfrom(sock_fd,rd_buf,BUFSIZ-1,(struct sockaddr*)&cin,&cin_len) < 0 )
{
perror("recvfrom");
continue;
}
//发送方的信息处理
char cin_ip[16];
if( inet_ntop(AF_INET,(void*)&cin.sin_addr,cin_ip,sizeof(cin)) == 0)
{
perror("inet_ntop");
exit(1);
}
printf("Received from(%s:%d),data:%s",cin_ip,ntohs(cin.sin_port),rd_buf);
}
close(sock_fd);
return 0;
}
四、客户端
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h> //exit
#include <string.h> //bzero
#include <strings.h> //strncasecmp 忽略大小写比较
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERV_PORT (5001)
#define SERV_IP "127.0.0.1"
#define BUFSIZE (128)
#define QUIT_STR "quit"
void usage(char *s)
{
printf("\nWarning:too few parameters!\n");
printf("\n\t%s SERV_IP SERV_PORT\n",s);
printf("SERV_PORT must > 5000\n");
}
int main(int argc, char **argv)
{
if( argc != 3)
{
usage(argv[0]);
exit(1);
}
int sock_fd;
struct sockaddr_in sin;
//1.创建
sock_fd = socket(AF_INET,SOCK_DGRAM,0);
if(sock_fd == -1)
{
perror("socket");
exit(1);
}
//配置sockaddr_in信息---用于sendto
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
//端口号、IP都从输入参数获得
u_short port = (u_short)atoi(argv[2]);
if(port<5000)
{
perror("port");
exit(1);
}
sin.sin_port = htons(port);
if( inet_pton(AF_INET,argv[1],(void*)&sin.sin_addr) != 1)
{
perror("inet_pton");
exit(1);
}
char wr_buf[BUFSIZE];
while(1)
{
//键盘获取内容-->发送到服务器
bzero(wr_buf,BUFSIZE);
if( fgets(wr_buf,BUFSIZE-1,stdin) == NULL )
{
perror("fgets");
continue;
}
sendto(sock_fd,wr_buf,strlen(wr_buf),0,(struct sockaddr*)&sin,sizeof(sin));
if( strncasecmp(wr_buf,QUIT_STR,strlen(QUIT_STR)) == 0) //输入了quit
{
break;
}
}
close(sock_fd);
return 0;
}