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 " "Host: www.baidu.com " " "; 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
",inet_ntoa(ip_protocol->ip_dst_address),ntohs(tcp_protocol->tcp_dst_port));
Socket 编程
1. int
client_socket = socket(AF_INET,SOCK_STREAM,0);
说明一下: 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的库函数,返回的是一个指向文件结构的指针。