server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#ifndef CONNECT_SIZE
#define CONNECT_SIZE 256
#endif
#define PORT 7777
#define MAX_LINE 2048
#define LISTENQ 20
void setNonblocking(int sockfd)
{
int opts;
opts=fcntl(sockfd,F_GETFL);
if(opts<0)
{
perror("fcntl(sock,GETFL)");
return;
}//if
opts = opts|O_NONBLOCK;
if(fcntl(sockfd,F_SETFL,opts)<0)
{
perror("fcntl(sock,SETFL,opts)");
return;
}//if
}
int main(int argc , char **argv)
{
int i, listenfd, connfd, sockfd, epfd, nfds;
ssize_t n, ret;
char buf[MAX_LINE];
socklen_t clilen;
struct sockaddr_in servaddr , cliaddr;
/*宣告epoll_event結構體變數,ev用於註冊事件,陣列用於回傳要處理的事件*/
struct epoll_event ev, events[20];
/*(1) 得到監聽描述符*/
listenfd = socket(AF_INET , SOCK_STREAM , 0);
setNonblocking(listenfd);
/*生成用於處理accept的epoll專用檔案描述符*/
epfd = epoll_create(CONNECT_SIZE);
/*設定監聽描述符*/
ev.data.fd = listenfd;
/*設定處理事件型別*/
ev.events = EPOLLIN | EPOLLET;
/*註冊事件*/
epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
/*(2) 繫結套接字*/
bzero(&servaddr , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);
bind(listenfd , (struct sockaddr *)&servaddr , sizeof(servaddr));
/*(3) 監聽*/
listen(listenfd , LISTENQ);
/*(4) 進入伺服器接收請求死迴圈*/
while(1)
{
/*等待事件發生*/
nfds = epoll_wait(epfd , events , CONNECT_SIZE , -1);
if(nfds <= 0)
continue;
printf("nfds = %d\n" , nfds);
/*處理髮生的事件*/
for(i=0 ; i<nfds ; ++i)
{
/*檢測到使用者連結*/
if(events[i].data.fd == listenfd)
{
/*接收客戶端的請求*/
clilen = sizeof(cliaddr);
if((connfd = accept(listenfd , (struct sockaddr *)&cliaddr , &clilen)) < 0)
{
perror("accept error.\n");
exit(1);
}//if
printf("accpet a new client: %s:%d\n", inet_ntoa(cliaddr.sin_addr) , cliaddr.sin_port);
/*設定為非阻塞*/
setNonblocking(connfd);
ev.data.fd = connfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd , EPOLL_CTL_ADD , connfd , &ev);
}//if
/*如果是已連結使用者,並且收到資料,進行讀入*/
else if(events[i].events & EPOLLIN){
if((sockfd = events[i].data.fd) < 0)
continue;
bzero(buf , MAX_LINE);
printf("reading the socket~~~\n");
if((n = read(sockfd , buf , MAX_LINE)) <= 0)
{
close(sockfd);
events[i].data.fd = -1;
}//if
else{
buf[n] = '\0';
printf("clint[%d] send message: %s\n", i , buf);
/*設定用於註冊寫操作檔案描述符和事件*/
ev.data.fd = sockfd;
ev.events = EPOLLOUT| EPOLLET;
epoll_ctl(epfd , EPOLL_CTL_MOD , sockfd , &ev);
}//else
}//else
else if(events[i].events & EPOLLOUT)
{
if((sockfd = events[i].data.fd) < 0)
continue;
if((ret = write(sockfd , buf , n)) != n)
{
printf("error writing to the sockfd!\n");
break;
}//if
/*設定用於讀的檔案描述符和事件*/
ev.data.fd = sockfd;
ev.events = EPOLLIN | EPOLLET;
/*修改*/
epoll_ctl(epfd , EPOLL_CTL_MOD , sockfd , &ev);
}//else
}//for
}//while
free(events);
close(epfd);
exit(0);
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#define PORT 7777
#define MAX_LINE 2048
int max(int a , int b)
{
return a > b ? a : b;
}
/*readline函式實現*/
ssize_t readline(int fd, char *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = vptr;
for (n = 1; n < maxlen; n++) {
if ( (rc = read(fd, &c,1)) == 1) {
*ptr++ = c;
if (c == '\n')
break; /* newline is stored, like fgets() */
} else if (rc == 0) {
*ptr = 0;
return(n - 1); /* EOF, n - 1 bytes were read */
} else
return(-1); /* error, errno set by read() */
}
*ptr = 0; /* null terminate like fgets() */
return(n);
}
/*普通客戶端訊息處理函式*/
void str_cli(int sockfd)
{
/*傳送和接收緩衝區*/
char sendline[MAX_LINE] , recvline[MAX_LINE];
while(fgets(sendline , MAX_LINE , stdin) != NULL)
{
write(sockfd , sendline , strlen(sendline));
bzero(recvline , MAX_LINE);
if(readline(sockfd , recvline , MAX_LINE) == 0)
{
perror("server terminated prematurely");
exit(1);
}//if
if(fputs(recvline , stdout) == EOF)
{
perror("fputs error");
exit(1);
}//if
bzero(sendline , MAX_LINE);
}//while
}
int main(int argc , char **argv)
{
/*宣告套接字和連結伺服器地址*/
int sockfd;
struct sockaddr_in servaddr;
/*判斷是否為合法輸入*/
if(argc != 2)
{
perror("usage:tcpcli <IPaddress>");
exit(1);
}//if
/*(1) 建立套接字*/
if((sockfd = socket(AF_INET , SOCK_STREAM , 0)) == -1)
{
perror("socket error");
exit(1);
}//if
/*(2) 設定連結伺服器地址結構*/
bzero(&servaddr , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
if(inet_pton(AF_INET , argv[1] , &servaddr.sin_addr) < 0)
{
printf("inet_pton error for %s\n",argv[1]);
exit(1);
}//if
/*(3) 傳送連結伺服器請求*/
if(connect(sockfd , (struct sockaddr *)&servaddr , sizeof(servaddr)) < 0)
{
perror("connect error");
exit(1);
}//if
/*呼叫訊息處理函式*/
str_cli(sockfd);
exit(0);
}
complied & run
$ gcc server.c -o server
$ gcc client.c -o client
$ ./server &
$ ./client 127.0.0.1