今天继续网络编程,基本的TCP和UDP实现方式我们已经可以实现了,接下来就是学习一些更加底层的原理了,预计这部分是需要七天文章对应一星期的写作。这周刚好完结,希望有人愿意跟我一起学习呀。
作者简介:一个学嵌入式的年轻人
✨联系方式:2201891280(QQ)
源码地址:https://gitee.com/xingleigao/study_qianrushi
⏳全文大约阅读时间: 60min
在UNIX/Linux下主要有四种I/O模型
异步
通信模式阻塞I/O模式时最普遍使用的I/O模式,大部分程序使用的都是阻塞模式的I/O
缺省情况下,套接字建立后所处于的模式就是阻塞式I/O模式
之前使用的很多函数在调用的时候会发生阻塞
sendto默认不阻塞
在UDP套接字上执行写操作永远不会阻塞
。非阻塞模式的实现
int fcntl(int fd, int cmd, long arg);
int flag;
flag = fcntl(sockfd, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(sockdf, F_SETEL, flag);
int b_on = 1;
ioctl(sock_fd, FIONBIO, &b_on);
基本常识:
Linux中每个进程默认情况下,最多可以打开1024个文件,最多有1024哥文件描述符
文件描述符特点:
1. 非负整数
2. 从最小的数字分配
3. 每个进程启动时默认打开0、1、2三个文件描述符
多路复用针对不止套接字,也针对普通文件描述符。
void FD_ZERO(df_set *fdset) //清空集合
void FD_SET(int fd, fd_set *fdset) //把fd加入集合
void FD_CLR(int fd, fd_set *fdset) //从集合中删去fd
int FD_ISSET(int fd, set *fdset) //判断fd是否在set中
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
一般:填读集合,写集合填NULL,异常集合(带外数据)一般填NULL
struct timeval{
long tv_sec; /*seconds*/
long tv_usec; /*microsecods*/
};
时间单位:1秒(s) = 103毫秒(ms) = 106微秒(us)=109纳秒(ns) = 1012皮秒(ps)
注:select推出后,集合表示有数据的集合,内核对fd_set进行了改变。
if(FD_ISSET(fd, &Rset){
1. 若是监听套接字有数据,则有新客户端链接,则accept()
2 .若是建立的连接套接字有数据,则读数据
}
示例代码:
#include "net.h"
#define QUIT_STR "quit"
int main(void){
int fd = -1;
struct sockaddr_in sin;
/*创建sockt fd*/
if((fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0){
perror("socket");
exit(1);
}
/*2.连接服务器*/
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT); //网络字节序的端口号转换
//sin.sin_addr = inet_addr(SERV_IP_ADDR); //IPV4
if(inet_pton(AF_INET, SERV_IP_ADDR,(void *)&sin.sin_addr) != 1){
perror("inet_pton");
exit(1);
}
if(connect(fd, (struct sockaddr *)&sin, sizeof(sin)) <0){
perror("connect");
exit(1);
}
fd_set rset;
int maxfd = -1;
struct timeval tout;
char buf[BUFSIZ];
int ret;
while(1){
FD_ZERO(&rset);
FD_SET(0, &rset);
FD_SET(fd, &rset);
maxfd = fd;
tout.tv_sec = 5;
tout.tv_usec = 0;
select(maxfd + 1, &rset, NULL, NULL, &tout);
if(FD_ISSET(0,&rset)){
bzero(buf, BUFSIZ);
do{
ret = read(0, buf, BUFSIZ - 1);
}while(ret <0 &&EINTR == errno);
if(ret < 0){
continue;
}
if(!ret) break; //服务器关闭
if(write(fd, buf, strlen(buf)) < 0){
perror("write() to socket");
continue;
}
if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))){
printf("Client is exiting!\n");
break;
}
}
if(FD_ISSET(fd, &rset)){//服务器给发送过了数据
//的读取套接字数据,处理
bzero(buf, BUFSIZ);
do{
ret = read(fd, buf, BUFSIZ - 1);
}while(ret <0 &&EINTR == errno);
if(ret < 0){
perror("read from socket");
continue;
}
if(!ret) break; //服务器关闭
printf("serve send mesg:%s\n",buf);
if((strlen(buf) > strlen(SERV_RESP_STR)) && !strncasecmp(buf + strlen(SERV_RESP_STR), QUIT_STR, strlen(QUIT_STR))){
printf("Client is exiting!\n");
break;
}
}
}
/*4.关闭服务器*/
close(fd);
return 0;
}
放假这两天有些懒了,今天我要更完这部分内容,主要是后面的用的也不多,前面的应用较多大家跟我一起改变世界。啊哈哈哈,求求大家给个三连再走吧