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