UNIX TCP回射服务器/客户端之使用epoll模型的服务器
程序簡介:這是一個運用epoll系列函數(shù)進行IO復(fù)用的服務(wù)器模型。它是目前UNIX與LINUX平臺上效率最高,最受歡迎的IO復(fù)用傳輸模型。
其他的不說了,直接粘貼代碼吧!
服務(wù)器端:
#include "my_unp.h" int main(void) { int listenfd, connfd, sockfd, epfd; int i, maxi, nfds; ssize_t n; char buf[MAXLINE]; socklen_t clilen; struct sockaddr_in cliaddr; struct sockaddr_in servaddr; //聲明epoll_event結(jié)構(gòu)體的變量,ev用于注冊事件,數(shù)組用于回傳要處理的事件 struct epoll_event ev, events[256]; //創(chuàng)建一個epoll的句柄,size用來告訴內(nèi)核這個監(jiān)聽的數(shù)目一共有多大 epfd = Epoll_create(256); //創(chuàng)建用于TCP協(xié)議的套接字 listenfd = Socket(AF_INET, SOCK_STREAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); //把socket和socket地址結(jié)構(gòu)聯(lián)系起來 Bind(listenfd, (SA*)&servaddr, sizeof(servaddr)); //開始監(jiān)聽LISTENQ端口 Listen(listenfd, LISTENQ); //設(shè)置與要處理的事件相關(guān)的文件描述符和事件 ev.data.fd = listenfd; ev.events = EPOLLIN|EPOLLET; //注冊epoll事件 Epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd,&ev); maxi = 0; while(1) { //等待epoll事件的發(fā)生 //返回需要處理的事件數(shù)目nfds,如返回0表示已超時。 nfds = Epoll_wait(epfd, events, 20, 500); //處理所發(fā)生的所有事件 for(i=0; i < nfds; ++i) { //如果新監(jiān)測到一個SOCKET用戶連接到了綁定的SOCKET端口,建立新的連接。 if(events[i].data.fd == listenfd) { connfd = Accept(listenfd,(SA*)&cliaddr, &clilen); printf("connection from %s, port %d.\n", Inet_ntop(AF_INET, (void*)&cliaddr.sin_addr, buf, sizeof(buf)), ntohs(cliaddr.sin_port)); //設(shè)置用于讀操作的文件描述符和事件 ev.data.fd = connfd; ev.events = EPOLLIN|EPOLLET; //注冊事件 Epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); } //如果是已經(jīng)連接的用戶,并且收到數(shù)據(jù),那么進行讀入。 else if(events[i].events & EPOLLIN) { sockfd = events[i].data.fd; if ( sockfd < 0 ) continue; n = read(sockfd, buf, MAXLINE); if ( n < 0) { // Connection Reset:你連接的那一端已經(jīng)斷開了, //而你卻還試著在對方已斷開的socketfd上讀寫數(shù)據(jù)! if (errno == ECONNRESET) { Close(sockfd); events[i].data.fd = -1; } else error_quit("read error"); } //如果讀入的數(shù)據(jù)為空 else if ( n == 0 ) { Close(sockfd); events[i].data.fd = -1; } else { //設(shè)置用于寫操作的文件描述符和事件 ev.data.fd = sockfd; ev.events = EPOLLOUT|EPOLLET; //注冊事件 Epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); } } //如果有數(shù)據(jù)發(fā)送 else if(events[i].events & EPOLLOUT) { sockfd = events[i].data.fd; Writen(sockfd, buf, n); //設(shè)置用于讀操作的文件描述符和事件 ev.data.fd = sockfd; ev.events = EPOLLIN|EPOLLET; //注冊事件 Epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev); } } } return 0; }
客戶端的測試例子:
#include "my_unp.h"?
void str_cli(FILE *fp, int sockfd)??
{??
?char sendline[MAXLINE], recvline[MAXLINE];??
?int len;
?//從終端獲取一行字符串,將其寫入套接字??
?//然后從套接字一行字符串,將其寫入終端??
?while( Fgets(sendline, MAXLINE, fp) != NULL )??
?{??
? Writen(sockfd, sendline, strlen(sendline));??
? len = Read(sockfd, recvline, MAXLINE);
? if( len == 0 )??
? ?error_quit("str_cli: server terminated prematurely");??
? recvline[len] = 0;
? Fputs(recvline, stdout);??
?}??
}?
int main(int argc, char **argv)??
{??
?int sockfd;??
?struct sockaddr_in servaddr;??
?if( argc != 2 )??
? error_quit("usage: client <IPAddress>");?
?//創(chuàng)建用于TCP協(xié)議的套接字??
?sockfd = Socket(AF_INET, SOCK_STREAM, 0);??
?memset(&servaddr, 0, sizeof(servaddr));??
?servaddr.sin_family = AF_INET;??
?servaddr.sin_port = htons(SERV_PORT);?
?//將程序的參數(shù)1(argv[1])轉(zhuǎn)換成套接字地址結(jié)構(gòu)??
?Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);??
?向服務(wù)器發(fā)起連接,連接成功后client_socket代表了客戶機和服務(wù)器的一個socket連接??
?Connect(sockfd, (SA*)&servaddr, sizeof(servaddr));?
?str_cli(stdin, sockfd);??
?return 0;??
}
運行示例(紅色字體的為輸入)(假設(shè)某個回射字符串的服務(wù)器已經(jīng)啟動)
www.linuxidc.com@ubuntu:~/code$?gcc my_unp_v1.c client.c -o client
www.linuxidc.com@ubuntu:~/code$?./client 127.0.0.1
ABCD
ABCD
Ctrl+D
總結(jié)
以上是生活随笔為你收集整理的UNIX TCP回射服务器/客户端之使用epoll模型的服务器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小程序点餐系统,外卖点餐系统源码
- 下一篇: Json的优缺点