注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Under the bule sky

Get what I want "cause I ask for it.

 
 
 

日志

 
 

TCP/IP网络编程Ⅰ  

2014-08-19 20:04:51|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、IO模型
在UNIX/Linux下主要有4种I/O模型
1.阻塞io:
最常用、最简单、效率最低
阻塞io模式是最普遍的使用的io模式,大部分程序使用的都是阻塞io模式,默认情况下,套接字建立后所处于的模式就是阻塞模式。
前面学过的很多读写函数在调用过程中会发生阻塞:
读操作中的read、recv、recvfrom
写操作中的write、send
其他操作accept、connect

2.非阻塞io:
可防止进程阻塞在io操作上,需要轮询
当一个应用程序使用了非阻塞模式的套接字,他需要使用一个循环来不停的测试是否一个文件描述符有数据可读,这将是一个非常浪费cpu的操作。

当一开始建立一个套接字时,系统默认为阻塞模式。可以使用函数fctrl()设置一个套接字的标志为O_NONBLOCK来实现非阻塞。
用法:

int fctrl(int fd,int cmd,long arg);

int flag;

flag = fctrl(sockfd,F_GETFL,0);

flag |= O_NONBLOCK;

fctrl(sockfd,F_SETFL,flag);



3.io多路复用
允许同时对多个io进行控制
先构造一张描述符的表,然后调用函数。当这些文件描述符中的一个或者多个已经准备好的进行io时函数才有返回。
函数返回时,告诉进程那个文件描述符已经就绪,可以进行io操作。
TCP/IP网络编程Ⅰ - 戴↑Ω听歌 - Under the bule sky
 
int select(int nfds, fd_set *readfds, fd_set *writefds,
    fd_set *exceptfds, struct timeval *timeout);
功能:监控检测指定的文件描述符
参数:
        nfds:       文件描述符最大范围maxfd + 1
        readfds:    读的文件描述符集合
        writefds:   写的文件描述符集合
        exceptfds:  其它的文件描述符集合一般指异常
        timeout:    NULL阻塞方式,时间设为0非阻塞方式,时间设置非0,指定时间内等待 
返回值:成功返回就绪文件描述符个数,并且在文件描述符表中清掉其它未就绪文件描述符;
        失败返回-1,置errno

工作方式:select可检测指定文件描述符集合中是否有文件描述符就绪;若无任何文件描述符就绪,select可选择阻塞等待,timeout传NULL;若有文件描述符就绪,select函数会返回返回值为就绪文件描述符个数并将指定文件描述符表中未就绪文件描述符清除。

void FD_CLR(int fd, fd_set *set);       //将fd从文件描述符表set中删除
int  FD_ISSET(int fd, fd_set *set);     //检测fd是否在文件描述符表set中
void FD_SET(int fd, fd_set *set);       //将fd加入文件描述符表set
void FD_ZERO(fd_set *set);              //将set所有位(文件描述符)清除

例子:监测listen_fd  STDIN_FILENO

fd_set readfds,fds_bak;
int maxfd = 0;

//将表清空
FD_ZERO(&fds_bak);
FD_ZERO(&readfds);

FD_SET(listen_fd,&fds_bak);
FD_SET(STDIN_FILENO,&fds_bak);

maxfd = listen_fd > STDIN_FILENO ? listen_fd : STDIN_FILENO;

while(1)
{
readfds = fds_bak;

//A:4 B:5 C:6
n = select(maxfd + 1,&readfds,NULL,NULL,NULL);

for(i = 0;i <= maxfd;i++)
{
if(FD_ISSET(i,&readfds))
{
if(i == STDIN_FILENO)
{
//读标准输入
}
else if(i == listen_fd)
{
//提取连接请求,并创建新的可用sockfd
sockfd = accept();

//FD_SET(sockfd,&fds_bak);/*{{{*/

//maxfd = sockfd > maxfd ? sockfd : maxfd;
}
else
{
读写i代表sockek /*}}}*/
}
}
}
}


select函数应用于tcp服务器
监测标准输入,listen_fd,若标准输入就绪,读数据并打印,若有新连接请求,提取并打印请求方ip和端口后关闭sockfd。

服务器端:
tcp_server.c

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BACKLOG 128
#define N 1024


#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)

typedef struct sockaddr sa_t;

int main(int argc, const char *argv[])
{
fd_set readfds,fds_bak;
int maxfd = 0;
int n = 0;
int i = 0;
int n_s = 0;
char buf[N] = {0};
int listen_fd = 0,sockfd;
struct sockaddr_in server_addr,peer_addr;
socklen_t addrlen = sizeof(struct sockaddr_in);




if(argc < 3)
{
fprintf(stderr,"Usage %s ip port\n",argv[1]);
exit(EXIT_FAILURE);
}

if((listen_fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
handle_error("socket");

memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);

//TODO step2 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
if(bind(listen_fd,(sa_t *)&server_addr,sizeof(sa_t)) < 0)
handle_error("bind");

//TODO step3 int listen(int sockfd, int backlog);
if(listen(listen_fd,BACKLOG) < 0)
handle_error("listen");

printf("listenning ...\n");

FD_SET(listen_fd,&fds_bak);
FD_SET(STDIN_FILENO,&fds_bak);

maxfd = listen_fd > STDIN_FILENO ? listen_fd : STDIN_FILENO;


while(1)
{
readfds = fds_bak;

n = select(maxfd + 1,&readfds,NULL,NULL,NULL);

for(i = 0;i <= maxfd;i++)
{
if(FD_ISSET(i,&readfds))
{
if(i == STDIN_FILENO)
{
n_s = read(STDIN_FILENO,buf,sizeof(buf));
buf[n_s-1] = '\0';
printf("%s\n",buf);
}
else if(i == listen_fd)
{
if((sockfd = accept(listen_fd,(sa_t *)&peer_addr,&addrlen)) < 0)
handle_error("accept");

printf("sockfd = %d\n",sockfd);

printf("===============================\n");
printf("IP:\t%s\n",inet_ntoa(peer_addr.sin_addr));
printf("PORT:\t%d\n",ntohs(peer_addr.sin_port));
printf("===============================\n");

n_s = recv(sockfd,buf,sizeof(buf) - 1,0);
buf[n_s] = '\0';
printf("Receive %d bytes:%s\n",n,buf);
}
}
}

}



return 0;
}

客户端代码:
client.c

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BACKLOG 128
#define N 1024

#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)

typedef struct sockaddr sa_t;


//./a.out 192.168.0.xxx 8888
int main(int argc, const char *argv[])
{
int n = 0;
char buf[N] = {0};
int sockfd = 0;
struct sockaddr_in server_addr,peer_addr;
socklen_t addrlen = sizeof(struct sockaddr_in);


if(argc < 3)
{
fprintf(stderr,"Usage %s ip port\n",argv[0]);
exit(EXIT_FAILURE);
}

//TODO step1 int socket(int domain, int type, int protocol);
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
handle_error("socket");

memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);


if(connect(sockfd,(sa_t *)&server_addr,sizeof(sa_t)) < 0)
handle_error("connect");

send(sockfd,"hello server!",13,0);

#if 0
n = recv(sockfd,buf,sizeof(buf) - 1,0);
buf[n] = '\0';
printf("Receive %d bytes:%s\n",n,buf);
#endif
close(sockfd);


return 0;
}



4.信号驱动io
一种异步通信模型

  评论这张
 
阅读(42)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017