//頭文件 pub.h #ifndef _vsucess #define _vsucess #ifdef __cplusplus extern "C" { #endif //服務器創建socket int server_socket(int port); //設置非阻塞 int setnonblock(int st); //接收客戶端socket int server_accept(int st); //關閉socket int close_socket(int st); //接收消息 int socket_recv(int st); //連接服務器 int connect_server(char *ipaddr,int port); //發送消息 int socket_send(int st); //將sockaddr_in轉化成IP地址 int sockaddr_toa(const struct sockaddr_in * addr, char * ipaddr); #ifdef __cplusplus } #endif #endif
//輔助方法--pub.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>//htons()函數頭文件 #include <netinet/in.h>//inet_addr()頭文件 #include <fcntl.h> #include "pub.h" #define MAXBUF 1024 //創建socket int socket_create() { int st = socket(AF_INET, SOCK_STREAM, 0); if (st == -1) { printf("create socket failed ! error message :%s\n", strerror(errno)); return -1; } return st; } //設置服務端socket地址重用 int socket_reuseaddr(int st) { int on = 1; if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { printf("setsockopt reuseaddr failed ! error message :%s\n", strerror(errno)); //close socket close_socket(st); return -1; } return 0; } //服務器綁定--監聽端口號 int socket_bind(int st, int port) { struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); //type addr.sin_family = AF_INET; //port addr.sin_port = htons(port); //ip addr.sin_addr.s_addr = htonl(INADDR_ANY); //bind ip address if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1) { printf("bind failed ! error message :%s\n", strerror(errno)); //close socket close_socket(st); return -1; } //listen if (listen(st, 20) == -1) { printf("listen failed ! error message :%s\n", strerror(errno)); //close socket close_socket(st); return -1; } return 0; } //服務器創建socket int server_socket(int port) { if (port < 0) { printf("function server_socket param not correct !\n"); return -1; } //create socket int st = socket_create(); if (st < 0) { return -1; } //reuseaddr if (socket_reuseaddr(st) < 0) { return -1; } //bind and listen if (socket_bind(st, port) < 0) { return -1; } return st; } //連接服務器 int connect_server(char *ipaddr,int port) { if(port<0||ipaddr==NULL) { printf("function connect_server param not correct !\n"); return -1; } int st=socket_create(); if(st<0) { return -1; } //conect server struct sockaddr_in addr; memset(&addr,0,sizeof(addr)); addr.sin_family=AF_INET; addr.sin_port=htons(port); addr.sin_addr.s_addr=inet_addr(ipaddr); if(connect(st,(struct sockaddr *)&addr,sizeof(addr))==-1) { printf("connect failed ! error message :%s\n",strerror(errno)); return -1; } return st; } //設置非阻塞 int setnonblock(int st) { if (st < 0) { printf("function setnonblock param not correct !\n"); //close socket close_socket(st); return -1; } int opts = fcntl(st, F_GETFL); if (opts < 0) { printf("func fcntl failed ! error message :%s\n", strerror(errno)); return -1; } opts = opts | O_NONBLOCK; if (fcntl(st, F_SETFL, opts) < 0) { printf("func fcntl failed ! error message :%s\n", strerror(errno)); return -1; } return opts; } //接收客戶端socket int server_accept(int st) { if (st < 0) { printf("function accept_clientsocket param not correct !\n"); return -1; } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); socklen_t len = sizeof(addr); int client_st = accept(st, (struct sockaddr *) &addr, &len); if (client_st < 0) { printf("accept client failed ! error message :%s\n", strerror(errno)); return -1; } else { char ipaddr[20] = { 0 }; sockaddr_toa(&addr, ipaddr); printf("accept by %s\n", ipaddr); } return client_st; } //關閉socket int close_socket(int st) { if (st < 0) { printf("function close_socket param not correct !\n"); return -1; } close(st); return 0; } //將sockaddr_in轉化成IP地址 int sockaddr_toa(const struct sockaddr_in * addr, char * ipaddr) { if (addr == NULL || ipaddr == NULL) { return -1; } unsigned char *p = (unsigned char *) &(addr->sin_addr.s_addr); sprintf(ipaddr, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]); return 0; } //接收消息 int socket_recv(int st) { if (st < 0) { printf("function socket_recv param not correct !\n"); return -1; } char buf[MAXBUF] = { 0 }; int rc=0; rc=recv(st,buf,sizeof(buf),0); if(rc==0) { printf("client is close ! \n"); return -1; }else if(rc<0) { /* * recv錯誤信息:Connection reset by peer * 錯誤原因:服務端給客戶端發送數據,但是客戶端沒有接收,直接關閉,那么就會報錯 * 如果客戶端接受了數據,再關閉,也不會報錯,rc==0. */ printf("recv failed ! error message :%s \n",strerror(errno)); return -1; } printf("%s",buf); //send message /* memset(buf,0,sizeof(buf)); strcpy(buf,"i am server , i have recved !\n"); if(send(st,buf,strlen(buf),0)<0) { printf("send failed ! error message :%s \n",strerror(errno)); return -1; } */ return 0; } //發送消息 int socket_send(int st) { char buf[MAXBUF]={0}; while(1) { //read from keyboard read(STDIN_FILENO,buf,sizeof(buf)); if(buf[0]=='0') { break; } if(send(st,buf,strlen(buf),0)<0) { printf("send failed ! error message :%s \n",strerror(errno)); return -1; } memset(buf,0,sizeof(buf)); } return 0; }
//網絡編程服務端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>//htons()函數頭文件 #include <netinet/in.h>//inet_addr()頭文件 #include <fcntl.h> #include <sys/epoll.h> #include "pub.h" #define MAXSOCKET 20 int main(int arg, char *args[]) { if (arg < 2) { printf("please print one param!\n"); return -1; } //create server socket int listen_st = server_socket(atoi(args[1])); if (listen_st < 0) { return -1; } /* * 聲明epoll_event結構體變量ev,變量ev用於注冊事件, * 數組events用於回傳需要處理的事件 */ struct epoll_event ev, events[100]; //生成用於處理accept的epoll專用文件描述符 int epfd = epoll_create(MAXSOCKET); //把socket設置成非阻塞方式 setnonblock(listen_st); //設置需要放到epoll池里的文件描述符 ev.data.fd = listen_st; //設置這個文件描述符需要epoll監控的事件 /* * EPOLLIN代表文件描述符讀事件 *accept,recv都是讀事件 */ ev.events = EPOLLIN | EPOLLERR | EPOLLHUP; /* * 注冊epoll事件 * 函數epoll_ctl中&ev參數表示需要epoll監視的listen_st這個socket中的一些事件 */ epoll_ctl(epfd, EPOLL_CTL_ADD, listen_st, &ev); while (1) { /* * 等待epoll池中的socket發生事件,這里一般設置為阻塞的 * events這個參數的類型是epoll_event類型的數組 * 如果epoll池中的一個或者多個socket發生事件, * epoll_wait就會返回,參數events中存放了發生事件的socket和這個socket所發生的事件 * 這里強調一點,epoll池存放的是一個個socket,不是一個個socket事件 * 一個socket可能有多個事件,epoll_wait返回的是有消息的socket的數目 * 如果epoll_wait返回事件數組后,下面的程序代碼卻沒有處理當前socket發生的事件 * 那么epoll_wait將不會再次阻塞,而是直接返回,參數events里面的就是剛才那個socket沒有被處理的事件 */ int nfds = epoll_wait(epfd, events, MAXSOCKET, -1); if (nfds == -1) { printf("epoll_wait failed ! error message :%s \n", strerror(errno)); break; } int i = 0; for (; i < nfds; i++) { if (events[i].data.fd < 0) continue; if (events[i].data.fd == listen_st) { //接收客戶端socket int client_st = server_accept(listen_st); /* * 監測到一個用戶的socket連接到服務器listen_st綁定的端口 * */ if (client_st < 0) { continue; } //設置客戶端socket非阻塞 setnonblock(client_st); //將客戶端socket加入到epoll池中 struct epoll_event client_ev; client_ev.data.fd = client_st; client_ev.events = EPOLLIN | EPOLLERR | EPOLLHUP; epoll_ctl(epfd, EPOLL_CTL_ADD, client_st, &client_ev); /* * 注釋:當epoll池中listen_st這個服務器socket有消息的時候 * 只可能是來自客戶端的連接消息 * recv,send使用的都是客戶端的socket,不會向listen_st發送消息的 */ continue; } //客戶端有事件到達 if (events[i].events & EPOLLIN) { //表示服務器這邊的client_st接收到消息 if (socket_recv(events[i].data.fd) < 0) { close_socket(events[i].data.fd); //接收數據出錯或者客戶端已經關閉 events[i].data.fd = -1; /*這里continue是因為客戶端socket已經被關閉了, * 但是這個socket可能還有其他的事件,會繼續執行其他的事件, * 但是這個socket已經被設置成-1 * 所以后面的close_socket()函數都會報錯 */ continue; } /* * 此處不能continue,因為每個socket都可能有多個事件同時發送到服務器端 * 這也是下面語句用if而不是if-else的原因, */ } //客戶端有事件到達 if (events[i].events & EPOLLERR) { printf("EPOLLERR\n"); //返回出錯事件,關閉socket,清理epoll池,當關閉socket並且events[i].data.fd=-1,epoll會自動將該socket從池中清除 close_socket(events[i].data.fd); events[i].data.fd = -1; continue; } //客戶端有事件到達 if (events[i].events & EPOLLHUP) { printf("EPOLLHUP\n"); //返回掛起事件,關閉socket,清理epoll池 close_socket(events[i].data.fd); events[i].data.fd = -1; continue; } } } //close epoll close(epfd); //close server socket close_socket(listen_st); return 0; }
//網絡編程客戶端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>//htons()函數頭文件 #include <netinet/in.h>//inet_addr()頭文件 #include <fcntl.h> #include <sys/epoll.h> #include "pub.h" int main(int arg,char *args[]) { if(arg<2) { printf("please print two param !\n"); } //端口號 int port=atoi(args[2]); //服務端IP地址 char ipaddr[30]={0}; strcpy(ipaddr,args[1]); //connect server int st=connect_server(ipaddr,port); //send message //發送消息-- socket_send(st); //close socket close(st); return 0; }
.SUFFIXES:.c .o CC=gcc SRCS1=epoll_client.c\ pub.c SRCS2=epoll_server.c\ pub.c OBJS1=$(SRCS1:.c=.o) OBJS2=$(SRCS2:.c=.o) EXEC1=mclient EXEC2=mserver start:$(OBJS1) $(OBJS2) $(CC) -o $(EXEC1) $(OBJS1) $(CC) -o $(EXEC2) $(OBJS2) @echo "-------ok-----------" .c.o: $(CC) -Wall -g -o $@ -c $< clean: rm -f $(OBJS1) rm -f $(EXEC1) rm -f $(OBJS2) rm -f $(EXEC2)