C语言UDP编程流程
C语言UDP编程流程
服务器:
创建套接字socket()
将服务器的ip地址、端口号与套接字进行绑定bind()
接收数据recvfrom()
发送数据sendto()
客户端:
创建套接字socket()
发送数据sendto()
接收数据recvfrom()
关闭套接字close()
一、创建UDP套接字
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
int main()
{
//使用socket函数创建套接字
//创建一个用于UDP网络编程的套接字
int sockfd;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) == -1)
{
perror("fail to socket");
exit(1);
}
printf("sockfd = %d\n",sockfd);
return 0;
}
ipv4套接字地址结构
在网络编程中经常使用的结构体sockaddr_in:
struct in_addr
{
in_addr_t s_addr;//IP 地址4字节
};
struct sockaddr_in
{
sa_family_t sin_family;//协议族 2字节
in_port_t sin_port;//端口号 2字节
struct in_addr sin_addr;// IP地址 4字节
char sin_zero[8]// 填充,不起什么作用 8字节
};
通用结构体 sockaddr
struct sockaddr
{
sa_family_t sa_family; //2字节
char sa_data[14] //14字节
}
在定义源地址时用struct sockaddt_in
struct sockaddr_in my_addr;
调用编程接口函数的时候需要强制类型转换为struct sockaddr类型
bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr));
二、发送数据——sendto函数
ssize_t sendto(int sockfd,const void*buf,seze_t nbytes,int flags,
const struct sockaddr*to,socken_t addrlen);
功能
- 向to结构体指针指定的ip,发送UDP数据
参数
- sockfd: 套接字
- buf: 发送数据缓冲区
- nbytes:发送数据缓冲区大小
- flags:一般为0
- to:指向目的主机地址的结构体指针
- addrlen:to所指向内容的长度
注意
- 通过to和addrlen确定目的地址
- 可以发送0长度的UDP数据包
返回值
- 成功返回发送的字节数
- 失败返回-1
sendto函数应用实例
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define N 128
int main(int argc,char *argv[])
{
if(argc < 3)
{
fprintf(stderr,"Usage: %s ip port\n",argv[0]);
exit(1);
}
//第一步:使用socket函数创建套接字
//创建一个用于UDP网络编程的套接字
int sockfd;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) == -1)
{
perror("fail to socket");
exit(1);
}
printf("sockfd = %d\n",sockfd);
//第二部:填充服务器网络信息结构体 sockaddr_in
struct sockaddr_in serveraddr;
socklen_t addrlen = sizeof(serveraddr);
serveraddr.sin_family = AF_INET; //协议族,AF_INET:ipv4协议
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //IP地址
serveraddr.sin_port = htons(atoi(argv[2]));
//第三步:发送数据
char buf[N] = "";
while(1)
{
fgets(buf,N,stdin);
buf[strlen(buf) - 1] = '\0';//把buf字符串中的\n转化为\0
if(sendto(sockfd,buf,N,0,(struct sockaddr *)&serveraddr,addrlen) == -1)
{
perror("fail to sendto");
exit(1);
}
}
//第四步:关闭套接字文件描述符
close(sockfd);
return 0;
}
bind()函数的使用
int bind(int sockfd,const struct sockaddr *my_addr,socklen_t addrlen);
功能:
- 将本地协议地址与sockfd绑定
参数
- sockfd: 套接字
- my_addr: 指向特定协议的地址结构指针
- addrlen: 该地址结构的长度
返回值
- 成功: 返回0
- 失败: 其他
实例
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
//bind示例,一般服务器都需要执行bind函数
int main(int argc,char* argv[])
{
if(argc < 3)
{
fprintf(stderr,"Usage: %s ip port\n",argv[0]);
exit(1);
}
//第一步:创建套接字
int sockfd;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) == -1)
{
perror("fail to socket");
exit(1);
}
//第二步:将服务器的网络信息结构体绑定前填充
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
//第三步:将网络信息结构体与套接字绑定
if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr)) == -1)
{
perror("fail to bind");
exit(1);
}
return 0;
}
接收数据——recvfrom 函数
ssize_t recvfrom(int sockfd,void *buf,size_t nbytes,int flags,
struct sockaddr *from,socklen_t *addrlen);
功能:
- 接收UDP数据,并将源地址信息保存在from指向的结构中
参数:
- sockfd:套接字
- buf:接收数据缓冲区
- nbytes:接收数据缓冲区大小
- flags:套接字标志(常为0)
- from:源地址结构体指针,用来保存数据的来源
- addrlen:from所指向内容的长度
返回值:
- 成功:返回实际接收的字节数
- 失败:返回-1
实例
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define N 128
//bind示例,一般服务器都需要执行bind函数
int main(int argc,char* argv[])
{
if(argc < 3)
{
fprintf(stderr,"Usage: %s ip port\n",argv[0]);
exit(1);
}
//第一步:创建套接字
int sockfd;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) == -1)
{
perror("fail to socket");
exit(1);
}
//第二步:将服务器的网络信息结构体绑定前填充
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//192.168.12.128
serveraddr.sin_port = htons(atoi(argv[2]));//9999
//第三步:将网络信息结构体与套接字绑定
if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr)) == -1)
{
perror("fail to bind");
exit(1);
}
//接收数据
char buf[N] = "";
struct sockaddr_in clientaddr;
socklen_t addrlen = sizeof(struct sockaddr_in);
while(1)
{
if(recvfrom(sockfd,buf,N,0,(struct sockaddr *)&clientaddr,&addrlen) == -1)
{
perror("fail to recvfrom");
exit(1);
}
//打印数据
//打印客户端的ip地址和端口号
printf("ip:%s,port:%d\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
//打印接收到的数据
printf("from client: %s\n",buf);
}
return 0;
}