Linux下實現簡單Echo中繼伺服器
阿新 • • 發佈:2019-02-01
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;
}