1.網絡編程和套接字編程
網絡編程與C語言中的printf函數和scanf函數以及文件的輸入輸出相似,本質上也是一種基於I/O的編程方法。之因此這麼說,是由於網絡編程大可能是基於套接字(socket,網絡數據傳輸的軟件設備,操做系統爲咱們提供的編程接口)來實現數據的輸入輸出的。服務器
套接字通訊過程能夠類比打電話的過程。電話機能夠用來拔打和接聽,但對於套接字而言,拔打和接聽是有區別的。網絡
構建接電話套接字dom
調用socket函數安裝電話機socket
#include <sys/socket.h> int socket(int domain, int type, int protocol); -> 成功時返回文件描述符,失敗時返回-1
調用bind函數分配電話號碼ide
#include <sys/socket.h> int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen); -> 成功時返回0,失敗時返回-1
調用listen函數鏈接電話函數
#include <sys/socket.h> int listen(int sockfd, int backlog); -> 成功時返回0,失敗時返回-1
調用accept函數接聽電話spa
#include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); -> 成功時返回文件描述符,失敗時返回-1
網絡編程中接受鏈接請求的套接字建立過程總結以下:操作系統
1.調用socket函數建立套接字3d
2.調用bind函數分配IP地址和端口號
3.調用了listen函數轉換爲可接收請求狀態
4.調用accept函數受理鏈接請求
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <arpa/inet.h> 6 #include <sys/socket.h> 7 8 void error_handling(char *message); 9 10 int main(int argc, char *argv[]) 11 { 12 int serv_sock; 13 int clnt_sock; 14 15 struct sockaddr_in serv_addr; 16 struct sockaddr_in clnt_addr; 17 socklen_t clnt_addr_size; 18 19 char message[]="Hello World!"; 20 21 if(argc!=2){ 22 printf("Usage : %s <port>\n", argv[0]); 23 exit(1); 24 } 25 26 serv_sock=socket(PF_INET, SOCK_STREAM, 0); 27 if(serv_sock == -1) 28 error_handling("socket() error"); 29 30 memset(&serv_addr, 0, sizeof(serv_addr)); 31 serv_addr.sin_family=AF_INET; 32 serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); 33 serv_addr.sin_port=htons(atoi(argv[1])); 34 35 if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))==-1 ) 36 error_handling("bind() error"); 37 38 if(listen(serv_sock, 5)==-1) 39 error_handling("listen() error"); 40 41 clnt_addr_size=sizeof(clnt_addr); 42 clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_addr,&clnt_addr_size); 43 if(clnt_sock==-1) 44 error_handling("accept() error"); 45 46 write(clnt_sock, message, sizeof(message)); 47 close(clnt_sock); 48 close(serv_sock); 49 return 0; 50 } 51 52 void error_handling(char *message) 53 { 54 fputs(message, stderr); 55 fputc('\n', stderr); 56 exit(1); 57 }
構建打電話套接字
服務器端建立的套接字又稱爲服務器端套接字或監聽(listening)套接字。而客戶端套接字則比較簡單,除了建立套接字外,只須要鏈接過程便可。
客戶端發起打電話動做
#include <sys/socket.h> int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen); -> 成功時返回0,失敗時返回-1
網絡編程中發出鏈接請求的套接字建立過程總結以下:
1.調用socket函數建立套接字
2.調用connect函數發出鏈接請求
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <arpa/inet.h> 6 #include <sys/socket.h> 7 8 void error_handling(char *message); 9 10 int main(int argc, char* argv[]) 11 { 12 int sock; 13 struct sockaddr_in serv_addr; 14 char message[30]; 15 int str_len; 16 17 if(argc!=3){ 18 printf("Usage : %s <IP> <port>\n", argv[0]); 19 exit(1); 20 } 21 22 sock=socket(PF_INET, SOCK_STREAM, 0); 23 if(sock == -1) 24 error_handling("socket() error"); 25 26 memset(&serv_addr, 0, sizeof(serv_addr)); 27 serv_addr.sin_family=AF_INET; 28 serv_addr.sin_addr.s_addr=inet_addr(argv[1]); 29 serv_addr.sin_port=htons(atoi(argv[2])); 30 31 if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1) 32 error_handling("connect() error!"); 33 34 str_len=read(sock, message, sizeof(message)-1); 35 if(str_len==-1) 36 error_handling("read() error!"); 37 38 printf("Message from server: %s \n", message); 39 close(sock); 40 return 0; 41 } 42 43 void error_handling(char *message) 44 { 45 fputs(message, stderr); 46 fputc('\n', stderr); 47 exit(1); 48 }
2.基於Linux的文件操做
在Linux的世界裏,socket也被認爲是文件的一種,所以在網絡數據傳輸過程當中天然可使用文件I/O相關函數。操做文件或是套接字,首先須要瞭解什麼是文件描述符?文件描述符是一種系統資源,一般是在文件和套接字建立過程由系統分配的一個整數。三個知名文件描述符(標準輸入輸出及標準錯誤文件描述符)以下,它們不通過特殊的建立過程,而是伴隨系統運行而自動分配的。
下面介紹四個經常使用文件操做函數
//打開文件 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *path, int flag); -> 成功時返回文件描述符,失敗時返回-1 path: 文件名的字符串地址 flag : 文件打開模式信息
其中,文件打開模式經常使用值以下,能夠經過位或運算傳遞多個值
//關閉文件 #include <unistd.h> int close(int fd); -> 成功時返回0,失敗時返回-1 fd: 須要關閉的文件或套接字的文件描述符
若調用此函數並傳入文件描述符,則關閉相應文件,且此函數同時能夠關閉套接字。
//寫文件 #include <unistd.h> ssize_t write(int fd, const char *buf, size_t nbytes); -> 成功時返回寫入的字節數,失敗時返回-1
write函數用於向文件輸入數據,固然,經過套接字向其餘計算機傳輸數據亦可以使用該函數。
//讀文件 #include <unistd.h> ssize_t read(int fd, char *buf, size_t nbytes); -> 成功時返回讀出的字節數(遇到文件結尾則返回0),失敗時返回-1
read函數用於從文件讀出數據,一樣,經過套接字從其餘計算機接收數據亦可以使用該函數。
文件描述符與套接字的關係
經過觀察下面程序運行結果可知,描述符從3開始從小到大的順序編號,再次說明了Linux系統中套接字與文件並沒有差異。
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <sys/socket.h> int main(void) { int fd1, fd2, fd3; fd1=socket(PF_INET, SOCK_STREAM, 0); fd2=open("test.dat", O_CREAT|O_WRONLY|O_TRUNC); fd3=socket(PF_INET, SOCK_DGRAM, 0); printf("file descriptor 1: %d\n", fd1); printf("file descriptor 2: %d\n", fd2); printf("file descriptor 3: %d\n", fd3); close(fd1); close(fd2); close(fd3); return 0; }
一些思考:
1.INADDR_ANY是什麼地址?
2.sockaddr_in與socaddr的關係?
3.爲何客戶端不須要bind函數將IP信息綁定到套接字?
4.服務端的阻塞函數是什麼?
5.listen函數中,監聽套接字參數sockfd的做用?與accept返回的套接字參數sockfd之間的關係?
6.listen函數中,參數backlog怎麼理解?
以後系列文章中我會結合書中涉及的內容以及本身的理解給出給出參考