Linux学习_UDP编程
Posted Leslie X徐
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux学习_UDP编程相关的知识,希望对你有一定的参考价值。
UDP编程
UDP编程模型
- UDP客户端服务器编程模型
- 服务器端调用序列
- 调用socket函数创建套接字
- 调用bind绑定本地地址和端口
- 调用readfrom函数获取发送来的数据
- 调用sendto函数向对方发送数据报文
- 调用close关闭套接字
- 客户端调用序列
- 调用socket函数创建套接字
- 调用bind绑定本地地址和端口,可不绑定
- 调用sendto函数向对方发送数据报文
- 调用readfrom函数获取发送来的数据
- 调用close关闭套接字
数据传输
- 发送数据报文函数
#include <sys/socket.h>
ssize_t send(int sockfd, //套接字描述符
const void* buf, //要传送的数据
size_t nbytes, //数据长度
int flag); //默认为0
返回:成功返回发送的字节数,出错返回-1。
ssize_t sendto(int sockfd,
const void* buf,
size_t nbytes,
int flag,
const struct sockaddr* destaddr, //目的地址信息
socklen_t destlen); //地址结构体长度
返回:成功返回发送的字节数,出错返回-1。
ssize_t sendmsg(int sockfd,
const struct msghdr* msg,
int flag);
返回:成功返回发送的字节数,出错返回-1。
struct msghdr {
void *msg_name; /* optional address */
socklen_t msg_namelen; /* size of address */
struct iovec *msg_iov; /* scatter/gather array */
size_t msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data, see below */
size_t msg_controllen; /* ancillary data buffer len */
int msg_flags; /* flags (unused) */
};
- 接收数据报文函数
#include <sys/socket.h>
ssize_t recv(int sockfd,
void* buf,
size_t nbytes,
int flag);
ssize_t recvfrom(int sockfd,
void* restrict buf,
size_t len,
int flag,
struct sockaddr* restrict addr,
socklen_t* restrict addrlen);
ssize_t recvmsg(int sockfd,
struct msghdr* msg,
int flag);
返回:有消息则返回消息的字节数,无消息返回0,出错返回-1
服务器和客户端编程
- 服务器编程
/*
* time_udp_server.c
*/
#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <arpa/inet.h>
int sockfd;
void sig_handler(int signo)
{
if(signo == SIGINT){
printf("server close\\n");
close(sockfd);
exit(1);
}
}
//输出客户端的信息
void out_addr(struct sockaddr_in *clientaddr)
{
char ip[16];
memset(ip,0,sizeof(ip));
inet_ntop(AF_INET,
&clientaddr->sin_addr.s_addr, ip, sizeof(ip)); //ip
int port = ntohs(clientaddr->sin_port); //port
printf("client: %s(%d)\\n",ip,port);
}
//与客户端进行通信
void do_service(void)
{
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
//接收客户端的数据报文
if(recvfrom(sockfd,buffer,sizeof(buffer), 0, //flag默认0
(struct sockaddr*)&clientaddr, &len)<0){
perror("recvfrom error");
exit(1);
}else{
out_addr(&clientaddr);
printf("client send info: %s\\n",buffer);
//向客户端发送数据报文
long int t = time(0);
char *ptr = ctime(&t);
size_t size = strlen(ptr)*sizeof(char);
//传送时间到客户端
if(sendto(sockfd, ptr, size, 0,
(struct sockaddr*)&clientaddr, len)<0){
perror("sendto error");
exit(1);
}
}
}
//主函数传入监听的端口
int main(int argc, char **argv)
{
if(argc<2){
printf("usage: %s port\\n",argv[0]);
exit(1);
}
if(signal(SIGINT,sig_handler) == SIG_ERR){
perror("signal sigint error");
exit(1);
}
/*步骤1:创建socket*/
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd<0){perror("socket error");exit(1);}
int ret;
int opt = 1;
//设置套接字选项,SO_REUSEADDR让停止的端口能够马上使用
if((ret = setsockopt(sockfd, SOL_SOCKET,
SO_REUSEADDR, &opt, sizeof(opt)))<0){
perror("set socket opt error");
exit(1);
}
/*步骤2:调用bind函数对socket和地址进行绑定*/
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET; //IPv4
serveraddr.sin_port = htons(atoi(argv[1])); //port
serveraddr.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY获得所有ip地址请求
if(bind(sockfd, (struct sockaddr*)&serveraddr,
sizeof(serveraddr))<0){
perror("bind error");
exit(1);
}
/*步骤3:和客户端进行双向的数据通信*/
while(1){
do_service();
}
return 0;
}
- 客户机编程
/*
* time_udp_client.c
*
*/
#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <memory.h>
#include <arpa/inet.h>
//判断输入是否为域名
int is_host(struct hostent* host,char* name)
{
if(!strcmp(host->h_name,name))return 1;
int i=0;
while(host->h_aliases[i]!=NULL){
if(!strcmp(host->h_aliases[i],name))return 1;
++i;
}
return 0;
}
unsigned int get_ip_by_name(char *name)
{
unsigned int ip = 0;
struct hostent *host;
while((host = gethostent())!=NULL){
if(is_host(host,name)){ //判断传进来的域名是否在/etc/hosts里面
memcpy(&ip, host->h_addr_list[0], 4);
break;
}
}
endhostent();
return ip;
}
int main(int argc, char **argv)
{
if(argc<3){
printf("usage: %s ip port\\n",argv[0]);
exit(1);
}
/*步骤1:创建socket*/
int sockfd = socket(AF_INET, SOCK_DGRAM,0);
if(sockfd<0){
perror("socket error");
exit(1);
}
/*步骤2:调用recvfrom和sendto函数
* 和服务器端双向通讯
* */
struct sockaddr_in serveraddr;
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));//port
//inet_pton(AF_INET,argv[1],
//&serveraddr.sin_addr.s_addr);//ip
/*使用域名解析*/
unsigned int ip = get_ip_by_name(argv[1]);
if(ip!=0){
serveraddr.sin_addr.s_addr = ip;
}else{ //如果根据域名无法找到ip
inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);
}
if(connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){
perror("connect error");
exit(1);
}
char buffer[1024] = "hello iotek";
//向服务器发送数据报文
if(sendto(sockfd,buffer,sizeof(buffer),0,
(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){
perror("sendto error");
exit(1);
}
else{
/*成功发送后,开始接收服务器发送的数据报文(因为之前已经知道服务器端的信息,所以直接用recv函数)
* */
memset(buffer,0,sizeof(buffer));
if(recv(sockfd,buffer,sizeof(buffer),0)<0){
perror("recv error");
exit(1);
}
else{
printf("%s",buffer);
}
}
close(sockfd);
return 0;
}
输出:
客户端:
pi@raspberrypi:~/haitong-learning/Linux/homework/网络编程 $ ./time_udp_client xuwenhan 8888
Wed May 19 00:14:30 2021
pi@raspberrypi:~/haitong-learning/Linux/homework/网络编程 $ ./time_udp_client www.xu.com 8888
Wed May 19 00:14:43 2021
pi@raspberrypi:~/haitong-learning/Linux/homework/网络编程 $
服务器:
pi@raspberrypi:~/haitong-learning/Linux/homework/网络编程 $ ./time_udp_server 8888
client: 192.168.31.174(56188)
client send info: hello iotek
client: 192.168.31.174(45804)
client send info: hello iotek
^Cserver close
pi@raspberrypi:~/haitong-learning/Linux/homework/网络编程 $
以上是关于Linux学习_UDP编程的主要内容,如果未能解决你的问题,请参考以下文章