1. 程式人生 > >Linux下實現簡單Echo中繼伺服器

Linux下實現簡單Echo中繼伺服器

Linux下編寫一個Echo中繼伺服器,echo客戶端通過它獲取Echo伺服器的響應。中繼伺服器能同時作為多個echo伺服器的中繼,並且具有一個簡單的負載均衡演算法。

1. 伺服器與客戶端描述與設計
支援多個伺服器進行Echo服務,伺服器需要設定輸入埠引數,伺服器和客戶端可以直接使用多程序版本的設計即可。

2. 中繼伺服器描述與設計
為了簡化,假定所有的伺服器都在相同的ip地址上,而使用不同的埠,中繼伺服器只需要一個ip和多個埠引數的輸入。
當客戶端連線時,中繼伺服器Accept之後,開啟新的程序A,程序A向使用負載均衡演算法找出的Echo伺服器申請連線,連線成功之後,程序A建立工作執行緒P。程序A的主執行緒接收從客戶端發來的資訊併發給Echo伺服器,工作執行緒P接收從Echo伺服器發來的資訊併發給客戶端。
這樣設計是為了當客戶端退出時,能夠關閉socket連線,正常退出程式。
對於中繼伺服器還需要考慮負載均衡的,現使用最簡單的演算法,即在中繼伺服器維護一個“下次連線伺服器號”,每次有客戶端連線,就使用這個號碼找到應該連線的伺服器,然後這個號加1。

3. 埠規定
中繼伺服器在9000埠監聽客戶端的連線;
伺服器在給定的埠監聽中繼伺服器的連線。

完整程式碼如下:
1. 中繼器

// recpeater.c
#include "unp.h"
 
#define BUFFER_SIZE BUFFSIZE
#define SERVPORT 9000
#define BACKLOG 20
 
#define MAX_SERVER 10
 
char buffer_c[BUFFER_SIZE];
char buffer_s[BUFFER_SIZE];
int serAmount;
int port[MAX_SERVER];
int portCurrent;
char *serIp;
 
typedef struct m_socket {
    int s_server;
    int s_client;
} m_socket, * pm_socket;
 
void str_send_2_server(int s_server, int s_client, 
        char* buf) {
    size_t n;
 
    while (1) {
        n = Read(s_client, buf, 
            BUFFER_SIZE);
        if (n <= 0)
            return; /* EOF */
 
        Write(s_server, buf, n);
    } 
}
 
void* str_echo_2_client(void *arg) {
    size_t n;
    int s_client = ((pm_socket)arg)->s_client;
    int s_server = ((pm_socket)arg)->s_server;
 
    while (1)   {
        n = Read(s_server, buffer_c, 
            BUFFER_SIZE);
 
        if (n <= 0)
            return; /* EOF */
 
        Write(s_client, buffer_c, n);
    } 
}
 
void str_echo_via_repeater(int s_client) {
    int     s_server, ret;
    struct sockaddr_in servaddr;
 
    s_server = Socket(AF_INET, SOCK_STREAM, 0);
 
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port[portCurrent]);
    Inet_pton(AF_INET, serIp, &servaddr.sin_addr);
 
    Connect(s_server, (struct sockaddr *) &servaddr, 
        sizeof(servaddr));
 
    pthread_t tid;
    m_socket socket;
    socket.s_client = s_client;
    socket.s_server = s_server;
 
    Pthread_create(&tid, NULL, str_echo_2_client, 
        (void *) &socket);
 
    str_send_2_server(s_server, s_client, buffer_s);
 
    Shutdown(s_server, 2);
    Shutdown(s_client, 2);
 
    Close(s_server);
    Close(s_client);
}
 
void sig_chld(int signo) {
    pid_t    pid;
    int      stat;
 
    while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
        printf("child %d terminated\n", pid);
    return;
}
 
int main(int argc, char *argv[]) {
    int     listenfd, connfd;
    size_t  clilen;
    struct sockaddr_in cliaddr, servaddr;
    int i;
     
    if (argc < 3 || argc > MAX_SERVER + 2) {
        printf("usage: repeater IP port1 [port2 port3 ... port10]\n");
        return 1;
    }
 
    serIp = argv[1];
 
    serAmount = argc - 2;
    for (i=0; i<serAmount; i++)
        port[i] = atoi(argv[i+2]);
 
    portCurrent = 0;
 
    signal(SIGCHLD, sig_chld);
 
    listenfd = Socket(AF_INET, SOCK_STREAM, 0);
 
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
    servaddr.sin_port = htons(SERVPORT);
    Bind(listenfd, (struct sockaddr *) &servaddr, 
        sizeof(servaddr));
 
    Listen(listenfd, BACKLOG);
     
    while (1) {
        clilen = sizeof(cliaddr);
        connfd = Accept(listenfd, 
            (struct sockaddr *) &cliaddr, 
            &clilen);
        if (connfd < 0 && connfd == EINTR)
            continue;
        if (Fork() == 0) {
            Close(listenfd);
            str_echo_via_repeater(connfd);
            return 0;
        }
 
        // 實現負載均衡演算法
        portCurrent = (++portCurrent % serAmount);
 
        Close(connfd);  
    }
    return 0;
}

2. 伺服器
// ser.c
#include "unp.h"
 
#define BUFFER_SIZE BUFFSIZE
//#define SERVPORT 9993
#define BACKLOG 20
 
char buffer[BUFFER_SIZE];
 
void str_echo2(int sockfd, char* buf) {
    ssize_t n;
 
    while (1) {
        n = Read(sockfd, buf, BUFFER_SIZE);
 
        if (n > 0)
            Write(sockfd, buf, n);
        else
            return;
    }
}
 
void sig_chld(int signo) {
    pid_t    pid;
    int      stat;
 
    while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
        printf("child %d terminated\n", pid);
    return;
}
 
int main(int argc, char *argv[]) {
    int     listenfd, connfd;
    size_t  clilen;
    struct sockaddr_in cliaddr, servaddr;
    int SERVPORT;
     
    if (argc != 2) {
        printf("usage: ser PORT\n");
        return 1;
    }
 
    SERVPORT = atoi(argv[1]);
 
    signal(SIGCHLD, sig_chld);
 
    listenfd = Socket(AF_INET, SOCK_STREAM, 0);
 
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
    servaddr.sin_port = htons(SERVPORT);
    Bind(listenfd, (struct sockaddr *) &servaddr, 
        sizeof(servaddr));
 
    Listen(listenfd, BACKLOG);
     
    while (1) {
        clilen = sizeof(cliaddr);
        connfd = Accept(listenfd, 
            (struct sockaddr *) &cliaddr, 
            &clilen);
        if (connfd < 0 && connfd == EINTR)
            continue;
        if (Fork() ==0) {
            Close(listenfd);
            str_echo2(connfd, buffer);
 
            Close(connfd);
 
            return 0;
        }
        Close(connfd);  
    }
    return 0;
}

3. 客戶端
// cln.c
#include "unp.h"
 
#define BUFFER_SIZE BUFFSIZE
#define SERVPORT 9000
#define BACKLOG 20
 
char sbuffer[BUFFER_SIZE];
char rbuffer[BUFFER_SIZE];
 
void str_cli1(int infd, int outfd, int sockfd) {
    size_t n;
 
    while (1) {
        n = Read(infd, sbuffer, BUFFER_SIZE);
        if (n <= 0)
            return; /* EOF */
 
        Write(sockfd, sbuffer, n);
        n = Read(sockfd, rbuffer, BUFFER_SIZE);
        if (n <= 0)
            return; /* FIN */
        Write(outfd, rbuffer, n);
    } 
}
 
int main(int argc, char *argv[]) {
    int     sockfd, ret;
    struct sockaddr_in servaddr;
 
    if (argc != 2) {
        printf("usage: cln IP\n");
        return 1;
    }
 
    sockfd = Socket(AF_INET, SOCK_STREAM, 0);
 
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERVPORT);
    Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
 
    Connect(sockfd, (struct sockaddr *) &servaddr, 
        sizeof(servaddr));
    str_cli1(fileno(stdin), fileno(stdout), 
        sockfd);     /* do it all */
    Close(sockfd);
     
    return 0;
}