是否有时候因为没有U盘拷贝文件而烦恼?如果远程的两个文件夹能像绑定挂载一样多好!
若在一个文件夹里删除,添加,远程的文件夹也会保持同步!
我在Linux系统上用C语言实现了这个功能,目前只在Ubuntu18系统上测试过,其他Linux系统应该也没问题。
Gitee gitee
GitHub github
下面是演示视频(视频里是同一个主机,实际使用时可以在不同的主机上进行文件同步)
演示视频
喜欢的兄弟给我点个Star吧,感谢!
备注: 目前只支持一个文件夹里面的常规文件,不支持文件夹嵌套更新
功能简介
本项目旨在实现两个远程文件夹内容同步(linux系统)
主要工作
1. 使用C语言,基于linux系统提供的Inotify和socket库,实现两个远程文件夹内容的自动同步
2. 采用零拷贝技术,高效上传文件数据
3. 高并发,断线重连
//
// Created by xia on 2022/4/15.
// 常用方法封装
#include "common.h"
ssize_t readLine(int sock,char *buf,int size)
{
int i = 0;
char c = '\0';
ssize_t n;
while ((i < size - 1) && (c != '\n'))
{
n = recv(sock, &c, 1, 0);
/* DEBUG printf("%02X\n", c); */
if (n > 0)
{
if (c == '\r')
{
n = recv(sock, &c, 1, MSG_PEEK);
/* DEBUG printf("%02X\n", c); */
if ((n > 0) && (c == '\n'))
recv(sock, &c, 1, 0);
else
c = '\n';
}
buf[i] = c;
i++;
}
else
c = '\n';
}
buf[i] = '\0';
return i;
}
Boolean sendInfo(int fd,const char *header_type,const char *arg1,const char *arg2)
{
if(header_type == NULL)
return FALSE;
static char buf[BUF_SIZE];
if(arg1 == NULL)
arg1 = "";
if(arg2 == NULL)
arg2 = "";
sprintf(buf,"%s|%s|%s\r\n",header_type,arg1,arg2);
return write(fd,buf, strlen(buf)) == strlen(buf);
}
Boolean sendMsg(int fd,const char *format,...)
{
va_list ap;
va_start(ap,format);
static char buf[BUF_SIZE];
vsnprintf(buf,BUF_SIZE,format,ap);
va_end(ap);
return sendInfo(fd,STR_CMD[MESSAGE],buf,NULL);
}
Boolean isSock(int fd)
{
static struct stat stat_buf;
if(fstat(fd,&stat_buf) == 0)
{
if(S_ISSOCK(stat_buf.st_mode))
return TRUE;
}
return FALSE;
}
void safeCloseSock(int fd)
{
if(isSock(fd))
close(fd);
}
int isConnected(int fd)
{
if(!isSock(fd))
return 0;
static struct tcp_info info;
socklen_t size = sizeof(info);
getsockopt(fd,IPPROTO_TCP,TCP_INFO,&info,&size);
if(info.tcpi_state == TCP_ESTABLISHED)
return 1;
return 0;
}
int setNonBlock(int fd)
{
int flag = fcntl(fd,F_GETFL);
return fcntl(fd,F_SETFL,flag|O_NONBLOCK);
}
int addEpollInLetEvent(int epoll_fd,int fd)
{
struct epoll_event event;
event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
event.data.fd = fd;
return epoll_ctl(epoll_fd,EPOLL_CTL_ADD,fd,&event);
}
int addEpollInLevelEvent(int epoll_fd,int fd)
{
struct epoll_event event;
event.events = EPOLLIN | EPOLLRDHUP;
event.data.fd = fd;
return epoll_ctl(epoll_fd,EPOLL_CTL_ADD,fd,&event);
}
错误处理函数
//
// Created by xia on 2022/3/2.
//
#include "stdarg.h"
#include "error_handlers.h"
#include "ename.c.inc"
#include "common.h"
#ifdef __GNUC__
__attribute__ ((__noreturn__))
#endif
static void terminate(Boolean useExit3)
{
char *s;
s= getenv("EF_DUMPCORE");
if(s!=NULL && *s != '\0')
abort();
else if (useExit3)
exit(EXIT_FAILURE);
else
_exit(EXIT_FAILURE);
}
static void outputError(Boolean useErr,int err,Boolean flushStdout,const char *format, va_list ap)
{
char buf[BUF_SIZE],userMsg[BUF_SIZE],errText[BUF_SIZE];
vsnprintf(userMsg,BUF_SIZE,format,ap);
if(useErr)
snprintf(errText,BUF_SIZE," [%s %s]",(err>0&&err<=MAX_ENAME)?ename[err]:"?UNKNOW?",
strerror(err));
else
snprintf(errText,BUF_SIZE,":");
snprintf(buf,BUF_SIZE,"ERROR%s %s\n",errText,userMsg);
if(flushStdout)
fflush(stdout);
fputs(buf,stderr);
fflush(stderr);
}
void errMsg(const char *format,...)
{
va_list argList;
int savedErrno;
savedErrno = errno;
va_start(argList,format);
outputError(TRUE,errno,TRUE,format,argList);
va_end(argList);
errno = savedErrno;
}
void errExit(const char *format,...) {
va_list argList;
va_start(argList, format);
outputError(TRUE, errno, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
void err_exit(const char *format,...)
{
va_list argList;
va_start(argList,format);
outputError(TRUE,errno,FALSE,format,argList);
va_end(argList);
terminate(FALSE);
}
void errExitEn(int errnum,const char *format,...)
{
va_list argList;
va_start(argList,format);
outputError(TRUE,errnum,TRUE,format,argList);
va_end(argList);
terminate(TRUE);
}
void fatal(const char *format,...)
{
va_list argList;
va_start(argList,format);
outputError(FALSE,0,TRUE,format,argList);
va_end(argList);
terminate(TRUE);
}
void usageErr(const char *format,...)
{
va_list argList;
fflush(stdout);
fprintf(stderr,"Usage: ");
va_start(argList,format);
vfprintf(stderr,format,argList);
va_end(argList);
fflush(stderr);
exit(EXIT_FAILURE);
}
void cmdLineErr(const char *format,...)
{
va_list argList;
fflush(stdout);
fprintf(stderr,"Command-line usage error: ");
va_start(argList,format);
vfprintf(stderr,format,argList);
va_end(argList);
fflush(stderr);
exit(EXIT_FAILURE);
}
客户端代码
//
// Created by xia on 2022/4/13.
//
#include
#include "client.h"
int createClientSocket(const char *ip,int port)
{
int sock;
int reuse = 1;
struct sockaddr_in server_address;
memset(&server_address,0,sizeof(struct sockaddr_in));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port);
if(inet_pton(AF_INET,ip,&server_address.sin_addr) <= 0)
{
LOG_ERROR("ip: %s is not correct!",ip);
return -1;
}
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
LOG_ERROR("socket error!");
return -1;
}
/* Address can be reused instantly after program exits */
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse);
/* Bind socket to server address */
if(connect(sock,(struct sockaddr*) &server_address, sizeof(server_address)) < 0){
LOG_ERROR("Cannot connect socket to address");
return -1;
}
return sock;
}
/*
* when a file was created or changed,this function will be used.
* */
void handleCloseWrite(struct inotify_event *event,int client_fd)
{
static char buf[BUF_SIZE];
if(event->len > 0)
{
int fd = open(event->name,O_RDONLY);
if(fd == -1)
{
LOG_WARN("Could not open file: %s",event->name);
return;
}
off_t size = lseek(fd,0,SEEK_END);
if(size == (off_t)-1)
{
LOG_WARN("lseek failed: %s",event->name);
return;
}
lseek(fd,0,SEEK_SET);
snprintf(buf,BUF_SIZE,"%ld",(long int)size);
ssize_t curSend,numSend = 0;
int val = 1;
setsockopt(client_fd,IPPROTO_TCP,TCP_CORK,&val,sizeof(val));
clock_t start = clock();
if(sendInfo(client_fd,STR_CMD[UPDATE],event->name,buf))
{
while (numSend < size)
{
curSend = sendfile(client_fd,fd,NULL,size-numSend);
if(curSend == -1)
{
if(lseek(fd,0,SEEK_CUR)==size)
break;
else
continue;
}
numSend += curSend;
}
}
val = 0;
setsockopt(client_fd,IPPROTO_TCP,TCP_CORK,&val,sizeof(val));
if(numSend != size)
LOG_WARN("file: %s has %ld bytes,but send %ld bytes",event->name,size,numSend);
clock_t end = clock();
double seconds = (double )(end-start)/CLOCKS_PER_SEC;
double speed = (double)numSend/(1024 * 1024 * seconds);
LOG_INFO("end send file: %s,use %.2f seconds,speed %.2f Mb/s",event->name,seconds,speed);
close(fd);
}
}
/*
* when a file was deleted,use this function
* */
void handleDelete(struct inotify_event *event,int client_fd)
{
if(event->len > 0)
sendInfo(client_fd,STR_CMD[DELETE],event->name,NULL);
}
/*
* when the watched dir was deleted,should notify the pair don't send anything
* */
void handleDeleteSelf(struct inotify_event *event,int client_fd)
{
sendInfo(client_fd,STR_CMD[DELETE_SELF],NULL,NULL);
}
/*
* when the file was renamed,this function was activate
* */
void handleRename(struct inotify_event *event,int client_fd)
{
static uint32_t cookie;
static char old[BUF_SIZE];
if(event->len > 0)
{
if(event->mask & IN_MOVED_TO)
{
if(event->cookie == cookie)
{
sendInfo(client_fd,STR_CMD[RENAME],old,event->name);
LOG_INFO("rename file from %s to %s",old,event->name);
}
}
else if(event->mask & IN_MOVED_FROM)
{
cookie = event->cookie;
strcpy(old,event->name);
}
}
}
/*
* handle an inotify event
* */
void disparityEvent(struct inotify_event *event,int client_fd)
{
if(CouldSynchronous)
{
if(event->mask & IN_CLOSE_WRITE)
handleCloseWrite(event,client_fd);
if(event->mask & IN_DELETE)
handleDelete(event,client_fd);
if(event->mask & IN_DELETE_SELF)
handleDeleteSelf(event,client_fd);
if(event->mask & IN_MOVE)
handleRename(event,client_fd);
}
else
{
LOG_WARN("since the pair is not online,file change will not synchronous");
}
}
/*
* add a dir to Inotify List
* */
int addInotifyDirWatch(int inotifyFd,const char *dir,uint32_t mask)
{
static struct stat stat_buf;
if(stat(dir,&stat_buf) == -1 || !S_ISDIR(stat_buf.st_mode))
{
LOG_ERROR("file: %s is not exists or not a dir",dir);
return -1;
}
return inotify_add_watch(inotifyFd,dir,mask);
}
/*
* read inotify event from kernel
* */
void handleInotifyEvent(int inotifyFd,int client_fd)
{
static struct inotify_event *event;
static char buf[BUF_LEN];
ssize_t numRead;
while ((numRead = read(inotifyFd,buf,BUF_LEN)) > 0)
{
for(char *p = buf;p<buf + numRead;)
{
event = (struct inotify_event *)p;
disparityEvent(event,client_fd);
p += sizeof(struct inotify_event) + event->len;
}
memset(buf,0,BUF_LEN);
}
}
/*
* when a header is not valid,we should drop the remaining data
* */
void dropData(int fd)
{
static char buf[BUF_SIZE];
ssize_t numRead;
if(isConnected(fd))
{
while (TRUE)
{
numRead = read(fd,buf,BUF_SIZE);
if(numRead == -1)
{
if(errno == EAGAIN || errno == EWOULDBLOCK)
break;
else
err_exit("read");
}
}
}
}
/*
* we receive an update head,we should create and update the file
* */
void handleTcpUpdate(int client_fd,const char *filename,const char* size)
{
if(filename == NULL || size == NULL)
return;
int fd = open(filename,O_WRONLY|O_TRUNC|O_CREAT,0664);
if(fd == -1)
{
LOG_WARN("Could not open file: %s,so suspend the update",filename);
return;
}
char *endPtr;
ssize_t totalSize = strtol(size,&endPtr,10);
if(totalSize < 0)
{
LOG_WARN("the header size is %ld,so suspend the update",totalSize);
return;
}
ssize_t curWrite,numWrite=0;
ssize_t curRead;
static char buf[BUF_SIZE];
clock_t start = clock();
while (numWrite < totalSize)
{
curRead = read(client_fd,buf,BUF_SIZE);
if(curRead == -1)
{
if(errno == EAGAIN || errno == EWOULDBLOCK)
continue;
else
err_exit("read");
}
if((curWrite = write(fd,buf,curRead)) == -1)
{
LOG_WARN("write data to disk failed");
break;
}
else
numWrite+=curWrite;
}
clock_t end = clock();
close(fd);
if(numWrite != totalSize)
LOG_WARN("file: %s has %ld bytes,but received %ld bytes",filename,totalSize,numWrite);
double seconds = (double )(end-start)/CLOCKS_PER_SEC;
double speed = (double)numWrite/(1024 * 1024 * seconds);
LOG_INFO("end handleTcpUpdate file: %s,use %.2f seconds,speed %.2f Mb/s",filename,seconds,speed);
}
/*
* Delete file
* */
void handleTcpDelete(const char *filename)
{
if(filename == NULL)
{
LOG_WARN("delete filename is NULL");
return;
}
if(remove(filename) == -1)
LOG_WARN("remove file %s failed",filename);
}
/*
* Rename a file
* */
void handleTcpRename(const char *old,const char *new)
{
if(old==NULL || new == NULL)
{
LOG_WARN("old filename or new filename is empty");
return;
}
if(rename(old,new) == -1)
LOG_WARN("rename file %s to %s failed",old,new);
}
/*
* receive Tcp data,and handle the event
* */
void handleTcpEvent(int client_fd)
{
char buf[BUF_SIZE];
ssize_t numRead;
numRead = readLine(client_fd,buf,BUF_SIZE);
if(numRead == 0)
{
LOG_WARN("readLine return zero");
return;
}
if(numRead == BUF_SIZE - 1) //drop the too long data
{
LOG_WARN("header is too long,the data will");
dropData(client_fd);
return;
}
char *cmd,*arg1,*arg2;
cmd = strtok(buf,"|");
if(cmd == NULL)
{
LOG_WARN("command is NULL,please check the header format");
dropData(client_fd);
return;
}
if(strcmp(cmd,STR_CMD[MESSAGE]) != 0)
{
arg1 = strtok(NULL,"|");
arg2 = strtok(NULL,"|\n");
}
if(strcmp(cmd,STR_CMD[DELETE_SELF]) == 0)
{
LOG_WARN("the pair dir was deleted,turn off synchronous");
CouldSynchronous = FALSE;
}
else if(strcmp(cmd,STR_CMD[UNPAIRED]) == 0)
{
LOG_WARN("the pair was quit,turn off synchronous");
CouldSynchronous = FALSE;
}
else if(strcmp(cmd,STR_CMD[PAIRED]) == 0)
{
CouldSynchronous = TRUE;
LOG_WARN("the pair is online,turn on synchronous");
}
else if(strcmp(cmd,STR_CMD[MESSAGE]) == 0)
{
arg1 = strtok(NULL,"|\n");
LOG_INFO("Receive Message-> %s", (arg1==NULL)?"EMPTY MESSAGE":arg1);
}
else if (strcmp(cmd,STR_CMD[RENAME]) == 0)
handleTcpRename(arg1,arg2);
else if (strcmp(cmd,STR_CMD[DELETE]) == 0)
handleTcpDelete(arg1);
else if(strcmp(cmd,STR_CMD[UPDATE]) == 0)
handleTcpUpdate(client_fd,arg1,arg2);
else
LOG_WARN("UNKNOWN COMMAND");
dropData(client_fd);
}
int main(int argc,char *argv[])
{
if(argc!=6|| strcmp(argv[1],"--help") == 0)
usageErr("%s ip port username password dirname\n",argv[0]);
int inotifyFd,client_fd,epoll_fd,dir_fd,port;
port = (int)strtol(argv[2],NULL,10);
inotifyFd = inotify_init();
if(inotifyFd == -1)
errExit("create inotify failed");
if(setNonBlock(inotifyFd) == -1)
errExit("setNonBlock inotifyFd");
dir_fd = addInotifyDirWatch(inotifyFd,argv[5],IN_ALL_EVENTS);
if( dir_fd == -1)
errExit("failed to add %s",argv[5]);
if(chdir(argv[5]) == -1)
errExit("chdir");
epoll_fd = epoll_create(2);
if(epoll_fd < 0)
errExit("epoll_create");
client_fd = createClientSocket(argv[1], port);
if(client_fd < 0)
errExit("createClientSocket");
if(setNonBlock(client_fd) == -1)
errExit("setNonBlock client_fd");
if(addEpollInLetEvent(epoll_fd,inotifyFd) == -1)
errExit("addEpollInLetEvent inotifyFd");
if(addEpollInLetEvent(epoll_fd,client_fd) == -1)
errExit("addEpollInLetEvent client_fd");
int eventRead,i;
struct epoll_event events[2];
sendInfo(client_fd,STR_CMD[CONNECTING],argv[3],argv[4]);
while (TRUE)
{
eventRead = epoll_wait(epoll_fd,events,2,-1);
if(eventRead == -1)
{
if(errno == EINTR)
continue;
else
errExit("epoll_wait");
}
for (i = 0; i < eventRead; ++i) {
if(events[i].events & EPOLLIN)
{
if(events[i].data.fd == inotifyFd)
handleInotifyEvent(inotifyFd,client_fd);
else if(events[i].data.fd == client_fd)
{
if(inotify_rm_watch(inotifyFd,dir_fd) < 0)
errExit("inotify_rm_watch failed");
handleTcpEvent(client_fd);
dir_fd = addInotifyDirWatch(inotifyFd,argv[5],IN_ALL_EVENTS);
if(dir_fd < 0)
errExit("addInotifyDirWatch failed,probably the dir was deleted");
}
}
if(events[i].events & (EPOLLHUP | EPOLLERR | EPOLLRDHUP))
{
close(events[i].data.fd);
close(epoll_fd);
close(client_fd);
errExit("connection was closed");
}
}
}
}
服务器端代码
//
// Created by xia on 2022/4/15.
//
#define _GNU_SOURCE
#include "server.h"
#include "sys/wait.h"
#include "signal.h"
#include "fcntl.h"
/*
* clear an entry
* */
void clearEntry(int pos)
{
entries[pos].status = OFFLINE;
entries[pos].pid = -1;
safeCloseSock(entries[pos].first_fd);
entries[pos].first_fd = -1;
safeCloseSock(entries[pos].second_fd);
entries[pos].second_fd = -1;
entries[pos].username[0] = '\0';
entries[pos].password[0] = '\0';
}
void init()
{
LOG_INFO("init");
for(int i=0;i<MAX_PAIRS;i++)
clearEntry(i);
}
/*
* find entry by name,if don't find,return NULL
* */
int findEntryByName(const char *name)
{
for(int i=0;i<MAX_PAIRS;i++)
{
if(strcmp(entries[i].username,name) == 0)
return i;
}
return -1;
}
int findEntryByPid(pid_t pid)
{
for(int i=0;i<MAX_PAIRS;i++)
{
if(entries[i].pid == pid)
return i;
}
return -1;
}
/*
* find unused entry,except RUNNING status
* */
int findUnusedEntry()
{
for(int i=0;i<MAX_PAIRS;i++)
{
if(entries[i].status == OFFLINE)
return i;
}
for(int i=0;i<MAX_PAIRS;i++)
{
if(entries[i].status == SINGLE)
return i;
}
for(int i=0;i<MAX_PAIRS;i++)
{
if(entries[i].status == COUPLE)
return i;
}
return -1;
}
void updateOneEntry(int pos)
{
int first = isConnected(entries[pos].first_fd);
int second = isConnected(entries[pos].second_fd);
if(first+ second == 2 && entries[pos].status == RUNNING)
return;
else if(first + second == 1)
{
if(second == 1)
{
entries[pos].first_fd = entries[pos].second_fd;
entries[pos].second_fd = -1;
}
entries[pos].status = SINGLE;
entries[pos].pid = -1;
//sendInfo(entries[pos].first_fd,STR_CMD[UNPAIRED],NULL,NULL);
LOG_INFO("entry %d exist one established connection,name: %s",pos,entries[pos].username);
return;
}
clearEntry(pos);
}
/*
* update entry,if there are one established connection
* update the status to single
* */
void updateAllEntry()
{
LOG_INFO("start updateEntry");
for(int i=0;i<MAX_PAIRS;i++)
updateOneEntry(i);
LOG_INFO("end updateEntry");
}
/*
* insert an entry by name and password
* return -1 if insert fail
* return the pos of entry
* check the status of entry to determine the next step
* */
int insertEntry(const char *name,const char *password,int fd)
{
if(name == NULL || password == NULL)
return -1;
updateAllEntry();
int pos = findEntryByName(name);
if(pos==-1)
{
LOG_INFO("Couldn't find name: %s,try to find unused position",name);
pos = findUnusedEntry();
if(pos == -1)
{
LOG_WARN("Couldn't find an unused position to name: %s",name);
return -1;
}
clearEntry(pos);
strcpy(entries[pos].username,name);
strcpy(entries[pos].password,password);
entries[pos].status = SINGLE;
entries[pos].first_fd = fd;
entries[pos].second_fd = -1;
return pos;
}
else if(entries[pos].status == SINGLE)
{
if(strcmp(entries[pos].password,password) == 0 )
{
entries[pos].status = COUPLE;
entries[pos].second_fd = fd;
return pos;
} else
LOG_WARN("password for name: %s is not correct",name);
}
return -1;
}
/*
* clear the entry if subprocess exit
* if not found the entry or the entry is using,return -1 else return 0
* */
void clearEntryById(pid_t pid)
{
int pos = findEntryByPid(pid);
if(pos == -1)
{
LOG_WARN("Couldn't find entry for pid: %d",pid);
return;
}
updateOneEntry(pos);
}
/*
* when a subprocess exit,update the entry status
* */
void sigChildHandler(int sig)
{
pid_t pid;
while ((pid = waitpid(-1,NULL,WNOHANG)) > 0)
clearEntryById(pid);
}
int createServerSocket(const char *ip,int port)
{
int sock;
int reuse = 1;
struct sockaddr_in server_address;
memset(&server_address,0,sizeof(struct sockaddr_in));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port);
if(inet_pton(AF_INET,ip,&server_address.sin_addr) <= 0)
{
LOG_ERROR("ip: %s is not correct!",ip);
return -1;
}
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
LOG_ERROR("socket error!");
return -1;
}
/* Address can be reused instantly after program exits */
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse);
/* Bind socket to server address */
if(bind(sock,(struct sockaddr*) &server_address, sizeof(server_address)) < 0){
LOG_ERROR("Cannot connect socket to address");
return -1;
}
listen(sock,5);
return sock;
}
/*
* transmit the data between the pairs
* */
void transmit(int in_fd,int out_fd,int pipe_out_fd,int pipe_in_fd)
{
static ssize_t total = 0;
ssize_t totalReceived,totalSend;
totalReceived = splice(in_fd, NULL, pipe_out_fd, NULL,
1<<16, SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
if (totalReceived == -1) {
if(errno != EAGAIN && errno != EWOULDBLOCK)
err_exit("splice");
}
totalSend = splice(pipe_in_fd, NULL, out_fd, NULL,
1<<16, SPLICE_F_MOVE | SPLICE_F_NONBLOCK | SPLICE_F_MORE);
if (totalSend == -1) {
if(errno != EAGAIN && errno != EWOULDBLOCK)
err_exit("splice");
}
if(totalReceived != totalSend)
LOG_WARN("Server received %ld bytes,Send %ld bytes",totalReceived,totalSend);
else
LOG_INFO("Server received %ld bytes,Send %ld bytes",totalReceived,totalSend);
total += totalSend;
LOG_INFO("total is %f",total/(1024*1024.0));
}
void handleEntry(int pos)
{
int first_fd=entries[pos].first_fd,second_fd = entries[pos].second_fd;
if(isConnected(first_fd) + isConnected(second_fd) !=2)
err_exit("couple fd is not all open!");
sendInfo(first_fd,STR_CMD[PAIRED],NULL,NULL);
sendInfo(second_fd,STR_CMD[PAIRED],NULL,NULL);
int epoll_fd;
epoll_fd = epoll_create(2);
if(epoll_fd == -1)
err_exit("epoll_create");
if(addEpollInLevelEvent(epoll_fd,first_fd) == -1)
err_exit("epoll_ctl first_fd.");
if(addEpollInLevelEvent(epoll_fd,second_fd) == -1)
err_exit("epoll_ctl second_fd.");
int pipe_fd[2];
if(pipe(pipe_fd) == -1)
err_exit("piped failed");
int eventRead,i;
struct epoll_event events[2];
while (TRUE)
{
eventRead = epoll_wait(epoll_fd,events,2,-1);
if(eventRead == -1)
{
if(errno == EINTR)
continue;
else
err_exit("epoll_wait");
}
for (i = 0; i < eventRead; ++i) {
if(events[i].events & EPOLLIN)
{
if(events[i].data.fd == first_fd)
transmit(first_fd,second_fd,pipe_fd[1],pipe_fd[0]);
else if(events[i].data.fd == second_fd)
transmit(second_fd,first_fd,pipe_fd[1],pipe_fd[0]);
}
if(events[i].events & (EPOLLHUP | EPOLLERR | EPOLLRDHUP))
{
close(events[i].data.fd);
errExit("connection was closed");
}
}
}
}
int main(int argc,char *argv[])
{
if(argc!=3 || strcmp(argv[1],"--help") == 0)
usageErr("%s ip port\n",argv[0]);
int port = (int)strtol(argv[2],NULL,10);
signal(SIGCHLD,sigChildHandler);
init();
int server_fd = createServerSocket(argv[1],port);
if(server_fd == -1)
errExit("create_server_socket");
struct sockaddr_in client_address;
socklen_t socklen = sizeof(struct sockaddr_in);
int client_fd;
ssize_t numRead;
pid_t pid;
char *username,*password,*command;
char buf[BUF_SIZE];
LOG_INFO("start to receive connection");
while (TRUE)
{
socklen = sizeof(struct sockaddr_in);
client_fd = accept(server_fd,(struct sockaddr*)&client_address,&socklen);
if(client_fd < 0)
errExit("accept failed");
if(inet_ntop(AF_INET,&client_address.sin_addr,buf,BUF_SIZE)!=NULL)
LOG_INFO("Receive connection from (%s,%u)",buf, ntohs(client_address.sin_port));
numRead = readLine(client_fd,buf,BUF_SIZE);
if(numRead == 0)
{
LOG_WARN("readLine return zero");
close(client_fd);
continue;
}
if(numRead == BUF_SIZE - 1) //drop the too long data
{
LOG_WARN("header is too long,the data will");
close(client_fd);
continue;
}
command = strtok(buf,"|\n");
if(strcmp(command,STR_CMD[CONNECTING]) != 0)
{
errMsg("Command is not right!");
sendMsg(client_fd,"%s","Command is not right!");
close(client_fd);
continue;
}
username = strtok(NULL,"|\n");
password = strtok(NULL,"|\n");
int pos = insertEntry(username,password,client_fd);
if( pos == -1)
{
sendMsg(client_fd,"%s","connect error,check the name and password");
LOG_WARN("insert user: %s,password: %s failed",username,password);
close(client_fd);
continue;
}
LOG_INFO("user: %s,Status: %d",username,entries[pos].status);
if(entries[pos].status == COUPLE)
{
pid = fork();
if(pid < 0)
errExit("fork");
if(pid == 0)
{
close(server_fd);
handleEntry(pos);
_exit(0);
}
else
{
entries[pos].pid = pid;
entries[pos].status = RUNNING;
LOG_INFO("user: %s is Running,pid: %d",username,entries[pos].pid);
}
}
else if(entries[pos].status == SINGLE)
{
sendMsg(client_fd,"Server: Connect success,waiting "
"the pair to connect");
sendInfo(client_fd,STR_CMD[UNPAIRED],NULL,NULL);
}
}
}