fggets和fputs这两个函数来自标准I/O函数库,writen和readline见my_unp.h头文件
//my_unp.h #include<stdarg.h> #include<syslog.h> #include<stdio.h> #include<netinet/in.h>//网络地址结构表示库 #include<sys/socket.h>//TCP套接口库(socket connet bind listen accept) #include<unistd.h>//Unix标准库(fork exec close) #include<stdlib.h>//标准库 #include<memory.h>//内存管理 #include<errno.h>//错误号头文件 #include<string.h>//字符串处理 #include<arpa/inet.h>//(inet_pton inet_ntop) #define MAXLINE 2048 #define SERV_PORT 9877 #define LISTENQ 5 #define bzero(ptr,n) memset(ptr,0,n) int daemon_proc; static void err_doit(int, int, const char*, va_list); void err_sys(const char *fmt, ...) { va_list ap; va_start(ap,fmt); err_doit(1,LOG_ERR,fmt,ap); va_end(ap); exit(1); } static void err_doit(int errnoflag, int level, const char *fmt, va_list ap) { int errno_save,n; char buf[MAXLINE+1]; errno_save=errno; vsnprintf(buf,MAXLINE,fmt,ap); n=strlen(buf); if(errnoflag) snprintf(buf+n,MAXLINE-n,":%s",strerror(errno_save)); strcat(buf,"\n"); if(daemon_proc) { syslog(level,buf); } else { fflush(stdout); fputs(buf,stderr); fflush(stderr); } return; } ssize_t writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwriten; const char *ptr; ptr=(const char*)vptr; nleft=n; while(nleft>0) { if((nwriten=write(fd,ptr,nleft))<0) { if(nwriten<0 && errno==EINTR) nwriten=0; else return(-1); } nleft-=nwriten; ptr+=nwriten; } return(n); } ssize_t readline(int fd,void *vptr,size_t maxlen) { ssize_t n,rc; char c,*ptr; ptr=(char*)vptr; for(n=1;n<maxlen;n++) { again: if((rc=read(fd,&c,1))==1) { *ptr++=c; if(c=='\n') break; } else if(rc==0) { *ptr=0; return(n-1); } else { if(errno==EINTR) goto again; return(-1); } } *ptr=0; return(n); } void err_quit(const char *fmt, ...) { va_list ap; va_start(ap,fmt); err_doit(0,LOG_ERR,fmt,ap); va_end(ap); exit(1); }
#include"my_unp.h" void str_echo(int sockfd) { ssize_t n; char buf[MAXLINE]; again: while((n=read(sockfd,buf,MAXLINE))>0) writen(sockfd,buf,n); if( n<0 && errno==EINTR ) goto again; else if(n<0) err_sys("str_echo:read error"); } int main(int argc, char **argv) { int listenfd,connfd; pid_t childpid; socklen_t clilen; struct sockaddr_in cliaddr,servaddr; listenfd=socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family=AF_INET; servaddr.sin_addr.s_addr=htonl(INADDR_ANY); servaddr.sin_port=htons(SERV_PORT); bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)); listen(listenfd,LISTENQ); for(;;) { clilen=sizeof(cliaddr); connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&clilen); if((childpid=fork())==0) { close(listenfd); str_echo(connfd); exit(0); } close(connfd); } }
并发服务器的调用流程如右图所示
#include"my_unp.h" void str_cli(FILE *fp, int sockfd) { char sendline[MAXLINE],recvline[MAXLINE]; while(fgets(sendline,MAXLINE,fp)!=NULL) { writen(sockfd,sendline,strlen(sendline)); if(readline(sockfd,(void*)recvline,MAXLINE)==0) err_quit("str_cli:server terminated prematurely"); fputs(recvline,stdout); } } int main(int argc,char **argv) { int sockfd; struct sockaddr_in servaddr; if(argc!=2) err_quit("usage : tcpcli<IPaddress>"); sockfd=socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family=AF_INET; servaddr.sin_port=htons(SERV_PORT); inet_pton(AF_INET,argv[1],&servaddr.sin_addr); connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)); str_cli(stdin,sockfd); exit(0); }
正常启动过程
我们首先在一台Linux主机上启动服务器,先用g++编译,然后./serv运行服务器
我们可以查询一下网卡的IP地址,发现服务器的IP为192.168.255.129
我们这里是用VMware装了两个LINUX的虚拟机
网络的连接方式全是
检查一下服务器监听端口的状态
打开另一个LINUX主机,并指定IP地址为192.168.255.129,如上图ifconfig查询所示
编译客户端程序
运行客户端并开始测试
断开之前,再检查一下服务器的监听套结口连接状态
可以看到0.0.0.0:9877为监听套接口,状态为LISTEN
192.168.288.129:9877 到192.168.255.130:35635为已连接套接口,状态为ESTABLISHED,关闭时会变成TIME_WAIT,大概等待1~4分钟之后会完全断开
抓包分析结果
第一段为三次握手
第二段为hello,world发送过程
第三段为good bye
第四段为断开连接
#include<stdarg.h> #include<syslog.h> #include<stdio.h> #include<netinet/in.h> #include<sys/socket.h>//TCP套接口库(socket connet bind listen accept) #include<unistd.h>//Unix标准库(fork exec close) #include<stdlib.h> #include<memory.h> #include<errno.h> #include<string.h> #include<arpa/inet.h>//(inet_pton inet_ntop) #include<signal.h>//信号处理库 #include<sys/types.h>//使用wait和waitpid #include<sys/wait.h> #define MAXLINE 2048 #define SERV_PORT 9877 #define LISTENQ 5 #define bzero(ptr,n) memset(ptr,0,n) typedef void Sigfunc(int); int daemon_proc; static void err_doit(int, int, const char*, va_list); void err_sys(const char *fmt, ...) { va_list ap; va_start(ap,fmt); err_doit(1,LOG_ERR,fmt,ap); va_end(ap); exit(1); } static void err_doit(int errnoflag, int level, const char *fmt, va_list ap) { int errno_save,n; char buf[MAXLINE+1]; errno_save=errno; vsnprintf(buf,MAXLINE,fmt,ap); n=strlen(buf); if(errnoflag) snprintf(buf+n,MAXLINE-n,":%s",strerror(errno_save)); strcat(buf,"\n"); if(daemon_proc) { syslog(level,buf); } else { fflush(stdout); fputs(buf,stderr); fflush(stderr); } return; } ssize_t writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwriten; const char *ptr; ptr=(const char*)vptr; nleft=n; while(nleft>0) { if((nwriten=write(fd,ptr,nleft))<0) { if(nwriten<0 && errno==EINTR) nwriten=0; else return(-1); } nleft-=nwriten; ptr+=nwriten; } return(n); } ssize_t readline(int fd,void *vptr,size_t maxlen) { ssize_t n,rc; char c,*ptr; ptr=(char*)vptr; for(n=1;n<maxlen;n++) { again: if((rc=read(fd,&c,1))==1) { *ptr++=c; if(c=='\n') break; } else if(rc==0) { *ptr=0; return(n-1); } else { if(errno==EINTR) goto again; return(-1); } } *ptr=0; return(n); } void err_quit(const char *fmt, ...) { va_list ap; va_start(ap,fmt); err_doit(0,LOG_ERR,fmt,ap); va_end(ap); exit(1); } //包裹函数 char *Fgets(char *ptr, int n, FILE *stream) { char *rptr = fgets(ptr, n, stream); if ( rptr == NULL && ferror(stream) ) err_quit("fgets error"); return rptr; } void Fputs(const char *ptr, FILE *stream) { if (fputs(ptr, stream) == EOF) err_quit("fputs error"); } ssize_t Readline(int fd, void *ptr, size_t maxlen) { ssize_t n = readline(fd, ptr, maxlen); if ( n < 0) err_quit("readline error"); return n; } void Writen(int fd, void *ptr, size_t nbytes) { if ( writen(fd, ptr, nbytes) != nbytes ) err_quit("writen error"); }
#include"my_unp.h" //服务器程序 //回射程序 void str_echo(int sockfd) { ssize_t n; char buf[MAXLINE]; again: while((n=readline(sockfd,buf,MAXLINE))>0) writen(sockfd,buf,n); if( n<0 && errno==EINTR ) goto again; else if(n<0) err_sys("str_echo:read error"); } //定义自己的signal函数 Sigfunc *signal(int signo,Sigfunc *func) { struct sigaction act,oact; act.sa_handler=func; sigemptyset(&act.sa_mask); if(sigaction(signo,&act,&oact)<0) return(SIG_ERR); return(oact.sa_handler); } //信号处理函数 void sig_chld(int signo) { pid_t pid; int stat; while((pid=waitpid(-1,&stat,WNOHANG))>0){} return; } //主函数 int main(int argc, char **argv) { int listenfd,connfd; pid_t childpid; socklen_t clilen; struct sockaddr_in cliaddr,servaddr; void sig_chld(int); listenfd=socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family=AF_INET; servaddr.sin_addr.s_addr=htonl(INADDR_ANY); servaddr.sin_port=htons(SERV_PORT); bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)); listen(listenfd,LISTENQ); signal(SIGCHLD,sig_chld);//捕捉SIGCHLD信号的信号处理函数 for(;;) { clilen=sizeof(cliaddr); connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&clilen); if((connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&clilen))<0) { if(errno==EINTR) continue; else err_sys("accept error"); } if((childpid=fork())==0) { close(listenfd); str_echo(connfd); exit(0); } close(connfd); } }