`
473687880
  • 浏览: 487410 次
文章分类
社区版块
存档分类
最新评论

深入linux网络编程(一):同步IO

 
阅读更多

作者:yurunsun@gmail.com 新浪微博@孙雨润 新浪博客 CSDN博客日期:2012年11月16日

1. IO模型

IO分为同步、异步,阻塞、非阻塞,两两组合成4种模型。

2. 同步阻塞IO

2.1 阻塞的原因

一个常见的问题是IO对请求没有准备好:例如调用读请求的时候可能设备上没有数据,但是将来可能有;调用写请求时可能舍妹没有准备好接收数据,一会儿可能buffer清空就好了。调用过程一般不去理会这些问题,如果程序员仅仅要求在请求返回时工作做好,那么驱动设备就应该阻塞这个请求的进程,使他陷入睡眠状态。

2.2 什么是睡眠

当一个进程处于睡眠态, 意味着它被移除调度队列,直到这个状态被改变之前,CPU都不会处理这个进程。有几个注意事项:

  • 不要在原子上下文中sleep
  • 无法保证精确的睡眠时间
  • 只有在确定其他进程/内核会唤醒自己时,才能睡眠

2.3 同步阻塞IO的编程模型

3. 同步阻塞IO的网络编程代码示例

/* A simple server in the internet domain using TCP
   The port number is passed as an argument */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>

void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
     int sockfd, newsockfd, portno;
     socklen_t clilen;
     char buffer[256];
     struct sockaddr_in serv_addr, cli_addr;
     int n;
     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0) 
        error("ERROR opening socket");
     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);
     if (bind(sockfd, (struct sockaddr *) &serv_addr,
              sizeof(serv_addr)) < 0) 
              error("ERROR on binding");
     listen(sockfd,5);
     clilen = sizeof(cli_addr);
     newsockfd = accept(sockfd, 
                 (struct sockaddr *) &cli_addr, 
                 &clilen);
     if (newsockfd < 0) 
          error("ERROR on accept");
     bzero(buffer,256);
     n = read(newsockfd,buffer,255);
     if (n < 0) error("ERROR reading from socket");
     printf("Here is the message: %s\n",buffer);
     n = write(newsockfd,"I got your message",18);
     if (n < 0) error("ERROR writing to socket");
     close(newsockfd);
     close(sockfd);
     return 0; 
}

4. API解释

4.1 sockaddr_in

这是socket地址的基本数据结构:

struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};

/* Internet address. */
struct in_addr {
    uint32_t       s_addr;     /* address in network byte order */
};

一个IP socket地址被定义为一个IP地址和16位的端口,基本IP协议不支持端口号,而是由上层协议udp/tcp来实现。

4.2 socket函数

int sockfd = socket(int socket_family, int socket_type, int protocol);
  • socket函数用来创建一个socket文件描述符。

  • 第一个参数是socket协议族,包括AF_INET, AF_IPX, AF_PACKET等,一般选择AF_INET,具体含义请参考文档

  • 第二个参数是socket类型,有SOCK_STREAM(TCP)和SOCK_DGRAM(UDP等等。除此之外,这个参数还有第二层含义,来描述socket的其他表现:

    SOCK_NONBLOCK   // 阻塞非阻塞(后面详细解释)
    SOCK_CLOEXEC    // 当开辟其他进程调用exec()族函数时,调用前为其释放对应的文件描述符。(跟网络编程关系不大)
    
  • 第三个参数protocol为socket指定了协议,正常情况下对于一个给定的协议族,只有一个支持的协议,所以填0即可。特殊情况参见文档。

4.3 bind函数

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

struct sockaddr {
    sa_family_t sa_family;
    char        sa_data[14];
}
  • 当socket通过socket函数创建好之后,他存在在一个地址协议族的名字空间中,但是没有为他分配地址,bind就是将一个地址分配给socket描述符。

  • 这里sa_data没有用,完全是为了将sockaddr_in强制转换时内存大小一样,不会报错。

错误码见文档。

4.4 listen函数

int listen(int sockfd, int backlog);
  • listen函数将这个socket标记为被动socket,意思是可以用accept来接收别人的连接请求。

  • backlog参数指定了等待连接队列的最大长度,当队列满了之后,新的客户端连接会收到一个ECONNREFUSED连接错误。

4.5 accept

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • accept函数用在面向连接的socket类型,例如SOCK_STREAM, SOCK_SEQPACKET,它会将请求等待队列的第一个连接请求提取出来,创建一个新的连接socket,返回这个socket的fd。

  • 第一个参数sockfd就是刚刚使用socket函数创建、使用bind绑定本地地址、并使用listen标记为被动的那个sockfd。

  • 第二个参数sockaddr指针指向一个客户端地址的结构体。

  • 第三个参数addrlen是一个传出的参数,必须先用sizeof(sockaddr)初始化,函数返回时会将其赋值成客户端地址这个结构体的真实大小。如果客户端地址结构体的大小大于sizeof(sockaddr),那么会被阶段,并在addrlen中带回真正的大小。

如果连接等待队列中没有连接请求,默认情况下accept()会阻塞,直到有新的客户端连接出现;如果对socket指定了非阻塞,那么会返回错误码EAGAIN 或者EWOULDBLOCK

4.6 read/recvwrite/send

int recv(int sockfd,void *buf,int len,int flags);
int send(int sockfd,void *buf,int len,int flags);

注意到上边代码中使用了read,实际上也可以使用recv,后者提供了第四个参数flag,用来标记一些socket的状态:

MSG_DONTROUTE:不查找路由表
MSG_OOB:接受或发送带外数据
MSG_PEEK:查看数据,并不从系统缓冲区移走数据
MSG_WAITALL :等待任何数据

其中:

  • MSG_DONTROUTE:是send函数使用的标志.这个标志告诉IP协议.目的主机在本地网络上面,没有必要查找路由表.这个标志一般用网络诊断和路由程式里面。
  • MSG_OOB:表示能够接收和发送带外的数据.关于带外数据我们以后会解释的.
  • MSG_PEEK:是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不清除系统缓冲区的内容。这样下次读的时候,仍然是相同的内容。一般在有多个进程读写数据时能够使用这个标志。
  • MSG_WAITALL:是recv函数的使用标志,表示等到任何的信息到达时才返回。使用这个标志的时候recv会一直阻塞,直到指定的条件满足,或是发生了错误。 1)当读到了指定的字节时,函数正常返回,返回值等于len 2)当读到了文档的结尾时,函数正常返回.返回值小于len 3)当操作发生错误时,返回-1,且配置错误为相应的错误号(errno).

write/send的区别同理。

5. 同步非阻塞IO

同步非阻塞IO是刚刚同步阻塞IO的一个聊胜于无的变种,设备以非阻塞方式打开,IO操作不成功会返回一个错误代码。关键问题是我们无法知道设备何时准备就绪能够操作成功,因此大多数情况不得不在循环中一遍一遍查询。

这种模式极为少用。

分享到:
评论

相关推荐

    linux网络编程-宋敬彬-part1

    第2章 Linux编程环境 14 2.1 Linux环境下的编辑器 14 2.1.1 vim使用简介 14 2.1.2 使用vim建立文件 15 2.1.3 使用vim编辑文本 16 2.1.4 vim的格式设置 18 2.1.5 vim配置文件.vimrc 19 2.1.6 使用其他...

    Linux多线程服务端编程:使用muduo C++网络库

    《Linux多线程服务端编程:使用muduo C++网络库》主要讲述采用现代C++在x86-64 Linux上编写多线程TCP网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread。...

    Linux学习笔记(四):信息量、同步、互斥和网络编程

    谁先抢到资源就谁先执行网络编程:服务器:server客户端:client代码及演示收获及感悟 复习 结构体:struct 文件I/O: int open(char *pathname,int flag); eg:int fd = open(“1.txt”,O_RDWR);//打开文件 if(){...

    Linux高性能服务器编程

    《Linux高性能服务器编程》共17章,分为3个部分:第一部分对Linux服务器编程的核心基础——TCP/IP协议进行了深入的解读和阐述,包括TCP/IP协议族、TCP/IP协议,以及一个经典的TCP/IP通信案例;第二部分对高性能...

    高性能IO模型浅析

    服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型。 (2)同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置...

    基于C++11线程池技术简单易用的轻量级网络编程框架源码.zip

    一个基于C++11的轻量级网络框架,基于线程池技术可以实现大并发网络IO 特点: 基于C++11开发,避免使用裸指针,代码稳定可靠;同时跨平台移植简单方便,代码清晰简洁。 使用epoll+线程池+异步网络IO模式开发,并发...

    linux从零基础系统编程开始视频教程.zip

    Day4 (文件IO、阻塞非阻塞、目录函数) Day5 (递归遍历目录、dup2、进程) Day6 (进程、进程控制、管道) Day7 (进程间通信) Day8 (信号、信号捕捉) Day9 (守护进程、线程、线程控制、线程属性) Day10 ...

    透析Linux系统编程

    本课程的主题是: 透析Linux系统编程    不同于市面上的课程,只会把核心主题放到函数的讲解,本课程的核心主题是 Linux系统编程的经验总结 ;    当你在系统编程中,调用一个系统调用来解决问题时,是否考虑过 该...

    linux网路编程 中文 23M 版

    第2 章Linux编程环境....................................................................................................14 2.1 Linux环境下的编辑器................................................. 14 ...

    关于嵌入式Linux系统开发学习流程详解

    一:嵌入式c语言 C语言是嵌入式领域重要也是主要的编程... 三:Linux系统编程 重点学习标准IO库,Linux多任务编程中的多进程和多线程,以及进程间通信(pipe、FIFO、消息队列、共享内存、signal、信号量等),同步

    java8stream源码-SharedBookmark:同步我的工作书签

    网络编程 多线程 单元测试 应用示例 JDBC 序列化 JVM IO NIO 集合 Map List 爬虫 String 正则表达式 日志 log4j Maven Eclipse excel (POI不可操作CSV文件) Spring Database mysql misc sql 隔离级别 join index ...

    [14本经典Android开发教程]-8-Linux内核阅读心得体会

    [14本经典Android开发教程] 8 Linux内核阅读心得体会 读核感悟 2 读核感悟 Linux内核启动 内核的生成 2 读核感悟 Linux内核启动 从hello world说起 3 读核感悟 Linux内核启动 BIOS 5 读核感悟 Linux内核启动 setup...

    Android驱动开发权威指南

    9.2 Linux中常用的同步访问技术 9.2.1中断屏蔽 9.2.2原子操作 9.2.3自旋锁 9.2.4信号量 9.2.5互斥灯 9.3增加并发控制的virtualchar驱动 第10章Linux设备的阻塞式与非阻塞式访问 10.1阻塞式与非阻塞式访问 10.1.1等待...

    ZLToolKit:一个基于C++11的轻量级网络框架,基于线程池技术可以实现大并发网络IO

    一个基于C++11简单易用的轻量级网络编程框架项目特点基于C++11开发,避免使用裸指针,代码稳定可靠;同时跨平台移植简单方便,代码清晰简洁。使用epoll+线程池+异步网络IO模式开发,并发性能优越。代码经过大量的...

    详解Linux多线程使用信号量同步

    本篇文章主要是介绍了Linux多线程使用信号量同步,详细讲诉了信号量的接口和使用,有需要的朋友可以了解一下。

    GEN卡使用介绍.ppt

    GEN网络运动控制器是一款基于EtherCAT总线的插卡式运动控制器,它集成了EtherCAT主站解决方案,可实现多达64轴的同步运动控制,同时支持gLink-I IO模块和EtherCAT IO模块扩展,为用户提供了多轴数、多IO点数的总线...

    2019千峰Python超详细入门教程(百度云盘分享).docx

    ├─千锋Python教程:第01章 第一个Python程序与数据存储及数据类型(9集) │ │ .DS_Store │ │ │ ├─code │ │ 1、数据存储.txt │ │ 2、第一个python程序.py │ │ 3、注释.py │ │ 4、输出与输入.py │ ...

    MyWebServer:linux下用c ++实现的可支持上万并发的web服务器

    一个linux系统下的C ++轻量级Web服务器,进行学习和实践网络编程。经测试支持上万并发连接。 C / C ++ B / S模型 React器事件处理模式 ET模式非双边IO的epoll 半同步/半React堆螺纹池 时间轮实现的定时器处理非...

    Cacher-crx插件

    –功能齐全的Web应用程序:app.cacher.io –支持编辑和查看100多种编程语言。 –灵活的颜色编码标签,用于对摘要进行分类。 –通过Cacher的代码共享社区共享的摘要页面:snippets.cacher.io –团队和组织功能,例如...

Global site tag (gtag.js) - Google Analytics