目录
TCP协议喵
723__01:使用select实现一个基于UDP的一对一即时聊天程序。
001:
002: TIMEWAI OR BUG
721作业:
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 8081struct 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;//标准输入的文件描述符,通常为 0epoll_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 13332struct 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;//标准输入的文件描述符,通常为 0epoll_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 13332struct 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 13332struct 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--->peerfdfd_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 13332struct 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--->peerfdfd_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_setselect (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 massegeif(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 disconnectlist[i].isalive=0;FD_CLR(con.netfd,&set);close(list[i].netfd);}else{//recv msg-->send clientfor(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;
}