📄 example74.c
字号:
// 无法正确使用其功能,需要改进调试
/* example74.c */
/* 非阻塞式I/O */
/* talk实例——UDP的应用,用非阻塞的socket将其实现 */
/* 套接字通常缺省情况下都是工作在阻塞方式下。要使用非阻塞套接字,就要改变套接字的工作方式。通常可以通过下面两个函数改变其工作方式:fcntl()和ioctl()。
* fcntl() :
该函数可以设置一个套接字的O_NONBLOCK标志,可以通过下列操作来完成整个过程:
int flag;
flag = fcntl(sockfd, F_GETFEL, 0);
flag |= O_NONBLOCK;
fcntl(sockfd, F_SETFL, flag);
其中,
* sockfd 要设置的文件描述符
* F_GETFL 获取描述符标志位的操作名称
* F_SETFL 设置描述符标志位的操作名称
首先将所要改变的套接字的属性获得并存放在flag变量中。
然后将flag“与”O_NONBLOCK,即把O_NONBLOCK属性加进该套接字的属性中。
在执行了这些操作之后,一个套接字就被设置成为工作在非阻塞方式下。
* ioctl() :
在ioctl()中使用FINOBIO命令时,可将一个高字节设置成非阻塞方式的套接字:
int key = 1;
ioctl(sockfd, FIONBIO, &key);
其中,
* sockfd 要设置的文件描述符
执行这个函数后,sockfd所指示的套接字就被设置成为非阻塞方式。同时,key中保存了套接字的当前属性。
*/
/* 本例用fcntl()实现。
程序中,stdin和socket都将被设置为非阻塞的,然后通过在这两个句柄上轮询来检查哪个句柄变成“可读”,并进行相应处理。
值得注意的是,将stdin设置为非阻塞后,不能再使用标准输入输出函数操作该句柄,而要通过底层的输入输出函数来操作。
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#define BUFLEN 255
int main(int argc, char **argv)
{
struct sockaddr_in peeraddr, localaddr;
int sockfd, stdinfd, n, maxfd, socklen, flags;
char msg[BUFLEN + 1];
fd_set infds;
if (argc != 5) { // 输入错误提示(不符合输入的字符串数量要求则提示数据应输入的格式)
printf("%s <dest IP address> <dest port> <source IP address> <source port>\n", argv[0]);
exit(0);
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0); // socket UDP
if (sockfd < 0) {
fprintf(stderr, "socket creating error in udptalk.c!\n");
exit(0);
}
flags = fcntl(sockfd, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(sockfd, F_SETFL, flags);
stdinfd = fileno(stdin); // 聊天消息输入,取得文件描述符
// 设置套接字工作于非阻塞方式
flags = ( fcntl(stdinfd, F_GETFL, 0) );
flags |= O_NONBLOCK;
fcntl(stdinfd, F_SETFL, flags);
socklen = sizeof(struct sockaddr_in);
memset(&peeraddr, 0, socklen);
peeraddr.sin_family = AF_INET;
peeraddr.sin_port = htons(atoi(argv[2]));
if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) {
printf("Wrong dest IP address!\n");
exit(0);
}
memset(&localaddr, 0, socklen);
localaddr.sin_family = AF_INET;
if (inet_pton(AF_INET, argv[3], &localaddr.sin_addr) <= 0) {
printf("Wrong source IP address!\n");
exit(0);
}
localaddr.sin_port = htons(atoi(argv[4]));
if (bind(sockfd, (const struct sockaddr *)&localaddr, socklen) < 0) { // bind
fprintf(stderr, "bind local address error in udptalk.c!\n");
exit(2);
}
connect(sockfd, (const struct sockaddr *)&localaddr, socklen); // connect
for ( ; ; ) {
n = recvfrom(sockfd, msg, BUFLEN, 0, (struct sockaddr *)&peeraddr, &socklen);
if (n < 0) {
if (errno != EINTR) {
fprintf(stderr, "recvfrom error in udptalk.c!\n");
perror("");
exit(4);
}
else
fprintf(stderr, "recvfrom timeout in udptalk.c!\n");
}
else {
msg[n] = 0;
printf("peer:%s", msg);
}
n = read(stdinfd, msg, BUFLEN);
switch(n) {
case -1:
if (errno != EWOULDBLOCK) {
fprintf(stderr, "stdin error\n");
exit(4);
}
break;
case 0:
printf("talk over!\n");
exit(0);
break;
default:
write(msg, sockfd, n);
msg[n] = 0;
printf("sent: %s", msg);
}
}
/*
for ( ; ; ) {
n = read(sockfd, &peeraddr, socklen);
if (n == -1) {
if (errno != EWOULDBLOCK) {
printf("talk colsed by peer\n");
exit(3);
}
}
else {
msg[n] = 0;
printf("peer: %s", msg);
}
n = read(stdinfd, msg, BUFLEN);
switch(n) {
case -1:
if (errno != EWOULDBLOCK) {
fprintf(stderr, "stdin error\n");
exit(4);
}
break;
case 0:
printf("talk over!\n");
exit(0);
break;
default:
write(msg, sockfd, n);
msg[n] = 0;
printf("sent: %s", msg);
}
}
*/
}
/*
对于一个UDP数据保的socket来说,没有发送缓冲区的概念。因此,也不存在write()因为发送数据缓冲区满而阻塞的情况。
即再非阻塞的UDP socket中进行操作不可能出现EWOULDBLOCK的错误。
但是,对于面向连接的TCP socket来说,write()是有可能出现EWOULDBLOCK错误的。
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -