C++ 异步IO (一) 阻塞式HTTP客户端

An IO call is synchronous if, when you
call it, it does not return until the operation is completed, or until enough
time has passed that your network stack gives up.

简单来说,IO的函数后面的代码不会被执行,除非IO函数已返回,或函数超时了。


/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For gethostbyname */
#include <netdb.h>

#include <unistd.h>
#include <string.h>
#include <stdio.h>

int main(int c, char **v)
{
const char query[] =
"GET / HTTP/1.0\r\n" "Host: www.baidu.com\r\n" "\r\n";
const char hostname[] = "www.baidu.com";
struct sockaddr_in sin;
struct hostent *h;
const char *cp;
int fd;
ssize_t n_written, remaining;
char buf[1024];

/* Look up the IP address for the hostname. Watch out; this isn‘t
threadsafe on most platforms. */
h = gethostbyname(hostname);
if (!h) {
fprintf(stderr, "Couldn‘t lookup %s: %s", hostname, hstrerror(h_errno));
return 1;
}
if (h->h_addrtype != AF_INET) {
fprintf(stderr, "No ipv6 support, sorry.");
return 1;
}

/* Allocate a new socket */
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
return 1;
}

/* Connect to the remote host. */
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
sin.sin_addr = *(struct in_addr*)h->h_addr;
if (connect(fd, (struct sockaddr*) &sin, sizeof(sin))) {
perror("connect");
close(fd);
return 1;
}

/* Write the query. */
/* XXX Can send succeed partially? */
cp = query;
remaining = strlen(query);
while (remaining) {
n_written = send(fd, cp, remaining, 0);
if (n_written <= 0) {
perror("send");
return 1;
}
remaining -= n_written;
cp += n_written;
}

/* Get an answer back. */
while (1) {
ssize_t result = recv(fd, buf, sizeof(buf), 0);
if (result == 0) {
break;
} else if (result < 0) {
perror("recv");
close(fd);
return 1;
}
fwrite(buf, 1, result, stdout);
}

close(fd);
return 0;
}

All of the network calls in the code above are blocking: the
gethostbyname does not return until it has succeeded or failed in resolving
www.baidu.com; the connect does not return until it has connected; the recv
calls do not return until they have received data or a close; and the send call
does not return until it has at least flushed its output to the kernel’s write
buffers.

sockaddr_in 和 sockaddr 这两个结构体用来处理网络通信地址。在各种系统调用中,只要和网络地址打交道,就得用到这两个结构体。

网络地址包含三方面的属性

1. 地址类型: IPV4 (AF_INET) IPV6

2. IP 地址

3. 端口号


include <netinet/in.h>

struct sockaddr {
unsigned short sa_family; // 2 bytes address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};

// IPv4 AF_INET sockets:

struct sockaddr_in {
short sin_family; // 2 bytes e.g. AF_INET, AF_INET6
unsigned short sin_port; // 2 bytes e.g. htons(3490)
struct in_addr sin_addr; // 4 bytes see struct in_addr, below
char sin_zero[8]; // 8 bytes zero this if you want to
};

struct in_addr {
unsigned long s_addr; // 4 bytes load with inet_pton()
};

这两个结构体大小是一样的,都是16字节。不同之处是sockaddr_in将端口号和IP地址分开, 最好还用8个填充字节和sockaddr大小一样。

程序员不应该直接使用 sockaddr, 它是给操作系统用的。

程序员应该使用sockaddr_in来表示地址,它区分了端口号和IP地址,语义性更强。

一般的用法:

程序员把类型,IP地址,端口填充sockaddr_in结构体,然后强制转换成sockaddr,作为参数传递给调用函数。

一段典型代码:


int sockfd;
struct sockaddr_in servaddr;

sockfd = Socket(AF_INET, SOCK_STREAM, 0);

/* 填充struct sockaddr_in */
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);

/* 强制转换成struct sockaddr */
connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

struct hostent* gethostbyname(const char* name)

这个函数的传入值是主机名或域名,返回值是hostent结构体。如果函数调用失败,返回NULL。


struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
};

h_addrtype 返回是IPV4还是IPV6, h_length 返回IP地址的长度。

h_addr_list返回的是主机IP地址, 打印IP地址时需要inet_ntop()

const char *inet_ntop(int af, const void *src, char *dst, socklen_t
cnt) :

这个函数将类型为af的网络地址结构src转化成主机序的字符串形式,存放在长度为cnt的字符串中。

inet_ntoa 和 inet_ntop 的区别

inet_ntop 是线程安全的。使用 inet_ntoa 的话,就不能在同一个函数的几个参数里出现两次inet_ntoa,
或者第一个inet_ntoa结束之前不能使用第二个,亦或者使用两个数组保存地址

printf("%s:%d--->",inet_ntoa(ip_protocol->ip_src_address),ntohs(tcp_protocol->tcp_src_port)
);
printf("%s:%d\n",inet_ntoa(ip_protocol->ip_dst_address),ntohs(tcp_protocol->tcp_dst_port));

Socket 编程

1. int client_socket = socket(AF_INET,SOCK_STREAM,0);

创建用于 internet的流协议(TCP)socket, 用 client_socket 代表客户机
socket

2. connect(client_socket, (struct sockaddr*) &sin,
sizeof(sin)))

向服务器发起连接,连接成功后client_socket代表了客户机到服务器的一个socket连接

3. int written = send(client_socket,buffer,BUFFER_SIZE,0);

向服务器发送buffer中的数据, 如果written 记录发送成功的字节数

4. ssize_t result = recv(client_socket,
buf, sizeof(buf), 0);

接收服务器的回复,result返回写入buf的字节数

文件操作

1. fwrite(buf, 1, result, stdout);

将buf中的数据写入标准输出

说明一下:
fopen, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, freopen, fseek, ftell, rewind等
缓冲文件系统
缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用,当执行读文件的操作时,
从磁盘文件将数据先读入内存“缓冲区”, 装满后再从内存“缓冲区”依此读入接收的变量。执行写文件的
操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。由此可以看出,内存
“缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区”越大,则操作外存的次数就少,
执行速度就快、效率高。一般来说,文件“缓冲区”的大小随机器 而定。

open, close, read, write, getc, getchar, putc, putchar 等
非缓冲文件系统
非缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问,既可以读写字符、
字符串、格式化数据,也可以读写二进制数 据。非缓冲文件系统依赖于操作系统,通过操作系统的功能对
文件进行读写,是系统级的输入输出,它不设文件结构体指针,只能读写二进制文件,但效率高、速度快,
由于ANSI标准不再包括非缓冲文件系统,因此建议大家最好不要选择它。

open等属于低级IO,
fopen等是高级IO。

open等返回一个文件描述符(用户程序区的),
fopen等返回一个文件指针。

open等无缓冲,fopen等有缓冲。

fopen等是在open等的基础上扩充而来的,在大多数情况下,用fopen等。

open 是系统调用 返回的是文件句柄,文件的句柄是文件在文件描述符表里的索引,
fopen是C的库函数,返回的是一个指向文件结构的指针。

C++ 异步IO (一) 阻塞式HTTP客户端,布布扣,bubuko.com

时间: 2024-09-30 11:53:38

C++ 异步IO (一) 阻塞式HTTP客户端的相关文章

同步IO,异步IO,阻塞IO,非阻塞IO的联系与区别

转载 POSIX 同步IO.异步IO.阻塞IO.非阻塞IO,这几个词常见于各种各样的与网络相关的文章之中,往往不同上下文中它们的意思是不一样的,以致于我在很长一段时间对此感到困惑,所以想写一篇文章整理一下. POSIX(可移植操作系统接口)把同步IO操作定义为导致进程阻塞直到IO完成的操作,反之则是异步IO 按POSIX的描述似乎把同步和阻塞划等号,异步和非阻塞划等号,但是为什么有的人说同步IO不等于阻塞IO呢?先来说说几种常见的IO模型吧. IO模型 这里统一使用Linux下的系统调用recv

转 网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

Stevens在文章中一共比较了五种IO Model:    * blocking IO    * nonblocking IO    * IO multiplexing    * signal driven IO    * asynchronous IO    由signal driven IO在实际中并不常用,所以主要介绍其余四种IO Model. http://www.cnblogs.com/Forever-Kenlen-Ja/p/4480718.html

Java基础:非阻塞式IO

转载请注明出处:jiq?钦's technical Blog 引言 JDK1.4中引入了NIO,即New IO,目的在于提高IO速度.特别注意JavaNIO不完全是非阻塞式IO(No-Blocking IO),因为其中部分通道(如FileChannel)只能运行在阻塞模式下,而其他的通道可以在阻塞式和非阻塞式之间进行选择. 尽管这样,我们还是习惯将Java NIO看作是非阻塞式IO,而前面介绍的面向流(字节/字符)的IO类库则是非阻塞的,详细来看,两者区别如下: IO NIO 面向流(Strea

javaNIO原理(含代码)及与 同步阻塞IO 、伪异步IO比较

一.同步阻塞IO BIO就是阻塞式的IO,网络通信中对于多客户端的连入,服务器端总是与客户端数量一致的线程去处理每个客户端任务,即,客户端与线程数1:1,并且进行读写操作室阻塞的,当有你成千上完的客户端进行连接,就导致服务器不断的建立新的线程,最后导致低通资源不足,后面的客户端不能连接服务器,并且连接入的客户端并不是总是在于服务器进行交互,很可能就只是占用着资源而已. 二.伪异步IO 伪异步IO对同步IO进行了优化,后端通过一个线程池和任务队列去处理所有客户端的请求,当用完后在归还给线程池,线程

python(十)下:事件驱动与 阻塞IO、非阻塞IO、IO多路复用、异步IO

上节的问题: 协程:遇到IO操作就切换. 但什么时候切回去呢?怎么确定IO操作完了? 一.事件驱动模型介绍 通常,我们写服务器处理模型的程序时,有以下几种模型: (1)每收到一个请求,创建一个新的进程,来处理该请求: (2)每收到一个请求,创建一个新的线程,来处理该请求: (3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求 第三种就是协程.时间驱动的方式,一般普遍认为第(3)种方式是大多数网络服务器采用的方式 论事件驱动模型 在UI编程中,,常常要对鼠标点击进行相应,

理论铺垫:阻塞IO、非阻塞IO、IO多路复用/事件驱动IO(单线程高并发原理)、异步IO

完全来自:http://www.cnblogs.com/alex3714/articles/5876749.html 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network IO. 一 概念说明 在进行解释之前,首先要说明几个概念:- 用户空间和内核空间- 进程切换- 进程的阻塞- 文件描述符- 缓存 I/O 用户空间与内核空间 现在操作系统都是采用虚拟存储器,

Python_Day11_同步IO和异步IO

同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network IO. 一 概念说明 在进行解释之前,首先要说明几个概念:- 用户空间和内核空间- 进程切换- 进程的阻塞- 文件描述符- 缓存 I/O 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心是内核,独立于普通的应用程

伪异步IO理解

伪异步IO实在阻塞IO的基础上将每一个客户端发送过来的请求由新创建的线程来处理改进为用线程池来处理,因此避免了为每一个客户端请求创建一个新线程造成的资源耗尽问题. 来看一下伪异步IO的服务端代码: 线程池类 import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; impor

同步IO和异步IO

链接: 同步IO和异步IO socket阻塞与非阻塞,同步与异步.I/O模型 Linux的IO系统常用系统调用及分析 linux异步IO的两种方式