最近在學習Linux網絡編程方面的知識,感覺還是有些困難。主要是對協議過程的理解,還有socket的API的理解不夠深刻。今天復習編寫了一個TCP的服務端和客戶端的程序實現client.c從命令行參數中獲得一個字符串發給服務器,然后接收服務器返回的已處理的字符串並打印。
server.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <sys/socket.h> 6 #include <netinet/in.h> 7 #include <arpa/inet.h> 8 9 #define MAXLINE 80 10 #define SERV_PORT 8000 11 12 int main(void) 13 { 14 struct sockaddr_in servaddr,cliaddr; //IPV4的地址結構 15 socklen_t cliaddr_len; 16 int listenfd,connfd; 17 char buf[MAXLINE]; 18 char str[INET_ADDRSTRLEN]; 19 int i,n; 20 21 if(-1 == (listenfd = socket(AF_INET,SOCK_STREAM,0))) //對於IPV4的family的參數為AF_INET for TCP SOCK_STREAM 表示面向流的傳輸協議 22 { 23 perror("socket Error"); 24 exit(1); 25 } 26 bzero(&servaddr,sizeof(servaddr)); //對於UDP協議 type is SOCK_DGRAM 表示面向數據報的傳輸協議 protocol指定為零 27 servaddr.sin_family = AF_INET; //設置地址類型 28 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//網絡地址為INADDR_ANY 29 servaddr.sin_port = htons(SERV_PORT); //端口號為SERV_PORT 定義為8000 30 31 if(-1 == bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))) 32 { 33 perror("Bind error"); 34 exit(1); 35 } 36 37 if( -1 == listen(listenfd,20)) 38 { 39 perror("Listen error"); 40 exit(1); 41 } 42 43 printf("Accepting connections ..\n"); 44 45 while(1){ 46 cliaddr_len = sizeof(struct sockaddr_in); 47 if( -1 == (connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddr_len))) 48 { 49 perror("Accept error"); 50 exit(1); 51 } 52 53 if(-1 ==(n = read(connfd,buf,MAXLINE))) 54 { 55 perror("read error"); 56 exit(1); 57 } 58 printf("Connect from %s:%u ...!\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port)); 59 60 for (i = 0; i < n; i++) 61 buf[i] = toupper(buf[i]); 62 write(connfd, buf, n); 63 64 close(connfd); 65 } 66 close(listenfd); 67 exit(0); 68 }
client.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <sys/socket.h> 6 #include <netinet/in.h> 7 8 #define MAXLINE 80 9 #define SERV_PORT 8000 10 11 int main(int argc,char **argv) 12 { 13 struct sockaddr_in servaddr; 14 char buf[MAXLINE]; 15 int sockfd,n; 16 char *str; 17 18 if(argc != 2) 19 { 20 fputs("Usage: ./client message\n",stderr); 21 exit(1); 22 } 23 str = argv[1]; 24 sockfd = socket(AF_INET,SOCK_STREAM,0); 25 bzero(&servaddr,sizeof(servaddr)); 26 servaddr.sin_family = AF_INET; 27 inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr); 28 servaddr.sin_port = htons(SERV_PORT); 29 30 connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)); 31 write(sockfd,str,strlen(str)); 32 n = read(sockfd,buf,MAXLINE); 33 printf("Response from server:\n"); 34 35 //if(-1 == read(sockfd,buf,1024)){ 36 // perror("Recv Error:"); 37 //} 38 write(STDOUT_FILENO, buf, n); 39 40 close(sockfd); 41 return 0; 42 }
但是發現一直有個Segmentation fault顯示沒有返回正確的結果,一開始還以為是數組buf越界了,但檢查總是沒有什么問題。
[root@VM_62_27_centos changeBigSmall]# ./server
Accepting connections ..
Segmentation fault
[root@VM_62_27_centos changeBigSmall]# ./client abcdef
Response from server:
[root@VM_62_27_centos changeBigSmall]#
發現也沒有出現perror()的錯誤還有printf一直沒有打印連接的端口號,連接成功理應由成功連接的地址端口顯示出來。最后對函數inet_ntoa()查詢是發現要加入<arpa/inet.h> 的頭文件。哦NO,原來沒有加入相應的頭文件也會引起段錯誤。
加上就好了<arpa/inet.h>的頭文件就好了
[root@VM_62_27_centos changeBigSmall]# ./client abcd
Response from server:
ABCD[root@VM_62_27_centos changeBigSmall]#
那經過此次教訓之后,還是總結一下網絡編程中常見的頭文件。
1 sys/types.h:數據類型定義 2 3 sys/socket.h:提供socket函數及數據結構 4 5 netinet/in.h:定義數據結構sockaddr_in 6 7 arpa/inet.h:提供IP地址轉換函數 8 9 netdb.h:提供設置及獲取域名的函數 10 11 sys/ioctl.h:提供對I/O控制的函數 12 13 sys/poll.h:提供socket等待測試機制的函數
其他常見的頭文件
unistd.h:提供通用的文件、目錄、程序及進程操作的函數 errno.h:提供錯誤號errno的定義,用於錯誤處理 fcntl.h:提供對文件控制的函數 time.h:提供有關時間的函數 crypt.h:提供使用DES加密算法的加密函數 pwd.h:提供對/etc/passwd文件訪問的函數 shadow.h:提供對/etc/shadow文件訪問的函數 pthread.h:提供多線程操作的函數 signal.h:提供對信號操作的函數 sys/wait.h、sys/ipc.h、sys/shm.h:提供進程等待、進程間通訊(IPC)及共享內存的函數
還有一些常見的結構體
struct sockadd { unsigned short sa_family; char sa_data[14]; } struct sockaddr_in { short int sin_family; //AF_INET unsigned short int sin_port; //網絡字節順序 struct in_addr sin_addr; //struct in_addr { unsigned long s_addr; } unsigned char sin_zero[8]; }