目录
使用多进程实现并发服务器
使用多线程实现并发服务器
流式域套接字服务器
流式域套接字客户端
报式域套接字服务器
报式域套接字客户端
tftp客户端
思维导图
使用多进程实现并发服务器
#include <myhead.h>
void sighandler(int signum){if(signum==SIGCHLD){//回收退出的僵尸进程while(waitpid(-1,NULL,WNOHANG)>0);}
}
int main(int argc, const char *argv[])
{//signal函数捕获SIGCHLD信号if(signal(SIGCHLD,sighandler)==SIG_ERR){perror("signal");return -1;}//创建套接字int sfd=socket(AF_INET,SOCK_STREAM,0);if(sfd<0){perror("socket");return -1;}//绑定//填充地址信息结构体struct sockaddr_in sin;sin.sin_family=AF_INET;sin.sin_port=htons(6666);sin.sin_addr.s_addr=inet_addr("192.168.125.13");if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0){perror("bind");return -1;}//listenlisten(sfd,128);//接收客户端地址信息struct sockaddr_in cin;socklen_t addrlen=sizeof(cin);while(1){//acceptint newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);if(newfd<0){perror("accept");return -1;}//终端打印客户端连接信息printf("[%s %d]accept success\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));//创建子进程用于和客户端收发信息pid_t pid=fork();if(pid>0){//父进程用不到newfd,关闭父进程newfdclose(newfd);}//子进程和客户端收发信息else if(pid==0){//子进程用不到sfd,关闭sfdclose(sfd);char buf[128];while(1){memset(buf,0,sizeof(buf));if(0==recv(newfd,buf,sizeof(buf),0)){printf("[%s %d]客户端下线\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));break;}printf("[%s %d]%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);strcat(buf,"*-*");send(newfd,buf,sizeof(buf),0);}close(newfd);exit(EXIT_SUCCESS);}else if(pid<0){perror("fork");close(sfd);close(newfd);}}close(sfd);return 0;
}
使用多线程实现并发服务器
#include <myhead.h>
//多线程实现TCP并发服务器
//线程传参结构体
typedef struct{int newfd;struct sockaddr_in cin;
}para_t;void *task(void *arg);
int main(int argc, const char *argv[])
{//创建套接字int sfd=socket(AF_INET,SOCK_STREAM,0);if(sfd<0){perror("socket");return -1;}//设置端口快速重用int reuse=1;if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0){perror("setsockopt");return -1;}//填充地址信息结构体struct sockaddr_in sin;sin.sin_family=AF_INET;sin.sin_port=htons(6666);sin.sin_addr.s_addr=inet_addr("192.168.125.13");//绑定if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0){perror("bind");return -1;}//listenlisten(sfd,128);//客户端地址信息结构体struct sockaddr_in cin;socklen_t addrlen=sizeof(cin);while(1){//接收客户端的连接int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);if(newfd<0){perror("accept");return -1;}printf("[%s %d]accept success\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));//创建用于和客户端通信的线程para_t info={newfd,cin};pthread_t tid;if(pthread_create(&tid,NULL,task,&info)!=0){printf("pthread_create err\n");return -1;}//分离线程pthread_detach(tid);}close(sfd);return 0;
}
void *task(void *arg){int newfd=((para_t*)arg)->newfd;struct sockaddr_in cin=((para_t*)arg)->cin;char buf[128]="";while(1){//接收客户端信息memset(buf,0,sizeof(buf));if(0==recv(newfd,buf,sizeof(buf),0)){printf("[%s %d]客户端下线\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));break;}printf("[%s %d]:%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);strcpy(buf,"*-*");send(newfd,buf,sizeof(buf),0);}//关闭用于和客户端通信的套接字,退出线程close(newfd);pthread_exit(NULL);
}
流式域套接字服务器
#include <myhead.h>
//流式域套接字服务器
int main(int argc, const char *argv[])
{//创建套接字int sfd=socket(AF_UNIX,SOCK_STREAM,0);if(sfd<0){perror("socket");return -1;}//判断套接字文件是否存在if(access("./unix",F_OK)==0){if(unlink("./unix")<0){perror("unlink");return -1;}}//填充地址信息结构体struct sockaddr_un sun;sun.sun_family=AF_UNIX;strcpy(sun.sun_path,"./unix");//绑定if(bind(sfd,(struct sockaddr*)&sun,sizeof(sun))<0){perror("bind");return -1;}//监听listen(sfd,128);//客户端地址信息结构体struct sockaddr_un cun;socklen_t addrlen=sizeof(cun);//接受客户端连接int newfd=accept(sfd,(struct sockaddr*)&cun,&addrlen);if(newfd<0){printf("accept");return -1;}printf("11\n");printf("[%s] accept success\n",cun.sun_path);char buf[128]="";while(1){memset(buf,0,sizeof(buf));if(0==recv(newfd,buf,sizeof(buf),0)){printf("[%s]客户端下线\n",cun.sun_path);break;}printf("[%s]:%s\n",cun.sun_path,buf);strcat(buf,"*-*");send(newfd,buf,sizeof(buf),0);}close(newfd);close(sfd);return 0;
}
流式域套接字客户端
#include <myhead.h>
//流式域套接字客户端
int main(int argc, const char *argv[])
{//创建套接字int sfd=socket(AF_UNIX,SOCK_STREAM,0);if(sfd<0){perror("socket");return -1;}//tcp绑定非必须/*//判断套接字文件是否存在if(access("./unix",F_OK)==0){if(unlink("./unix")<0){perror("unlink");return -1;}}//填充地址信息结构体struct sockaddr_un sun;sun.sun_family=AF_UNIX;strcpy(sun.sun_path,"./unix");//绑定if(bind(sfd,(struct sockaddr*)&sun,sizeof(sun))<0){perror("bind");return -1;}
*/ //填充地址信息结构体struct sockaddr_un sun;sun.sun_family=AF_UNIX;strcpy(sun.sun_path,"./unix");if(connect(sfd,(struct sockaddr*)&sun,sizeof(sun))<0){perror("connect");return -1;}char buf[128]="";while(1){//从终端获取输入memset(buf,0,sizeof(buf));printf("请输入>>");fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1]='\0';//发送给服务器send(sfd,buf,sizeof(buf),0);memset(buf,0,sizeof(buf));if(0==recv(sfd,buf,sizeof(buf),0)){printf("[%s]服务器下线\n",sun.sun_path);break;}printf("[%s]:%s\n",sun.sun_path,buf);}close(sfd);return 0;
}
报式域套接字服务器
#include <myhead.h>
//报式域套接字
int main(int argc, const char *argv[])
{//创建套接字int sfd=socket(AF_UNIX,SOCK_DGRAM,0);if(sfd<0){perror("socket");return -1;}//判断套接字文件是否存在,存在则删除if(access("./unix",F_OK)==0){if(unlink("./unix")!=0){perror("unlink");return -1;}}//填充地址信息结构体struct sockaddr_un sun;sun.sun_family=AF_UNIX;strcpy(sun.sun_path,"./unix");//绑定if(bind(sfd,(struct sockaddr *)&sun,sizeof(sun))<0){perror("bind");return -1;}//客户端地址信息结构体struct sockaddr_un cun;socklen_t addrlen=sizeof(cun);char buf[128]="";//接收发送while(1){//接收客户端信息memset(buf,0,sizeof(buf));if(recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cun,&addrlen)<0){perror("recvfrom");break;}printf("[%s]:%s\n",cun.sun_path,buf);strcat(buf,"*-*");if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cun,addrlen)<0){perror("sendto");break;}}close(sfd);return 0;
}
报式域套接字客户端
#include <myhead.h>
//报式域套接字
int main(int argc, const char *argv[])
{//创建套接字int cfd=socket(AF_UNIX,SOCK_DGRAM,0);if(cfd<0){perror("socket");return -1;}//判断套接字文件是否存在,存在则删除if(access("./linux",F_OK)==0){if(unlink("./linux")!=0){perror("unlink");return -1;}}//填充地址信息结构体struct sockaddr_un cun;cun.sun_family=AF_UNIX;strcpy(cun.sun_path,"./linux");//绑定if(bind(cfd,(struct sockaddr *)&cun,sizeof(cun))<0){perror("bind");return -1;}//服务器地址信息结构体struct sockaddr_un sun;sun.sun_family=AF_UNIX;strcpy(sun.sun_path,"./unix");socklen_t addrlen=sizeof(sun);char buf[128]="";//接收发送while(1){//从终端获取输入memset(buf,0,sizeof(buf));printf("请输入>>");fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1]='\0';//发送给服务器if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sun,addrlen)<0){perror("sendto");break;}//接收服务信息memset(buf,0,sizeof(buf));if(recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sun,&addrlen)<0){perror("recvfrom");break;}printf("%s\n",buf);}close(cfd);return 0;
}
tftp客户端
#include <myhead.h>
//tftp客户端
#define ERR_MSG(msg) do{fprintf(stderr,"__%d__",__LINE__);\perror(msg);\}while(0)
//#define IP "192.168.126.8" //服务器IP地址
char IP[128];
#define PORT 69 //tftp服务器端口号69
// 下载
int download(int sfd);
//上传
int upload(int sfd);
int main(int argc, const char *argv[])
{//1.打开套接字int sfd = socket(AF_INET,SOCK_DGRAM,0);//从终端获取服务器IPprintf("server IP:");fgets(IP,sizeof(IP),stdin);IP[strlen(IP)-1]='\0';//2.绑定非必须while(1){puts(" \033[1;34;10m 1.下载");puts(" \033[1;34;10m 2.上传");puts(" 3.退出\033[0m");printf("\033[1;32;10m请选择:\033[0m");int choice;scanf("%d",&choice);while(getchar()!=10);switch(choice){case 1:{ //下载 download(sfd);break;}case 2:{ //上传upload(sfd);break;}case 3:{ //退出goto end; break;}default:{ //其他break;}}printf("\033[1;33;10mpress space return menu or any key exit\033[0m");if(getchar()!=' '){break;}system("clear");}
end://4.关闭套接字close(sfd);return 0;
}//下载
int download(int sfd){//服务器地址信息结构体struct sockaddr_in sin;sin.sin_family=AF_INET;sin.sin_port=htons(PORT);sin.sin_addr.s_addr=inet_addr(IP);socklen_t len=sizeof(sin);//从终端获取文件名char fname[128]={0};fgets(fname,sizeof(fname),stdin);fname[strlen(fname)-1]='\0';//下载char buf[516]={0};short *p1=(short *)buf;//填充读写请求报文*p1=htons(1); //1为读,2为写strcpy((buf+2),fname);char *p2=buf+2+strlen(fname);*p2='\0';strcpy((p2+1),"octet");*(p2+2+strlen("octet"))='\0';int size=4+strlen(fname)+strlen("octet");//发送下载请求if(sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))<0){ERR_MSG("sendto");return -1;}printf("发送下载请求成功\n");int fd=-1;short hblock=0;while(1){memset(buf,0,sizeof(buf));//读取服务器数据包int res=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&len);//获取服务器操作码short data=ntohs(*(short *)buf);//获取服务器发送的block编号short block=ntohs(*((short *)(buf+2)));//判断操作码3为数据包if(3==data){//判断有无丢包if(block==++hblock){//无丢包//当块编号为1时打开存储的文件if(1==block){fd=open(fname,O_WRONLY|O_CREAT|O_TRUNC,0664);if(fd<0){ERR_MSG("open");return -1;}}//把读取到的数据写入到本地文件if(write(fd,buf+4,res-4)<0){ERR_MSG("write");return -1;}//ack报文格式 操作码 块编号char ack[4];memset(ack,0,sizeof(ack));short *p1=(short *)ack;*p1=htons(4); //操作码*(p1+1)=htons(block); //块编号,无丢包直接回复收到的块编号//给服务器回ACKif(sendto(sfd,ack,sizeof(ack),0,(struct sockaddr*)&sin,sizeof(sin))<0){ERR_MSG("sendto");return -1;}//当数据包长度小于516,说明数据小于512,数据发送完毕if(res<516){printf("%s 下载完成\n",fname);break;}}else{//ack报文格式 操作码 块编号char ack[4];memset(ack,0,sizeof(ack)); short *p1=(short *)ack;*p1=htons(4); //操作码*(p1+1)=htons(hblock); //块编号,无丢包直接回复收到的块编号//给服务器回ACKif(sendto(sfd,ack,sizeof(ack),0,(struct sockaddr*)&sin,sizeof(sin))<0){ERR_MSG("sendto");return -1;}}}//5为错误包errorelse if(5==ntohs(*p2)){short err;err=ntohs(*(buf+2));printf("err=%d\n",err);break;} }close(fd);
}
//上传
int upload(int sfd){//服务器地址信息结构体struct sockaddr_in sin;sin.sin_family=AF_INET;sin.sin_port=htons(PORT);sin.sin_addr.s_addr=inet_addr(IP);socklen_t len=sizeof(sin);//从终端获取文件名char fname[128]={0};fgets(fname,sizeof(fname),stdin);fname[strlen(fname)-1]='\0';//上传char buf[516]={0};short *p1=(short *)buf;//填充读写请求报文*p1=htons(2); //1为读,2为写strcpy((buf+2),fname);char *p2=buf+2+strlen(fname);*p2='\0';strcpy((p2+1),"octet");*(p2+2+strlen("octet"))='\0';int size=4+strlen(fname)+strlen("octet");//发送上传请求if(sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))<0){ERR_MSG("sendto");return -1;}printf("上传请求发送成功\n");//读取服务器应答 服务器允许上传则发送一个0号应答包char ack[128]={0};int res=recvfrom(sfd,ack,sizeof(ack),0,(struct sockaddr*)&sin,&len);if(res<0){ERR_MSG("recvfrom");return -1;}//获取服务器操作码short data=ntohs(*(short *)ack);//获取服务器发送的block编号short block=ntohs(*((short *)(ack+2)));//如果不允许上传直接返回if(block!=0) return -1;int fd=-1;short hblock=0;//打开要上传的文件fd=open(fname,O_RDONLY);if(fd<0){ERR_MSG("open");return -1;} while(1){//清空bufmemset(buf,0,sizeof(buf));short *p1=(short *)buf;*p1=htons(3); //操作码*(p1+1)=htons(++hblock); //块编号,无丢包直接回复收到的块编号//把读取本地文件int res=read(fd,buf+4,512);if(res<0){ERR_MSG("read");return -1;}//给服务器发送数据包if(sendto(sfd,buf,4+res,0,(struct sockaddr*)&sin,sizeof(sin))<0){ERR_MSG("sendto");return -1;}//ack报文格式 操作码 块编号memset(ack,0,sizeof(ack));//当文件读取结束,上传完毕if(res==0){printf("%s 上传完成\n",fname);break;}while(1){//读取服务器应答res=recvfrom(sfd,ack,sizeof(ack),0,(struct sockaddr*)&sin,&len);if(res<0){ERR_MSG("recvfrom");return -1;}//获取服务器操作码data=ntohs(*(short *)ack);//获取服务器发送的block编号block=ntohs(*((short *)(ack+2)));//判断操作码4为服务器应答if(4==data){//判断有无丢包if(block==hblock){//无丢包break; }//丢包重新发送当前包if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin))<0){ERR_MSG("sendto");return -1;}} //5为错误包errorelse if(5==data){printf("err=%d\n",block);return -1;}} }close(fd);
}