1.服务器
#include <myhead.h> #define ERR_MSG(msg) do{\ fprintf(stderr,"__%d__",__LINE__);\ perror(msg);\ }while(0) #define IP "192.168.123.104" #define PORT 8888 int main(int argc, const char *argv[]) { int sfd = socket(AF_INET,SOCK_STREAM,0); if(sfd<0) { ERR_MSG("socket"); return -1; } int reuse = 1; if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0) { ERR_MSG("setsockopt"); return -1; } printf("允许端口快速被重用成功\n"); //填充服务器的地址信息结构体 struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(PORT); sin.sin_addr.s_addr = inet_addr(IP); //绑定服务器的IP和端口---》必须绑定 if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin))<0) { ERR_MSG("bind"); return -1; } printf("bind success\n"); //套接字设置被动监听状态,监听是否有客户端连接 if(listen(sfd,128)<0) { ERR_MSG("listen"); return -1; } printf("listen success\n"); //创建集合 fd_set readfds,tempfds; //fd_set本质上是一个结构体,且结构体中是一个整形数组,若不初始化,其中会存有随机值 //由于fd-set这个集合需要存放要检测的文件描述符,不能让其中随机到有效文件描述符 //所以需要初始化集合 //集合初始化 FD_ZERO(&readfds); FD_ZERO(&tempfds); //将需要监测的文件描述符放入集合中 FD_SET(0,&readfds); FD_SET(sfd,&readfds); int maxfd = sfd; //存最大的文件描述符 int newfd = -1; char buf[128]= ""; ssize_t res = 0; int s_res = 0; struct sockaddr_in cin; //存储客户端地址信息 socklen_t addrlen = sizeof(cin); while(1) { tempfds = readfds; //让内核监测集合中的文件描述符是否准备就绪 s_res = select(maxfd+1,&tempfds,NULL,NULL,NULL); if(s_res < 0) { ERR_MSG("select"); return -1; } else if(0 == s_res) { printf("time out....\n"); break; } printf("__%d__\n",__LINE__); //能运行到当前位置,则代表集合中有文件描述符准备就绪 //判断哪个文件描述符准备就绪,走对应的处理函数即可 //当集合中有某个文件描述符准备就绪了,集合中只会剩下产生事件的文件描述符 //若0准备就绪,则集合中只会剩下0 //若sfd准备就绪,则集合钟会剩下sfd //若sfd和0均准备就绪,则集合中会剩下0和sfd for(int i=0;i<=maxfd;i++) { if(FD_ISSET(i,&tempfds) == 0) { continue; } //能运行到当前位置,则说明i所代表的文件描述符在tempfds中 if(0 ==i) //键盘输入事件 { printf("触发键盘输入事件\n"); int sndfd; //存储要发送给哪个客户端 res = scanf("%d %s",&sndfd,buf); while(getchar()!=10); if(res !=2) { fprintf(stderr,"输入的数据格式错误:fd string\n"); continue; } //能运行到当前位置,则代表输入的格式是正确的 //需要排除非法的文件描述符 if(sndfd<=2 || sndfd>=1024 || !FD_ISSET(sndfd,&readfds)) { fprintf(stderr,"sndfd=%d 文件描述符错误\n",sndfd); continue; } if(send(sndfd,buf,sizeof(buf),0) <0) { ERR_MSG("send"); continue; } printf("发送成功\n"); } else if(sfd == i) //客户端连接事件 { printf("触发客户端连接事件\n"); newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen); if(newfd < 0) { ERR_MSG("accept"); return -1; } printf("[%s : %d] newfd = %d 客户端连接成功\n",\ inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd); //将newfd添加到集合readfds中 FD_SET(newfd,&readfds); //更新maxfd maxfd = maxfd>newfd ?maxfd :newfd; } else //客户端交互事件 { printf("触发客户端交互事件\n"); bzero(buf,sizeof(buf)); //接收数据 res = recv(i,buf,sizeof(buf),0); if(res < 0) { ERR_MSG("recv"); continue; } else if(0 == res) { printf("[%s : %d] newfd = %d 客户端下线\n",\ inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd); //关闭文件描述符 close(i); //将i所代表的文件描述符删除 FD_CLR(i,&readfds); //更新maxfd ---> //从最大的文件描述符开始遍历,直到文件描述符在集合中 //则该文件描述符就是最大的文件描述符 /* int j = maxfd; for(;j>=0;j--) { if(FD_ISSET(j,&readfds)) break; } maxfd = j; */ while(!FD_ISSET(maxfd,&readfds) && maxfd-->=0); continue; } printf("[%s : %d] newfd=%d : %s\n",\ inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd,buf); //发送数据 strcat(buf,"*_*"); //可以修改成从其他地方获取 if(send(i,buf,sizeof(buf),0) <0) { ERR_MSG("send"); continue; } printf("发送成功\n"); } } } if(close(sfd)<0) { ERR_MSG("close"); return -1; } return 0; }
2.客户端
#include <myhead.h> #define ERR_MSG(msg) do { fprintf(stderr,"__%d__",__LINE__);perror(msg);}while(0) #define IP "192.168.123.104" //ifconfig查看本机IP #define PORT 8888 int main(int argc, const char *argv[]) { //创建流式套接字 int cfd = socket(AF_INET,SOCK_STREAM,0); if(cfd < 0) { ERR_MSG("socket"); return -1; } //允许端口快速被重用 int reuse = 1; if(setsockopt(cfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) <0) { ERR_MSG("setsockopt"); return -1; } printf("允许端口快速被重用成功\n"); //填充服务器的地址信息结构体 AF_INET:man 7 ip //填充服务器的地址信息结构体,给connect函数使用 //要连接哪个服务器,就填充哪个服务器绑定的地址信息 struct sockaddr_in sin; sin.sin_family = AF_INET; //必须填AF_INET sin.sin_port = htons(PORT); //端口号的网络字节序,1024-49151 sin.sin_addr.s_addr= inet_addr(IP); //本机IP的网络字节序 //连接服务器 if(connect(cfd,(struct sockaddr *)&sin,sizeof(sin))<0) { ERR_MSG("connect"); return -1; } printf("connect success\n"); //创建集合 fd_set readfds,tempfds; //集合初始化 FD_ZERO(&tempfds); FD_ZERO(&readfds); //将需要监测的文字描述符放入集合中 FD_SET(0,&readfds); FD_SET(cfd,&readfds); int newfd = -1; char buf[128] = ""; ssize_t res = 0; int s_res = 0; while(1) { //让内核监测集合中的文件描述符是否准备就绪 s_res = select(cfd+1,&readfds,NULL,NULL,NULL); if(s_res<0) { ERR_MSG("select"); return -1; } if(FD_ISSET(0,&readfds)) { bzero(buf,sizeof(buf)); //发送数据 printf("请输入:"); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1]=0; //对方接收多少个字节,发送多少个字节 if(send(cfd,buf,sizeof(buf),0) <0) { ERR_MSG("send"); return -1; } printf("发送成功\n"); bzero(buf,sizeof(buf)); //接收数据 res = recv(cfd,buf,sizeof(buf),0); } if(FD_ISSET(cfd,&readfds)) { if(res < 0) { ERR_MSG("recv"); return -1; } else if(0 == res) { printf("cfd = %d 服务器下线\n",cfd); break; } printf("cfd=%d : %s\n",cfd,buf); } } //关闭文件描述符 if(close(cfd)<0) { ERR_MSG("close"); return -1; } return 0; }