阅读量:0
目录
723__01:使用select实现一个基于UDP的一对一即时聊天程序。
01:在一对一聊天的基础上,使用select实现一对多的回显服务。(回显服务即接收到客户端发送的数据后,再回复给客户端)
02:使用select编写聊天室程序:客户端和服务端使用tcp通信;服务端可以处理新客户端的连接和转发消息;客户端可以连入服务端并发送消息。
TCP协议喵
struct sockaddr struct sockaddr_in --> struct in_addr struct hostent htonl() htons() ntohl() ntohs() int inet_aton(const char* cp,struct in_addr * inp); in_addr_t inet_addr(const char* cp); const char* inet_ntop(int af,const void* src,char* dst,socklen_t size); struct hostent * gethostbyname(const char* name); client :socket——connect——recv/send——close (one fd) server:socket——bind——listen——accept——recv/send——close (listendfd) (peerfd) int socket (int domain,int type,int protocol); int bind (int sockfd,const struct sockaddr* addr ,socklen_t addrlen); int listen (int sockfd,int backlog); int connect (int sockfd,const struct sockaddr*addr, socklen_t addrlen); int accept (int sockfd,struct sockaddr* addr, socklen_t addrlen); ssize_t recv ( int sockfd,void* buf,size_t buf,int flags); ssize_t send( int sockfd,void* buf,size_t buf,int flags); int setsockopt(int sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); int level ,int opname,const void* optval,socklen_t optlen
靓仔不想抄代码了呜呜,好困
————————client : socket connect while(1) close ————————server : socket bind listen accept send //send是阻塞式函数 close //第一次send可以正常执行,此时因为连接已经断开了,服务器会收到一个RST报文 //第二次send时,服务器会收到一个SIGPIPE信号 ,该信号的默认处理方式是终结进程。 //总结:client的连接关闭,会导致服务器进程奔溃,是一个不能接受的情况,因为服务器要服务其他的客户端。 ————————client : socket connect recv close ————————server : socket bind listen accept send sleep() send //服务器往一个已经断开了的连接上继续发送数据,会造成什么影响? close //数据在发送时是字节流,不是一个个的数据包 //数据之间是没有边界的概念 =》 TCP粘包问题 ————————client : socket connect recv recv close ————————server : socket bind listen accept send send close //recv的返回值为0的情况 ————————client : socket connect recv recv close ————————server : socket bind listen accept send close
723__01:使用select实现一个基于UDP的一对一即时聊天程序。
001:
#include <func.h> #define IP "192.168.235.128" #define PORT1 8080 #define PORT2 8081 struct sockaddr* addr_create(const char* ip,int port ){ struct sockaddr_in* addr=(struct sockaddr_in*)malloc(sizeof(struct sockaddr_in)); memset(addr,0,sizeof(*addr)); addr->sin_family=AF_INET; addr->sin_port=htons(port); addr->sin_addr.s_addr=inet_addr(ip); return (struct sockaddr*)addr; } int main() { struct sockaddr* addr=addr_create(IP,PORT1); struct sockaddr* addr1=addr_create(IP,PORT2); int sockfd=socket(AF_INET,SOCK_DGRAM,0); if(sockfd==-1){error(1,errno,"socket");} //addr1暴露一下? int err=bind(sockfd,addr1,sizeof(*addr)); if(err==-1){error(1,errno,"bind");} int epfd=epoll_create1(0); struct epoll_event epev; epev.events=EPOLLIN; epev.data.fd=sockfd; epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&epev); epev.data.fd=STDIN_FILENO;//标准输入的文件描述符,通常为 0 epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&epev); struct epoll_event epev_arr[2]; char buff[4096]; while(1){ int num=epoll_wait(epfd,epev_arr,2,-1); for(int i=0;i<num;i++){ int fd=epev_arr[i].data.fd; if(fd==STDIN_FILENO){ fgets(buff,4096,stdin); //给addr发消息 sendto(sockfd,buff,strlen(buff)+1,0,addr,sizeof(struct sockaddr)); } if(fd==sockfd){ recvfrom(sockfd,buff,4096,0,NULL,NULL); printf("REC::%s\n",buff); } } } close(sockfd); return 0; }
002: TIMEWAI OR BUG
#include <func.h> #define IP "192.168.235.128" #define IP1 "42.194.149.92" #define PORT1 8082 #define PORT2 13332 struct sockaddr* addr_create(const char* ip,int port ){ struct sockaddr_in* addr=(struct sockaddr_in*)malloc(sizeof(struct sockaddr_in)); memset(addr,0,sizeof(*addr)); addr->sin_family=AF_INET; addr->sin_port=htons(port); addr->sin_addr.s_addr=inet_addr(ip); return (struct sockaddr*)addr; } int main() { struct sockaddr* addr=addr_create(IP,PORT1); struct sockaddr* addr1=addr_create(IP1,PORT2); int sockfd=socket(AF_INET,SOCK_DGRAM,0); if(sockfd==-1){error(1,errno,"socket");} //addr暴露一下 /* int err=bind(sockfd,addr,sizeof(*addr)); */ /* if(err==-1){error(1,errno,"bind");} */ int epfd=epoll_create1(0); struct epoll_event epev; epev.events=EPOLLIN; epev.data.fd=sockfd; epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&epev); epev.data.fd=STDIN_FILENO;//标准输入的文件描述符,通常为 0 epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&epev); struct epoll_event epev_arr[2]; char buff[4096]; while(1){ int num=epoll_wait(epfd,epev_arr,2,-1); for(int i=0;i<num;i++){ int fd=epev_arr[i].data.fd; if(fd==STDIN_FILENO){ fgets(buff,4096,stdin); //给addr1发送消息 sendto(sockfd,buff,strlen(buff)+1,0,addr1,sizeof(struct sockaddr)); } if(fd==sockfd){ recvfrom(sockfd,buff,4096,0,NULL,NULL); printf("REC::%s\n",buff); } } } close(sockfd); return 0; }
721作业:
01:在一对一聊天的基础上,使用select实现一对多的回显服务。(回显服务即接收到客户端发送的数据后,再回复给客户端)
一对一的喵:
//client.c #include <func.h> #define IP1 "192.168.235.128" #define IP2 "42.194.149.92" #define PORT1 8080 #define PORT2 13332 struct sockaddr* addr_create(const char* ip,int port ){ struct sockaddr_in* addr=(struct sockaddr_in*)malloc(sizeof(struct sockaddr_in)); memset(addr,0,sizeof(*addr)); addr->sin_family=AF_INET; addr->sin_port=htons(port); addr->sin_addr.s_addr=inet_addr(ip); return (struct sockaddr*)addr; } int main() { int sofd=socket(AF_INET,SOCK_STREAM,0); if(sofd==-1){error(1,errno,"socket");} struct sockaddr* addr=addr_create(IP1,PORT1); int err=connect(sofd,addr,sizeof(*addr)); if(err==-1){error(1,errno,"connect");} printf("connect sucess\n"); fd_set set; FD_ZERO(&set); char buff[100]={0}; while(1){ FD_SET(STDIN_FILENO,&set); FD_SET(sofd,&set); select(sofd+1,&set,NULL,NULL,NULL); if(FD_ISSET(STDIN_FILENO,&set)){ memset(buff,0,sizeof(buff)); err=read(STDIN_FILENO,buff,sizeof(buff)); if(strcmp(buff,"byby\n")==0){break;} send(sofd,buff,err-1,0); } if(FD_ISSET(sofd,&set)){ memset(buff,0,sizeof(buff)); err=recv(sofd,buff,sizeof(buff),0); if(err==0){printf("byebye\n");break;} printf("ret: %d,recv:%s \n",err,buff); } } close(sofd); return 0; }
//server.c #include <func.h> #define IP1 "192.168.235.128" #define IP2 "42.194.149.92" #define PORT1 8080 #define PORT2 13332 struct sockaddr* addr_create(const char* ip,int port ){ struct sockaddr_in* addr=(struct sockaddr_in*)malloc(sizeof(struct sockaddr_in)); memset(addr,0,sizeof(*addr)); addr->sin_family=AF_INET; addr->sin_port=htons(port); addr->sin_addr.s_addr=inet_addr(ip); return (struct sockaddr*)addr; } int main() { int sofd=socket(AF_INET,SOCK_STREAM,0); if(sofd==-1){error(1,errno,"socket");} struct sockaddr* addr=addr_create(IP1,PORT1); int err=bind(sofd,addr,sizeof(*addr)); if(err==-1){error(1,errno,"bimd");} printf("bind sucess\n"); /* struct sockaddr_in* addrin=(struct sockaddr_in*)addr; */ /* printf("%s,%d\n",inet_ntoa(addr->sin_addr), */ /* sizeof((struct sockaddr_in*)addr.sin.port)); */ err=listen(sofd,1); if(err==-1){error(1,errno,"listen");} struct sockaddr_in addr2; socklen_t len=sizeof(addr2); int peerfd=accept(sofd,(struct sockaddr*)&addr2,&len); printf("client :%s:%d has connect\n",inet_ntoa(addr2.sin_addr), ntohs(addr2.sin_port)); // sofd--->peerfd fd_set set; FD_ZERO(&set); char buff[100]={0}; while(1){ FD_SET(STDIN_FILENO,&set); FD_SET(peerfd,&set); select(peerfd+1,&set,NULL,NULL,NULL); if(FD_ISSET(STDIN_FILENO,&set)){ memset(buff,0,sizeof(buff)); err=read(STDIN_FILENO,buff,sizeof(buff)); if(strcmp(buff,"byby\n")==0){break;} send(peerfd,buff,err-1,0); } if(FD_ISSET(peerfd,&set)){ memset(buff,0,sizeof(buff)); err=recv(peerfd,buff,sizeof(buff),0); if(err==0){printf("byebye\n");break;} printf("ret: %d,recv:%s \n",err,buff); } } close(sofd); return 0; }
一对多的服务器喵:
#include <func.h> #define IP1 "192.168.235.128" #define IP2 "42.194.149.92" #define PORT1 8080 #define PORT2 13332 struct sockaddr* addr_create(const char* ip,int port ){ struct sockaddr_in* addr=(struct sockaddr_in*)malloc(sizeof(struct sockaddr_in)); memset(addr,0,sizeof(*addr)); addr->sin_family=AF_INET; addr->sin_port=htons(port); addr->sin_addr.s_addr=inet_addr(ip); return (struct sockaddr*)addr; } int main() { int sofd=socket(AF_INET,SOCK_STREAM,0); if(sofd==-1){error(1,errno,"socket");} struct sockaddr* addr=addr_create(IP1,PORT1); int err=bind(sofd,addr,sizeof(*addr)); if(err==-1){error(1,errno,"bimd");} printf("bind sucess\n"); /* struct sockaddr_in* addrin=(struct sockaddr_in*)addr; */ /* printf("%s,%d\n",inet_ntoa(addr->sin_addr), */ /* sizeof((struct sockaddr_in*)addr.sin.port)); */ err=listen(sofd,1); if(err==-1){error(1,errno,"listen");} struct sockaddr_in addr2; socklen_t len=sizeof(addr2); int peerfd=accept(sofd,(struct sockaddr*)&addr2,&len); printf("client :%s:%d has connect\n",inet_ntoa(addr2.sin_addr), ntohs(addr2.sin_port)); // sofd--->peerfd fd_set set; FD_ZERO(&set); char buff[100]={0}; while(1){ FD_SET(STDIN_FILENO,&set); FD_SET(peerfd,&set); select(peerfd+1,&set,NULL,NULL,NULL); if(FD_ISSET(STDIN_FILENO,&set)){ memset(buff,0,sizeof(buff)); err=read(STDIN_FILENO,buff,sizeof(buff)); if(strcmp(buff,"byby\n")==0){break;} send(peerfd,buff,err-1,0); } if(FD_ISSET(peerfd,&set)){ memset(buff,0,sizeof(buff)); err=recv(peerfd,buff,sizeof(buff),0); if(err==0){printf("byebye\n");break;} printf("ret: %d,recv:%s \n",err,buff); } } close(sofd); return 0; }
02:使用select编写聊天室程序:客户端和服务端使用tcp通信;服务端可以处理新客户端的连接和转发消息;客户端可以连入服务端并发送消息。
//server.c #include <func.h> #define IP1 "192.168.235.128" #define IP2 "42.194.149.92" #define PORT1 8080 #define PORT2 13332 //聊天室服务端 typedef struct conn_s{ int netfd; int isalive; }conn_t; struct sockaddr* addr_create(const char* ip,int port ){ struct sockaddr_in* addr=(struct sockaddr_in*)malloc(sizeof(struct sockaddr_in)); memset(addr,0,sizeof(*addr)); addr->sin_family=AF_INET; addr->sin_port=htons(port); addr->sin_addr.s_addr=inet_addr(ip); return (struct sockaddr*)addr; } int main(int argc,char* argv[]) { struct sockaddr* addr=addr_create(IP1,PORT1); int sofd=socket(AF_INET,SOCK_STREAM,0); if(sofd==-1){error(1,errno,"socket");} bind(sofd,(struct sockaddr *)&addr,sizeof(addr)); listen(sofd,10); //用于管理文件描述符集合,用于指示哪些文件描述符正在监听的 I/O 事件已经发生 fd_set set;//select 监听的集合 FD_ZERO(&set); FD_SET(sofd,&set); conn_t list[1024]; memset(list,0,sizeof(list)); int index; while(1){ fd_set temp_set;//构建此次的监听合计 memcpy(&temp_set,&set,sizeof(set));//cp ser temp_set select (10,&temp_set,NULL,NULL,NULL); if(FD_ISSET(sofd,&temp_set)){ int netfd=accept(sofd,NULL,NULL); list[index].isalive=1; list[index].netfd=netfd; FD_SET(netfd,&set);//增加监听 index++; } for(int i=0;i<index;i++){ conn_t con=list[i]; //isalive && isset==has new massege if(con.isalive==1&&FD_ISSET(con.netfd,&temp_set)){ char buff[100]={0}; int res_recv=recv(con.netfd,buff,sizeof(buff),0); if(res_recv==0){ //return 0 disconnect list[i].isalive=0; FD_CLR(con.netfd,&set); close(list[i].netfd); }else{ //recv msg-->send client for(int j=0;j<index;j++){ if(list[j].isalive==0||j==i){ continue; //跳过断开连接的客户端,跳过发信息过来的客户端 } send(list[j].netfd,buff,sizeof(buff),0); } } } } } printf("学姐我饿了\n"); printf("我也是\n"); close(sofd); free(addr); return 0; }