最近在学习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 .. "); 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 ...! ",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 ",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: "); 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]; }