一、广播接收方:
- #include <myhead.h>
-
- #define ERR_MSG(msg) do{\
- fprintf(stderr,"__%d__\n",__LINE__);\
- perror(msg);\
- }while(0)
-
- #define BRD_IP "192.168.114.255"
- #define BRD_PORT 8888
-
- int main(int argc, const char *argv[])
- {
- //创建报式套接字
- int sfd = -1;
- if((sfd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
- {
- ERR_MSG("socket");
- }
- printf("socket success sfd=%d\n",sfd);
-
- //填充地址信息结构体给bind用
- struct sockaddr_in sin;
- sin.sin_family = AF_INET; //必须填AF_INET
- sin.sin_port = htons(BRD_PORT); //广播端口号
- sin.sin_addr.s_addr = inet_addr(BRD_IP); //广播IP
- //bind绑定地址信息
- if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
- {
- ERR_MSG("bind");
- return -1;
- }
- printf("bind success[%s:%d]\n",BRD_IP,BRD_PORT);
-
- socklen_t addrlen = sizeof(sin);
- char buf[128]="";
- ssize_t len;
- while(1)
- {
- if((len = recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen)) < 0)
- {
- ERR_MSG("recvfrom");
- return -1;
- }
- printf("recvfrom success %s\n",buf);
- }
- close(sfd);
- return 0;
- }
二、广播发送方:
- #include <myhead.h>
-
- #define ERR_MSG(msg) do{\
- fprintf(stderr,"__%d__\n",__LINE__);\
- perror(msg);\
- }while(0)
-
- #define BRD_IP "192.168.114.255"
- #define BRD_PORT 8888
-
- int main(int argc, const char *argv[])
- {
- //创建报式套接字
- int cfd = -1;
- if((cfd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
- {
- ERR_MSG("socket");
- }
- printf("socket success cfd=%d\n",cfd);
-
- int broad = 1;
- if(setsockopt(cfd,SOL_SOCKET,SO_BROADCAST,(int*)&broad,sizeof(broad)) < 0)
- {
- ERR_MSG("setsockpot");
- return-1;
- }
- printf("setsockpot success\n");
- //非必须绑定
- //填充地址信息结构体给sendto
- struct sockaddr_in sin;
- sin.sin_family = AF_INET; //必须填AF_INET
- sin.sin_port = htons(BRD_PORT); //广播端口号
- sin.sin_addr.s_addr = inet_addr(BRD_IP); //广播IP
- printf("bind success[%s:%d]\n",BRD_IP,BRD_PORT);
-
- char buf[128]="";
- ssize_t len;
- while(1)
- {
- //清空buf
- bzero(buf,sizeof(buf));
-
- //从终端输入数据保存到buf中
- fscanf(stdin,"%s",buf);
- while(getchar()!=10);
-
- if(sendto(cfd,buf,strlen(buf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
- {
- ERR_MSG("sendto");
- return -1;
- }
- printf("sendto success\n");
- }
- close(cfd);
- return 0;
- }
三、组播接收方:
- #include <myhead.h>
-
- #define ERR_MSG(msg) do{\
- fprintf(stderr,"__%d__\n",__LINE__);\
- perror(msg);\
- }while(0)
-
- #define GRP_IP "224.1.2.3" //组播IP
- #define IP "192.168.114.94" //本机IP
- #define GRP_PORT 8888
-
- int main(int argc, const char *argv[])
- {
- //创建报式套接字
- int sfd = -1;
- if((sfd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
- {
- ERR_MSG("socket");
- }
- printf("socket success sfd=%d\n",sfd);
-
- //填充结构体,来完成组播连接
- struct ip_mreqn grp;
- grp.imr_multiaddr.s_addr = inet_addr(GRP_IP); //组播IP
- grp.imr_address.s_addr = inet_addr(IP); //本机IP
- grp.imr_ifindex = 0; //自动
-
- //设置连接组播
- if(setsockopt(sfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&grp,sizeof(grp)) < 0)
- {
- ERR_MSG("setsockopt");
- return -1;
- }
- printf("setsockopt success\n");
-
- //填充地址信息结构体给bind用
- struct sockaddr_in sin;
- sin.sin_family = AF_INET; //必须填AF_INET
- sin.sin_port = htons(GRP_PORT); //组播端口号
- sin.sin_addr.s_addr = inet_addr(GRP_IP); //组播IP
- //bind绑定地址信息
- if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
- {
- ERR_MSG("bind");
- return -1;
- }
- printf("bind success[%s:%d]\n",GRP_IP,GRP_PORT);
-
- socklen_t addrlen = sizeof(sin);
- char buf[128]="";
- ssize_t len;
- while(1)
- {
- if((len = recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen)) < 0)
- {
- ERR_MSG("recvfrom");
- return -1;
- }
- printf("recvfrom success %s\n",buf);
- }
- close(sfd);
- return 0;
- }
四、组播发送方:
- #include <myhead.h>
-
- #define ERR_MSG(msg) do{\
- fprintf(stderr,"__%d__\n",__LINE__);\
- perror(msg);\
- }while(0)
-
- #define BRD_IP "224.1.2.3"
- #define BRD_PORT 8888
-
- int main(int argc, const char *argv[])
- {
- //创建报式套接字
- int cfd = -1;
- if((cfd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
- {
- ERR_MSG("socket");
- }
- printf("socket success cfd=%d\n",cfd);
-
- int broad = 1;
- if(setsockopt(cfd,SOL_SOCKET,SO_BROADCAST,(int*)&broad,sizeof(broad)) < 0)
- {
- ERR_MSG("setsockpot");
- return-1;
- }
- printf("setsockpot success\n");
- //非必须绑定
- //填充地址信息结构体给sendto
- struct sockaddr_in sin;
- sin.sin_family = AF_INET; //必须填AF_INET
- sin.sin_port = htons(BRD_PORT); //广播端口号
- sin.sin_addr.s_addr = inet_addr(BRD_IP); //广播IP
- printf("bind success[%s:%d]\n",BRD_IP,BRD_PORT);
-
- char buf[128]="";
- ssize_t len;
- while(1)
- {
- //清空buf
- bzero(buf,sizeof(buf));
-
- //从终端输入数据保存到buf中
- fscanf(stdin,"%s",buf);
- while(getchar()!=10);
-
- if(sendto(cfd,buf,strlen(buf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
- {
- ERR_MSG("sendto");
- return -1;
- }
- printf("sendto success\n");
- }
- close(cfd);
- return 0;
- }
五、TCP多线程并发服务器:
- #include <myhead.h>
-
- #define ERR_MSG(msg) do{\
- fprintf(stderr,"__%d__\n",__LINE__);\
- perror(msg);\
- }while(0)
-
- #define IP "192.168.114.94"
- #define PORT 8888
-
- int del_cli_msg(int newfd,struct sockaddr_in cin);
- void handler(int num)
- {
- if(num == SIGCHLD)
- {
- while(waitpid(-1,NULL,WNOHANG) > 0);
- }
- }
- int main(int argc, const char *argv[])
- {
- if(signal(SIGCHLD,handler) == SIG_ERR)
- {
- ERR_MSG("signal");
- return -1;
- }
- printf("signal success\n");
- //创建流式套接字
- int sfd = -1;
- if((sfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
- {
- ERR_MSG("socket");
- return -1;
- }
- printf("socket success sfd = %d\n",sfd);
-
- //允许端口快速的被复用
- int reuse = 1;
- if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
- {
- ERR_MSG("setsockopt");
- return -1;
- }
- printf("允许端口快速的被复用成功\n"); //填充地址信息结构体给bind使用
-
- struct sockaddr_in sin;
- sin.sin_family = AF_INET;
- sin.sin_port = htons(PORT); //服务器端口号
- sin.sin_addr.s_addr = inet_addr(IP); //服务器IP
- //bind必须绑定IP和端口号
- if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
- {
- ERR_MSG("bind");
- return -1;
- }
- printf("bind success [%s:%d]\n",IP,PORT);
- //listen把sfd切换成监听模式
- if(listen(sfd,128) < 0)
- {
- ERR_MSG("listen");
- return -1;
- }
- printf("listen success sfd = %d\n",sfd);
- struct sockaddr_in cin;
- socklen_t addrlen=sizeof(cin);
- int newfd =-1;
- pid_t pid;
- while(1)
- {
- //accept接收客户端发送过来的地址信息,创建一个newfd与其通讯accept
- //父进程专门负责连接
- if((newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen)) < 0)
- {
- ERR_MSG("accept");
- return -1;
- }
- printf("[%s:%d]客户端连接成功 newfd = %d\n",\
- inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
- pid = fork();
- if(0 == pid)
- {
- close(sfd);
- //子进程专门负责交互
- del_cli_msg(newfd,sin);
- exit(0);
- }
- close(newfd);
- }
- close(sfd);
- return 0;
- }
- int del_cli_msg(int newfd,struct sockaddr_in cin)
- {
- char buf[128] ="";
- ssize_t len = 0;
- while(1)
- {
- bzero(buf,sizeof(buf));
- len = recv(newfd,buf,sizeof(buf),0);
- if(len< 0)
- {
- ERR_MSG("recvfrom");
- return -1;
- }
- else if(len == 0)
- {
- printf("[%s:%d]客户端已下线 newfd = %d\n",\
- inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
- break;
- }
- printf("recv success [%s:%d],newfd=%d buf=%s\n",\
- inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd,buf);
- /*
- bzero(buf,sizeof(buf));
- printf("请输入数据发送给客户端");
- fscanf(stdin,"%s",buf);
- while(getchar()!=10);
- */
- strcat(buf,">-<");
- if(send(newfd,buf,strlen(buf),0) < 0)
- {
- ERR_MSG("send");
- return -1;
- }
- printf("send success buf = %s\n",buf);
- }
- }
六、TCP多线程并发服务器:
- #include <myhead.h>
-
- #define ERR_MSG(msg) do{\
- fprintf(stderr,"__%d__\n",__LINE__);\
- perror(msg);\
- }while(0)
-
- #define IP "192.168.114.94"
- #define PORT 8888
- struct Data
- {
- int newfd;
- struct sockaddr_in cin;
- };
- void *del_cli_msg(void *arg);
- int main(int argc, const char *argv[])
- {
- printf("signal success\n");
- //创建流式套接字
- int sfd = -1;
- if((sfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
- {
- ERR_MSG("socket");
- return -1;
- }
- printf("socket success sfd = %d\n",sfd);
-
- //允许端口快速的被复用
- int reuse = 1;
- if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
- {
- ERR_MSG("setsockopt");
- return -1;
- }
- printf("允许端口快速的被复用成功\n"); //填充地址信息结构体给bind使用
-
- struct sockaddr_in sin;
- sin.sin_family = AF_INET;
- sin.sin_port = htons(PORT); //服务器端口号
- sin.sin_addr.s_addr = inet_addr(IP); //服务器IP
- //bind必须绑定IP和端口号
- if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
- {
- ERR_MSG("bind");
- return -1;
- }
- printf("bind success [%s:%d]\n",IP,PORT);
- //listen把sfd切换成监听模式
- if(listen(sfd,128) < 0)
- {
- ERR_MSG("listen");
- return -1;
- }
- printf("listen success sfd = %d\n",sfd);
- struct sockaddr_in cin;
- socklen_t addrlen=sizeof(cin);
- int newfd =-1;
- pthread_t tid;
- while(1)
- {
- //accept接收客户端发送过来的地址信息,创建一个newfd与其通讯accept
- //父进程专门负责连接
- if((newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen)) < 0)
- {
- ERR_MSG("accept");
- return -1;
- }
- printf("[%s:%d]客户端连接成功 newfd = %d\n",\
- inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
- struct Data data;
- data.newfd = newfd;
- data.cin = cin;
- if(pthread_create(&tid,NULL,del_cli_msg,&data))
- {
- printf("pthread_create error\n");
- return -1;
- }
- printf("create success\n");
- pthread_detach(tid);
- }
- close(sfd);
- return 0;
- }
- void *del_cli_msg(void *arg)
- {
- int newfd = ((struct Data*)arg)->newfd;
- struct sockaddr_in cin = ((struct Data*)arg)->cin;
- char buf[128] ="";
- ssize_t len = 0;
- while(1)
- {
- bzero(buf,sizeof(buf));
- len = recv(newfd,buf,sizeof(buf),0);
- if(len< 0)
- {
- ERR_MSG("recvfrom");
- break;
- }
- else if(len == 0)
- {
- printf("[%s:%d]客户端已下线 newfd = %d\n",\
- inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
- break;
- }
- printf("recv success [%s:%d],newfd=%d buf=%s\n",\
- inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd,buf);
- /*
- bzero(buf,sizeof(buf));
- printf("请输入数据发送给客户端");
- fscanf(stdin,"%s",buf);
- while(getchar()!=10);
- */
- strcat(buf,">-<");
- if(send(newfd,buf,strlen(buf),0) < 0)
- {
- ERR_MSG("send");
- break;
- }
- //printf("send success buf = %s\n",buf);
- }
- }
七、多线程并发服务器为什么不能将newfd定义成全局?
不能,因为如果newfd设成全局变量,而多线程之间共享0-3G用户空间,临界资源,所以会导致newfd变化后,原先线程进行交互。
八、基于UDP的TFTP文件传输,完成下载和上传功能,制作的是客户端,与tftp服务器进行交互
- #include <myhead.h>
-
- #define ERR(s) do\
- {\
- fprintf(stderr,"__%d__",__LINE__);\
- perror(s);\
- }while(0)
- #define PORT 69
- #define IP "192.168.114.103"
-
- int do_download(int cfd,struct sockaddr_in sin);
- int do_upload(int cfd,struct sockaddr_in sin);
- int main(int argc, const char *argv[])
- {
-
- //创建报式套接字socket
- int cfd;
- if((cfd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
- {
- ERR("socket");
- return -1;
- }
- printf("socket success\n");
-
- struct sockaddr_in sin;
- sin.sin_family = AF_INET;
- sin.sin_port = htons(PORT);
- sin.sin_addr.s_addr = inet_addr(IP);
- char choose = 0;
- while(1)
- {
- system("clear");
- printf("--------------------------------\n");
- printf("-------------1下载--------------\n");
- printf("-------------2上传--------------\n");
- printf("-------------3退出--------------\n");
- printf("--------------------------------\n");
- printf("请输入>>>");
- scanf("%c",&choose);
- while(getchar()!=10); //吸收垃圾字符
- switch(choose)
- {
- case '1':
- do_download(cfd,sin);
- break;
- case '2':
- do_upload(cfd,sin);
- break;
- case '3':
- goto END;
- break;
- default :
- printf("输入错误,请重新输入\n");
- }
- printf("请输入任意字符清屏\n");
- while(getchar()!=10);
- }
- END:
- //关闭套接字
- close(cfd);
- return 0;
- }
-
- int do_download(int cfd,struct sockaddr_in sin)
- {
- /*
- //组下载请求包
- char buf[516] = "";
- char f_n[20] = "";
- //操作码
- unsigned short *ptr1 = (unsigned short*)buf;
- *ptr1 = htons(1); //组操作码
- //文件名
- char *ptr2 = (char *)(ptr1+1);
- strcpy(ptr2,f_n);
- //模式
- char *ptr3 = ptr2 + strlen(f_n) + 1;
- strcpy(ptr3,"octet");
-
- int size = 2+strlen(ptr2)+1+strlen(ptr3)+1;
- */
- char buf[516] = "";
- char f_n[20] = "";
- int num=1;
- printf("请输入文件名>>> ");
- //终端输入要下载的文件名
- fgets(f_n,sizeof(f_n),stdin);
- f_n[strlen(f_n)-1] = '\0';
- int fd;
- if((fd = open(f_n,O_RDONLY|O_CREAT|O_TRUNC,0664)) == -1)
- {
- ERR("open");
- return -1;
- }
- //用sprintf拼接
- int size = sprintf(buf,"%c%c%s%c%s%c",0,1,f_n,0,"octet",0);
- //发送下载请求sendto
- if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)) < 0 )
- {
- ERR("sendto askpackage");
- return -1;
- }
- printf("sendto askpackage success\n");
- ssize_t len = -1;
- struct sockaddr_in addrin;
- socklen_t addrlen=sizeof(addrin);
- while(1)
- {
- //清空buf
- bzero(buf,sizeof(buf));
- //接收数据recvfrom,接收地址信息
- if((len = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&addrin,&addrlen)) < 0)
- {
- ERR("recvfrom datapackage");
- return -1;
- }
- unsigned short ptr2=ntohs(*(unsigned short*)(buf+2));
- printf("%hu\n",ptr2);
- //当操作码为5时
- if(5 == buf[1])
- {
- fprintf(stderr, "错误码:%d,错误信息:%s\n", ntohs(*(unsigned short*)(buf+2)), buf+4);
- break;
- }
- //当操作码为3,并且块编号正确时
- else if(3 == buf[1] && num == ptr2)
- {
- printf("%d\n",fd);
- //跳过前四个字节,写入文件
-
- if(write(fd,buf+4,len-4)<0)
- {
- ERR("write");
- return -1;
- }
-
- //组ACK包
- buf[1] = 4;
- //发送ACK包,sendto
- if(sendto(cfd,buf,4,0,(struct sockaddr*)&addrin,addrlen) < 0)
- {
- ERR("sendto ACKpackage");
- return -1;
- }
- num++;
- //判断数据是否小于512个字节,若小于则下载完成
- if(len < 516)
- {
- printf("download success\n");
- break;
- }
- }
- }
- close(fd);
- return 0;
- }
-
- int do_upload(int cfd,struct sockaddr_in sin)
- {
- char f_n[20]="";
- printf("请输入传输文件名>>>");
- fscanf(stdin,"%s",f_n);
- while(getchar()!=10);
-
- //打开文件
- int srcfd = -1;
- if((srcfd = open(f_n,O_RDONLY)) < 0)
- {
- if(errno == ENOENT)
- {
- printf("文件不存在,请重新输入>>>");
- return -2;
- }
- else
- {
- ERR("open srcfile");
- return -1;
- }
- }
-
- char buf[516]="";
- //用sprintf拼接读写请求封包
- int size=sprintf(buf,"%c%c%s%c%s%c",0,2,f_n,0,"octet",0);
- if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
- {
- ERR("sendto askpackage");
- return -1;
- }
- printf("sendto askpackage success\n");
-
-
- //记录块编号
- int num=0;
- unsigned short* ptr = (unsigned short *)(buf+2);
- //记录我要传过去数据的大小
- ssize_t len = -1;
- socklen_t addrlen = sizeof(sin);
- while(1)
- {
- bzero(buf,sizeof(buf));
- //接受ACK
- if(recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen) < 0)
- {
- ERR("recvfrom ACKpackage");
- return -1;
- }
- printf("recvfrom ACKpackage success\n");
- //当传过来的是ACK并且对应的块编号是需要的块编号时
- if(buf[1] == 4)
- {
- if(*ptr == htons(num))
- {
- num++;
- //把操作码改为3,向服务器发送数据
- buf[1] = 3;
- //*(unsigned short *)(buf+2)= htons(num);
- *ptr = htons(num);
- //从文件中读取数据,保存到buf中
- len = read(srcfd,buf+4,sizeof(buf)-4);
- if(len < 0)
- {
- ERR("read srcfd");
- return -1;
- }
- else if(0 == len)
- {
- printf("文件上传完毕\n");
- break;
- }
- //向服务器发送打包好的数据包
- if(sendto(cfd,buf,len+4,0,(struct sockaddr*)&sin,addrlen) < 0)
- {
- ERR("sendto datapackage");
- return -1;
- }
- printf("sendto datapackage success\n");
- }else{
- printf("网络不好,上传数据失败\n");
- break;
- }
- }
- else if(buf[1] == 5)
- {
- fprintf(stderr,"错误码:%d,错误信息:%s\n",\
- ntohs(*(unsigned short*)(buf+2)),buf+4);
- break;
- }
- }
-
- return 0;
- }