Linux网络编程之UDP套接字
来源:互联网 发布:日本f3战斗机知乎 编辑:程序博客网 时间:2024/04/28 08:46
udp协议是面向数据报的,无连接不可靠的传输层协议,因为udp无连接,因此实现udp socket不需要监听也不需要连接。在实现tcp socket时,其中的收发数据可以当做文件,通过read和write来读取(TCP是面向字节流的),而udp是面向数据报的协议,收发的单位都是数据报,需要采用专门的函数:
udp socket编程:
udp_server.c:
#include <stdio.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>int main(int argc, char* argv[]){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]);if(bind(sock, (struct sockaddr*)&local, sizeof(local))<0){perror("bind");return 2;}char buf[1024];struct sockaddr_in client;socklen_t len = sizeof(client);while(1){ssize_t s = recvfrom(sock, buf, sizeof(buf)-1, \0, (struct sockaddr*)&client, &len);if(s > 0){printf("[%s: %d]# %s\n", inet_ntoa(client.sin_addr), \ntohs(client.sin_port), buf);sendto(sock, buf, strlen(buf), 0, \(struct sockaddr*)&client, sizeof(client));}}return 0;}
udp_client.c:
#include <stdio.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>int main(int argc, char* argv[]){int sock = socket(AF_INET, SOCK_DGRAM, 0);if(sock < 0){perror("socket");return 1;}struct sockaddr_in server;server.sin_family = AF_INET;server.sin_port = htons(atoi(argv[2]));server.sin_addr.s_addr = inet_addr(argv[1]);char buf[1024];char *msg = "hello world";while(1){sendto(sock, msg, strlen(msg), 0, \(struct sockaddr*)&server, sizeof(server));struct sockaddr_in tmp;socklen_t len = sizeof(tmp);ssize_t s = recvfrom(sock, buf, sizeof(buf)-1, \0, (struct sockaddr*)&tmp, &len);if(s > 0){printf("%s\n", buf);}sleep(5);}return 0;}
运行结果:
然而,因为udp是无连接不可靠的,那么就需要在应用层设置一些功能使udp较为可靠,这里可以利用信号实现简单的超时重传:
在规定的时间内,若没有收到sendto的信息,我们就认为数据包丢失,打破recvfrom的阻塞式等待,设置一个闹钟定时器alarm,当收到SIGALRM信号时,程序中断,转去处理中断响应函数,通知客户端进行重传;
功能:从调用该函数开始,seconds秒之后向进程发送一个SIGALRM信号
仅仅只是这样是不够的!!!
看到这里也许你以为一切都解决了,但是还有一个容易被人忽视的问题:在Linux中,默认处理中断的方式是:
当从中断调用返回时,继续执行被中断的系统调用(用在刚才说的例子上就是:继续recvfrom……)这种默认处理方式大多数时候很有用,但是我们这里就不行了,那这个问题怎么解决呢?要解决这个问题,就不能单纯的用signal函数去设置中断处理程序了,而是要用另一个函数:sigaction
功能:拦截下signum消息,用act所给的方式处理,将原来的处理方式放在oldact中(一般oldact设为NULL);
参数:
signum:需要拦截的消息,这里是SIGALRM;
act:处理中断的方式,是一个结构体,后面会介绍这结构体;
oldact:用来存储原来的处理方式,一般为NULL,表示忽略;
结构体:struct sigaction介绍:
struct sigaction
{
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
成员:第一个sa_handler就是中断处理程序的入口,比如:要用alarm程序处理这个中断,就将此值设为alarm;
sa_mask:表示在中断处理中要屏蔽的中断;
sa_flags:这是很关键的东西~它包含了一些影响中断处理过程方式的标志,具体取值如下:
SA_NOCLDSTOP:这表示如果所处理的中断是SIGCHLD,由于收到其他信号而导致了子进程终止,将不发送SIG_CHLD;
SA_ONESHOT or SA_RESETHAND:sa_handler所指向的中断处理程序只被执行一次,之后将设为默认的中断处理程序;
SA_RESTART:让被处理的系统调用在中断返回后重新执行;
SA_NOMASK or SA_NODEFFER(这就是我们要用的):在中断处理程序执行时,不屏蔽自己的中断信号;在处理此信号未结束前不理会此信号的再次到来。
signal与sigaction函数区别:
signal是重启动函数,超时以后会自动启动已阻塞的函数,而不是中断它的执行,比如recvfrom,给人的感觉就是使用了alarm,程序依然阻塞在了recvfrom上,不往下执行,如果在信号处理函数中使用printf可以看到超时后输出了一条超时信息,然后signal又启动了recvfrom,继续阻塞。sigaction可以自己设置是否重启动函数,即上面例子中的alrmact.sa_flags = SA_NOMASK选项,SA_NOMASK为不重启动,中断已阻塞的函数recvfrom,使程序继续往下执行,SA_RESTART为重启动函数,与signal相同,继续阻塞在recvfrom上。
#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<string.h>#include<arpa/inet.h>#include<netinet/in.h>#include<unistd.h>#include<signal.h>struct sockaddr_in client;socklen_t len = sizeof(client); int sock;//打印输入提示选项static void* usage(const char* port){ printf("usage: %s [local_ip] [local_port]\n",port);}//中断处理函数void alarm_handler(int sigon) { char* m = "please input again\n"; printf("alarm interrupt\n"); //给客户端发送重发消息 sendto(sock,m,strlen(m),0,(struct sockaddr*)&client,len); }int main(int argc,char* argv[]){ if(argc!=3){ usage(argv[0]); return 1; } //创建套接字 sock = socket(AF_INET,SOCK_DGRAM,0); if(sock<0){ perror("socket"); exit(1); } //将套接字与ip地址和端口号进行绑定 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]); if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0){ perror("bind"); exit(2); } char buf[1024]; //设置中断处理 struct sigaction alr; alr.sa_handler = alarm_handler; alr.sa_flags = SA_NOMASK; alr.sa_restorer = NULL; char* msg = "my name is h"; while(1){ //设置闹钟,当超过10s没有收到消息,则认为数据报丢失,发送SIGALRM信号 alarm(10); //捕获SIGALRM信号 sigaction(SIGALRM,&alr,NULL); //读取数据 int r = recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&client,&len); if(r<0){ perror("recvfrom"); exit(3); }else{ buf[r] = 0; printf("%s\n",buf); //回送数据 if(sendto(sock,msg,strlen(msg),0,(struct sockaddr*)&client,len)<0){ perror("sendto"); exit(4); } alarm(0);//当数据报传输成功时,这是闹钟没有用处了,关闭闹钟 } } return 0;}
- Linux网络编程之UDP套接字
- linux网络编程之udp套接字
- Linux网络编程 UDP套接字编程
- 网络编程之套接字UDP
- 网络套接字socket编程之UDP
- linux网络编程之套接字编程
- 【Linux网络编程】原始套接字实例:发送 UDP 数据包
- Linux网络编程【二】:UDP socket套接字详解
- Linux UDP套接字编程
- 《网络编程》基本 UDP 套接字编程
- 《网络编程》高级 UDP 套接字编程
- LINUX网络编程之套接字
- Linux 网络编程之原始套接字
- Linux 网络编程之原始套接字
- Linux 网络编程之原始套接字
- Linux网络编程之套接字基础
- Linux网络编程之TCP套接字
- 网络编程练习-UDP套接字
- JavaScript学习总结——JavaScript函数(function)
- JS工具库封装:HTML5摄像头拍照组件的封装
- SDUT 1133 模拟计算器
- webstorm快捷键大全
- shiro学习之后——认证
- Linux网络编程之UDP套接字
- JavaScript简易计算器
- 使用freetype生成字体图片FireMonkey效率问题
- MAVEN常用命令
- java学习【web基础-Javascript入门】
- linux ln -s
- C语言中的隐式函数声明
- JVM占用问题解决
- Spring 简单的邮件发送