之前已经介绍过socket编程了,并且实现了一个简单TCP网络程序
https://blog.csdn.net/qq_34021920/article/details/80153071
现在再来实现一个简单的UDP程序,来看看需要用到的操作
注:UDP同样需要利用socket函数创建套接字,然后利用bind函数进行绑定。在这里就不再做详细介绍(链接戳上面),在这里介绍一下UDP进行数据读写的操作
1.发送数据
#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, 改变flags,将会改变sendto发送的形式
dest_addr:指向目的套接字的地址
addrlen:所指地址的长度
返回值:成功返回实际传送出去的字符数,失败返回-1。
2.接受数据
#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:接受缓冲区长度
flags:调用操作方式,一般设置为0
src_addr:指向装有源地址的缓冲区
addrlen:缓冲区长度
返回值:成功返回接收到的字符数,失败返回-1。
在之前实现TCP的代码中,我们用到了一些地址转换函数,UDP中同样需要。在这里介绍一下
sockaddr_in中的成员struct in_addr sin_addr表示32位的IP地址,但是我们通常用点分十进制的字符串表示IP地址,所以就需要将字符串转换为in_addr类型,以下函数可就可以实现这个功能:
点分十进制字符串转为32位的网络字节序
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
int inet_pton(int af, const char *src, void *dst);
上面的三个函数都是将点分十进制字符串转换成整数
1. inet_aton
返回值:若字符串有效则返回1,否则返回0
参数cp:输入型参数,要转换的IP地址
参数inp:输出型参数,保存转换后的内容
2.inet_addr
返回值:若字符串有效则返回32位二进制网络字节序的IPv4地址,否则返回INADDR_NONE
参数cp:要转换的IP地址
3.inet_pton
返回值:若成功则返回1,若输入不是有效的表达格式则返回0,若出错则返回-1
参数af:地址族,我们使用的IPV4则为AF_INET
参数src:指向要转换IP地址字符串的地址
参数dst:保存转换后的内容
< 注:inet_addr函数存在一些问题,所有2^32个可能的二进制值都是有效的ip地址(0.0.0.0-255.255.255.255),但是当出错时函数返回INADDR_NONE常量(通常是一个32位均为1的值),这意味着点分十进制数串255.255.255.255不能由该函数处理,因为它的二进制用来指示该函数失败 >
32位的网络字节序转为点分十进制字符串
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr in);
const char *inet_ntop(int af, const void *src,
char *dst, socklen_t size);
1.inet_ntoa
返回值:成功返回指向一个点分十进制字符串的指针,失败返回NULL
参数in:保存要转换的一个网络字节序
2.inet_ntop
返回值:成功返回一个指向指向点分十进制字符串的指针,失败返回NULL
参数af:地址族,我们使用的IPV4则为AF_INET
参数src:要转换的网络字节序
参数dst:不可以为空,必须为目标存储单元分配内存并制定其大小,调用成功时,这个指针就是返回值
参数size:指向缓存区dst的大小,避免溢出
实现简单的UDP网络程序
udp_server.c UDP服务器端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc,char* argv[])
{
if(argc!=3)
{
printf("Usage:%s ip port\n",argv[0]);
return 1;
}
//创建套接字
int sock=socket(AF_INET,SOCK_DGRAM,0);
if(sock<0)
{
perror("socket");
return -1;
}
printf("create socket ok\n");
//初始化bind的参数
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(atoi(argv[2]));
local.sin_addr.s_addr=inet_addr(argv[1]);
//绑定
int b=bind(sock,(struct sockaddr*)&local,sizeof(local));
if(b<0)
{
perror("bind");
return -2;
}
printf("bind ok\n");
//发送接收消息
char buf[521];
while(1)
{
socklen_t len=sizeof(local);
ssize_t s=recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&local,&(len));
if(s>0)
{
buf[s]=0;
if(strcmp(buf,"quit")==0)
{
printf("client quit!\n");
break;
}
printf("[%s:%d]:%s\n",inet_ntoa(local.sin_addr),ntohs(local.sin_port),buf);
sendto(sock,buf,strlen(buf),0,(struct sockaddr*)&local,len);
}
}
return 0;
}
udp_client.c UDP客户端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc,char* argv[])
{
if(argc!=3)
{
printf("Usage:%s ip port",argv[0]);
return 1;
}
//创建套接字
int sock=socket(AF_INET,SOCK_DGRAM,0);
if(sock<0)
{
perror("socket");
return -1;
}
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(atoi(argv[2]));
local.sin_addr.s_addr=inet_addr(argv[1]);
//发送接收消息
char buf[521];
struct sockaddr_in peer;
while(1)
{
printf("Please Enter:");
fflush(stdout);
socklen_t len=sizeof(peer);
ssize_t s=read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s-1]=0;
sendto(sock,buf,strlen(buf),0,(struct sockaddr*)&local,len);
if(strcmp(buf,"quit")==0)
{
break;
}
ssize_t num=recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&local,&(len));
if(num>0)
{
buf[s]=0;
printf("server say:%s\n",buf);
}
}
}
return 0;
}
运行结果:
在这里同样利用本地IP127.0.0.1来进行测试