前言
在最初接触网络这一领域的时候,就是傻傻地抱着一本TCP/IP协议详解来学习,主要学习协议的原理并研究协议相关的算法,大家都知道协议纯理论的学习是比较枯燥和复杂的,看着看着就睡着了。由于项目需要,没想到在没有经过实践的情况下经过半个多月的苦读,竟然真的将大部分协议理论给研究的差不多了,并且还被要求根据协议工作原理来设计测试算法。由于最开始项目只要求设计协议方面的算法,所以导致我在实现协议编程方面一片空白,在一次偶然的巧合下,我了解到有网络编程这一门学科,觉得它好像是为我量身定做一样,决心一定要学它,甚至可以不管它对于将来找工作有没有用都无所谓。于是立马去买了UNIX网络编程这一经典教材,在最初的几天翻了前几页后,就再也没有碰过它了,惭愧啊,直到现在重新拿起它。拖到现在主要有好几个原因:1觉得在以后的项目开发中肯定会涉及到,所以也没必要单独花时间去学;2.在买上Unix网络编程这本书之后,想利用业余时间去学习,可是平时基本没有时间来学这么复杂的东西,在深入学习前还需要配置源代码编译环境就直接让我跪了,再说我也不是那种有耐心的人。废话不多说了,进入正题吧。
1.环境配置
UNIX网络编程里面的例子都是在UNIX系统里运行的,其实在学习的时候也没必要去装个UNIX系统,用linux系统也一样可以,我就是装的windows/linux双系统。
相信看了这本书的人都看到了在每个例子前面都有句 #include “unp.h”这个头文件吧,这个是作者专门写的用于教材中所有例子的头文件,其实这也没什么奇怪的,他只是把socket编程用到的头文件及一些函数自行打包封装了一下,如果你不用 unp.h这个头文件,你也可以把需要的一个个自行加在你的程序中。其实我个人理解,如果你有编程基础的话,完全可以不用作者已经进行了一次封装的函数(如Socket,Accept,Listen),原因有两点:
一、按照作者的要求来配置环境有点麻烦,如果你不是很清楚整个程序结构的话,很可能会头晕,假如你百度到别人配置的步骤,你也配置成功了,有可能知其所以然而不知那样做的原因。
二、这点最重要,由于我们正处在学习阶段,所以一定要学系统给出的API,而不是经过别人封装过一次的API,如果你用习惯了书本上封装好了的包裹函数,你在其它环境编程时就可能导致一些不必要的错误和麻烦。为了方便我掌握知识和记忆,在学习的过程中我都坚持不用unp.h及作者提供的包裹函数,正因为这一点,我可以直接跳过我原先花了大量时间研究过的环境配置过程和网上下载的源代码。
老道理,理论学习不如直接看程序来得快,下面是经过修改后的获取服务器时间的两个程序。
client (注:端口必须是1024以后的,如果是root帐户可以选择1~1024间的数字作为端口)
1 #include<string.h> 2 #include <sys/types.h> 3 #include <sys/socket.h> 4 #include <sys/time.h> 5 #include <time.h> 6 #include <fcntl.h> 7 #include<netinet/in.h> 8 #include<arpa/inet.h> 9 #include <sys/errno.h> 10 #include<iostream> 11 #include<stdlib.h> 12 #include<stdio.h> //上面这几行可以用 #include "unp.h" 代替 13 using namespace std; 14 15 const int MAXLINE=1024; 16 int main(int argc,char** argv) 17 { 18 int sockfd,connectfd,n; 19 char recvline[MAXLINE+1]; 20 char sendline[MAXLINE+1]; 21 22 struct sockaddr_in servaddr; 23 if(argc!=2) 24 { 25 cout<<"usage:daytimecli <IPaddress> "<<endl; 26 exit(0); 27 } 28 sockfd=socket(AF_INET,SOCK_STREAM,0); 29 if(sockfd<0) //在实际项目的开发中,必须要进行这一步判断,看语句是否执行成功,教材中的包裹函数Socket()就是包含了创建及以下几行的判断过程。 30 { 31 cout<<"socket error"<<endl; 32 exit(0); 33 } 34 memset(&servaddr,0, sizeof(servaddr)); //bzero() ,可以在unp.h中看到该定义 35 servaddr.sin_family=AF_INET; 36 servaddr.sin_port=htons(1300); 37 if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<=0) 38 { 39 cout<<"inet_ptons error"<<endl; 40 exit(0); 41 } 42 connectfd = connect(sockfd,(sockaddr*)&servaddr,sizeof(servaddr)); 43 if(connectfd < 0) 44 { 45 cout<<"connect error : "<<connectfd<<endl; 46 exit(0); 47 } 48 while((n=read(sockfd,recvline,MAXLINE))>0) 49 { 50 recvline[n]=0; 51 snprintf(sendline,strlen("client send string!\n"),"client send string!\n"); 52 write(sockfd,sendline,strlen(sendline)); //相当于winsock的send() 53 if(fputs(recvline,stdout)==EOF) //fputs(str,file) 将str输入到文件,该句相当于 cout << recvline; 54 { 55 cout<<"fputs error"<<endl; 56 exit(0); 57 } 58 } 59 if(n<0) 60 { 61 cout<<"read error"<<endl; 62 exit(0); 63 } 64 exit(0); 65 }
Server 端
1 #include<string.h> 2 #include <sys/types.h> 3 #include <sys/socket.h> 4 #include <sys/time.h> 5 #include <time.h> 6 #include <fcntl.h> 7 #include<netinet/in.h> 8 #include<arpa/inet.h> 9 #include <sys/errno.h> 10 #include<iostream> 11 #include<stdlib.h> 12 #include<stdio.h> 13 #include<errno.h> 14 using namespace std; 15 16 const int MAXLINE = 2048; 17 18 int main(int argc, char **argv) 19 { 20 int listenfd,connfd,bindfd; 21 struct sockaddr_in servaddr; 22 char buff[MAXLINE],recvbuff[MAXLINE]; 23 time_t ticks; 24 25 listenfd = socket(AF_INET,SOCK_STREAM,0); 26 if (listenfd < 0) 27 { 28 cout << "listen error : "<<strerror(errno) << endl; 29 exit(0); 30 } 31 memset(&servaddr,0, sizeof(servaddr)); 32 servaddr.sin_family = AF_INET; 33 servaddr.sin_port = htons(1300); 34 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 35 36 bindfd=bind(listenfd, (sockaddr*)&servaddr,sizeof(servaddr)); 37 if (bindfd < 0) 38 { 39 cout << "bind error : " << bindfd << endl; 40 } 41 listen(listenfd, 5); 42 while(1) { 43 connfd = accept(listenfd,(sockaddr*)NULL,NULL); 44 ticks = time(NULL); 45 snprintf(buff,sizeof(buff),"%.24s\r\n",ctime(&ticks)); 46 cout << "before write,buff : " << buff << endl; 47 write(connfd,buff,strlen(buff)); 48 cout << "after write,buff : " << buff << endl; 49 strcat(buff," test for socket!\n" ); 50 cout << "after test write,buff : " << buff << endl; 51 write(connfd,buff,strlen(buff)); 52 cout << "before read,recvbuff : " << recvbuff << endl; 53 int n=read(connfd,recvbuff,strlen(buff)); 54 cout << "after read,recvbuff : " << recvbuff << endl; 55 cout << "read n = "<<n <<"length recvbuff = " << strlen(recvbuff) <<endl; 56 close(connfd); 57 } 58 }
哎,折腾了半个小时也没有找到linux下的抓图工具,也没法上程序运行结果了,有时间就自己去测试下吧,这个程序保证能运行成功。