服务器
#include<myhead.h>
//键盘输入事件
int keybord_events(fd_set readfds)
{char buf[128] = "";int sndfd = -1; //从终端获取一个文件描述符,发送数据给该文件描述符对应的客户端bzero(buf, sizeof(buf));int res = scanf("%d %s", &sndfd, buf);while(getchar() != 10);if(res != 2) //终端输入的数据格式错误{printf("输入数据的格式错误,:fd string\n");return -1;}if(sndfd<=2 || FD_ISSET(sndfd, &readfds)==0) //判断文件描述符的合法性{printf("非法的文件描述符:sndfd=%d\n", sndfd);return -1;}if(send(sndfd, buf, sizeof(buf), 0) < 0){perror("send");return -1;}printf("send success\n");return 0;
}//客户端连接事件
int cliConnect_events(int sfd, struct sockaddr_in saveCin[], fd_set *preadfds, int *pmaxfd)
{int newfd = -1;struct sockaddr_in cin; //存储客户端的地址信息socklen_t addrlen = sizeof(cin); //真实的地址信息结构体的大小newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if(newfd < 0){perror("newfd");return -1;}printf("[%s:%d]客户端连接成功 newfd=%d\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);saveCin[newfd] = cin; //将cin另存到newfd对应的下标位置去FD_SET(newfd, preadfds); //将newfd添加到集合中*pmaxfd = *pmaxfd>newfd ? *pmaxfd:newfd; //更新maxfdreturn 0;
}//客户端交互事件
int cliRcvSnd_events(int fd, struct sockaddr_in* saveCin, fd_set *preadfds, int* pmaxfd)
{char buf[128] = "";//清空字符串bzero(buf, sizeof(buf)); //memset//接收ssize_t res = recv(fd, buf, sizeof(buf), 0);if(res < 0){perror("recv");return -1;}else if(0 == res){printf("[%s:%d]客户端下线 newfd=%d\n", \inet_ntoa(saveCin[fd].sin_addr), ntohs(saveCin[fd].sin_port), fd);close(fd); //关闭文件描述符FD_CLR(fd, preadfds); //将文件描述符从集合中剔除while(FD_ISSET(*pmaxfd, preadfds)==0 && (*pmaxfd)-->=0);return 0;}printf("[%s:%d] newfd=%d : %s\n", \inet_ntoa(saveCin[fd].sin_addr), ntohs(saveCin[fd].sin_port), fd, buf);//发送strcat(buf, "*_*");if(send(fd, buf, sizeof(buf), 0) < 0){perror("send");return -1;}printf("send success\n");return 0;
}
int main(int argc, const char *argv[])
{int sfd=socket(AF_INET,SOCK_STREAM,0);if(sfd<0){perror("socket error");return -1;}printf("socket success\n");//允许端口快速复用int reuse=1;if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0){perror("复用失败 error");return -1;}printf("复用成功\n");//绑定地址和端口struct sockaddr_in sin;sin.sin_family=AF_INET;sin.sin_port=htons(8888);sin.sin_addr.s_addr=inet_addr("127.0.0.1");//绑定服务器if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0){perror("bind error");return -1;}//设置监听模式if(listen(sfd,128)<0){perror("listen error");return -1;}//创建集合fd_set readfds,tempfds;//清空FD_ZERO(&readfds);//添加集合FD_SET(0,&readfds);FD_SET(sfd,&readfds);int maxfd=sfd;int s_res=-1;ssize_t res=-1;char buf[128]="";struct sockaddr_in saveCin[1024];while(1){tempfds =readfds;s_res=select(maxfd+1,&tempfds,NULL,NULL,NULL);if(s_res<0){perror("select error");return -1;}else if(0==s_res){printf("time out,,\n");break;}for(int i=0; i<=maxfd; i++){if(FD_ISSET(i, &tempfds) == 0)continue;//能运行到当前位置,则说明i所代表的文件描述符在集合中if(0 == i) //0在集合中{printf("触发键盘输入事件\n");keybord_events(readfds);}else if(sfd == i) //sfd在集合中{printf("触发客户端连接事件\n");cliConnect_events(sfd, saveCin, &readfds, &maxfd);}else{printf("触发客户端交互事件\n");cliRcvSnd_events(i, saveCin, &readfds, &maxfd);}}}if(close(sfd) < 0){perror("close");return -1;}return 0;
}
客户端
#include <myhead.h>#define PORT 8888 //1024~49151
#define IP "127.0.0.1"int main(int argc, const char *argv[])
{//创建流失套接字int cfd=socket(AF_INET,SOCK_STREAM,0);if(cfd<0){perror("socket");return -1;}//填充服务器的ip地址信息结构体struct sockaddr_in sin;sin.sin_family=AF_INET;sin.sin_port=htons(PORT);sin.sin_addr.s_addr=inet_addr(IP);//连接服务器if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))<0){perror("connect");return -1;}printf("连接服务器成功\n");char buf[128];//与服务器交互while(1){bzero(buf,sizeof(buf));fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1]='\0';if(send(cfd,buf,sizeof(buf),0)<0){perror("send");return -1;}if(0==strlen(buf)){break;}printf("向服务器发送消息成功\n");bzero(buf,sizeof(buf));if(recv(cfd,buf,sizeof(buf),0)<0){perror("recv");return -1;}printf("服务器:%s\n",buf);}//关闭客户端close(cfd);return 0;
}
结果