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);
建立用于 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的庫函數,傳回的是一個指向檔案結構的指針。