天天看點

我個人的Linux TCP server和client測試源碼,C語言(1)(★firecat推薦★)

第一篇 TCP server

守護程序daemonize的源碼可以借鑒redis的:

void daemonize(void) { //come from /redis/server.c/daemonize()
    int fd;
    if (fork() != 0) exit(0); /* parent exits */
    setsid(); /* create a new session */
    /* Every output goes to /dev/null. If Redis is daemonized but
     * the 'logfile' is set to 'stdout' in the configuration file
     * it will not log at all. */
    if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
        dup2(fd, STDIN_FILENO);
        dup2(fd, STDOUT_FILENO);
        dup2(fd, STDERR_FILENO);
        if (fd > STDERR_FILENO) close(fd);
    }
}      

一、echo源碼1如下,main.c,注意是.c檔案

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
 
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <sys/types.h>
 
#include <sys/resource.h>    /*setrlimit */
#include <signal.h>
#include <fcntl.h>
 
#define bool  int    //linux C中沒有bool類型
#define false 0      //linux C中沒有bool類型
#define true  1      //linux C中沒有bool類型
#define IPADDRESS   "127.0.0.1"
#define PORT        1883
#define MAXSIZE     1024
#define LISTENQ     512
#define FDSIZE      1024
#define EPOLLEVENTS 60000
#define MAXCONN     60000
 
//函數聲明
//建立套接字并進行綁定
static int socket_bind(const char* ip,int port);
//IO多路複用epoll
static void do_epoll(int listenfd);
//事件處理函數
static void handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf);
//處理接收到的連接配接
static void handle_accpet(int epollfd,int listenfd);
//讀處理
static void do_read(int epollfd,int fd,char *buf);
//寫處理
static void do_write(int epollfd,int fd,char *buf);
//添加事件
static void add_event(int epollfd,int fd,int state);
//修改事件
static void modify_event(int epollfd,int fd,int state);
//删除事件
static void delete_event(int epollfd,int fd,int state);
 
void init_signal(void)//設定信号處理,SIG_IGN表示忽略信号,SIG_DFL表示使用信号的預設處理方式
{
    signal(SIGCHLD, SIG_DFL);
    signal(SIGPIPE, SIG_IGN);
}
 
int set_fdlimit()
{
    //設定每個程序允許打開的最大檔案數
    //這項功能等價于linux終端指令 "ulimit -n 102400"
    struct rlimit rt;
    rt.rlim_max = rt.rlim_cur = MAXCONN;
    if (setrlimit(RLIMIT_NOFILE, &rt) == -1)
    {
        perror("setrlimit error");
        return -1;
    }
 
    return 0;
}
 
void daemon_run_method1()//來自https://github.com/baloonwj/flamingo
{
    int pid;
    signal(SIGCHLD, SIG_IGN);
    //1)在父程序中,fork傳回新建立子程序的程序ID;
    //2)在子程序中,fork傳回0;
    //3)如果出現錯誤,fork傳回一個負值;
    pid = fork();
    if (pid < 0)
    {
        //std::cout << "fork error" << std::endl;
        exit(-1);
    }
    //父程序退出,子程序獨立運作
    else if (pid > 0)
    {
        exit(0);
    }
 
    //之前parent和child運作在同一個session裡,parent是會話(session)的領頭程序,
    //parent程序作為會話的領頭程序,如果exit結束執行的話,那麼子程序會成為孤兒程序,并被init收養。
    //執行setsid()之後,child将重新獲得一個新的會話(session)id。
    //這時parent退出之後,将不會影響到child了。
    setsid();
    int fd;
    fd = open("/dev/null", O_RDWR, 0);
    if (fd != -1)
    {
        dup2(fd, STDIN_FILENO);
        dup2(fd, STDOUT_FILENO);
        dup2(fd, STDERR_FILENO);
    }
 
    if (fd > 2)
    {
        close(fd);
    }
}
 
bool daemon_run_method2() //Linux高性能伺服器程式設計.pdf,遊雙
{
    //建立子程序,關閉父程序,這樣可以使程式在背景進行
    pid_t pid = fork();
    if ( pid < 0 )
    {
        return false;
    }
    else if ( pid > 0 )
    {
        exit( 0 );
    }
 
    //設定檔案權限掩碼。當程序建立新檔案時,檔案的權限将是mode & 0777
    umask( 0 );
 
    //建立新的會話,設定本程序為程序組的首領
    pid_t sid = setsid();
    if ( sid < 0 )
    {
        return false;
    }
 
    //切換工作目錄
    if ( ( chdir( "/" ) ) < 0 )
    {
        /* Log the failure */
        return false;
    }
 
    //關閉标準輸入裝置、标準輸出裝置和标準錯誤輸出裝置
    close( STDIN_FILENO );
    close( STDOUT_FILENO );
    close( STDERR_FILENO );
 
    //将标準輸入、輸出和錯誤輸出都定向到/dev/null檔案
    open( "/dev/null", O_RDONLY );
    open( "/dev/null", O_RDWR );
    open( "/dev/null", O_RDWR );
    return true;
}
 
int main(int argc,char *argv[])
{
    //設定信号處理
    init_signal();
 
    //設定每個程序允許打開的最大檔案數,socket
    if (set_fdlimit() < 0)
    {
        return -1;
    }
 
    //守護者程序
    bool bdaemon = false;
    if (bdaemon)
    {
        daemon_run_method1();
    }
 
    int  listenfd;
    listenfd = socket_bind(IPADDRESS,PORT);
    listen(listenfd,LISTENQ);
    do_epoll(listenfd);
    return 0;
}
 
static int socket_bind(const char* ip,int port)
{
    int  listenfd;
    struct sockaddr_in servaddr;
    listenfd = socket(AF_INET,SOCK_STREAM,0);
    if (listenfd == -1)
    {
        perror("socket error:");
        exit(1);
    }
 
    //一個端口釋放後會等待兩分鐘之後才能再被使用,SO_REUSEADDR是讓端口釋放後立即就可以被再次使用。
    int reuse_addr = 1;
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)) == -1)
    {
        return -1;
    }
 
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    //inet_pton(AF_INET,ip,&servaddr.sin_addr);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//綁定所有網卡所有IP
    //servaddr.sin_addr.s_addr = inet_addr("172.16.6.178");
    //servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//這樣寫指代不明,當伺服器有多網卡時,不知道綁定哪個IP,導緻連接配接失敗
    servaddr.sin_port = htons(port);
    if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
    {
        perror("bind error: ");
        exit(1);
    }
 
    printf("listen on: %d,listenfd=%d\n",PORT,listenfd);
    return listenfd;
}
 
static void do_epoll(int listenfd)
{
    int epollfd;
    struct epoll_event events[EPOLLEVENTS];
    int ret;
    char buf[MAXSIZE];
    memset(buf,0,MAXSIZE);
    //建立一個描述符
    epollfd = epoll_create(FDSIZE);
    //添加監聽描述符事件
    add_event(epollfd,listenfd,EPOLLIN);
    for ( ; ; )
    {
        //擷取已經準備好的描述符事件
        ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1);
        handle_events(epollfd,events,ret,listenfd,buf);
    }
    close(epollfd);
}
 
static void handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf)
{
    int i;
    int fd;
    //進行選好周遊
    for (i = 0;i < num;i++)
    {
        fd = events[i].data.fd;
        //根據描述符的類型和事件類型進行處理
        if ((fd == listenfd) &&(events[i].events & EPOLLIN))
            handle_accpet(epollfd,listenfd);
        else if (events[i].events & EPOLLIN)
            do_read(epollfd,fd,buf);
        else if (events[i].events & EPOLLOUT)
            do_write(epollfd,fd,buf);
    }
}
 
static void handle_accpet(int epollfd,int listenfd)
{
    int clifd;
    struct sockaddr_in cliaddr;
    socklen_t  cliaddrlen = sizeof(cliaddr);
    clifd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen);
    if (clifd == -1)
        perror("accpet error:");
    else
    {
        printf("accept a new client: %s:%d,fd=%d\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port,clifd);
        //添加一個客戶描述符和事件
        add_event(epollfd,clifd,EPOLLIN);
    }
}
 
static void do_read(int epollfd,int fd,char *buf)
{
    int nread;
    nread = read(fd,buf,MAXSIZE);
    if (nread == -1)
    {
        perror("read error:");
        close(fd);
        delete_event(epollfd,fd,EPOLLIN);
    }
    else if (nread == 0)
    {
        fprintf(stderr,"client close,fd=%d\n",fd);
        close(fd);
        delete_event(epollfd,fd,EPOLLIN);
    }
    else
    {
        printf("read message is: %s,fd=%d\n",buf,fd);
        //修改描述符對應的事件,由讀改為寫
        modify_event(epollfd,fd,EPOLLOUT);
    }
}
 
static void do_write(int epollfd,int fd,char *buf)
{
    int nwrite;
    nwrite = write(fd,buf,strlen(buf));
    if (nwrite == -1)
    {
        perror("write error:");
        close(fd);
        delete_event(epollfd,fd,EPOLLOUT);
    }
    else
        modify_event(epollfd,fd,EPOLLIN);
    memset(buf,0,MAXSIZE);
}
 
static void add_event(int epollfd,int fd,int state)
{
    struct epoll_event ev;
    ev.events = state;//LT
    ev.data.fd = fd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
}
 
static void delete_event(int epollfd,int fd,int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
}
 
static void modify_event(int epollfd,int fd,int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
}

      

繼續閱讀