每次運行客戶程序,在命令行參數指定服務器的ip地址,端口,發起連接的子進程數,和一個待發送的字符串數據,客戶程序將模擬多個客戶根據指定的子進程數創建子進程來並發的連接到服務器,並發送數據,服務器收到數據後都原樣的回發給客戶,是一點典型的回射服務器。
#include "net.h"
char *addr = NULL;
char *request = NULL;
unsigned int port;
int connCount;
int clientfd;
void client_deal()
{
char *buf = NULL;
int len;
Tcp_connect(addr, port, &clientfd);
if (sendAll(clientfd, request, strlen(request)) > 0)
{
len = recvAll(clientfd, (void**)&buf);
if (len > 0)
{
buf[len] = 0;
printf("%s\n", buf);
}
}
freePtr(buf);
Close(clientfd);
exit(0);
}
int main(int argc, char **argv)
{
if (argc != 5)
{
printf("use [ip] [port] [connCount] [request]\n");
exit(-1);
}
addr = argv[1];
port = atoi(argv[2]);
connCount = atoi(argv[3]);
request = argv[4];
for (int i=0; i<connCount; ++i)
{
if (fork() == 0)
{
client_deal();
}
}
while (wait(NULL) > 0);
if (errno != ECHILD)
{
perror("wait error");
exit(-1);
}
return 0;
}
在處理完成某個客戶的請求之後才轉向下一個客戶,比較少見,雖然總的服務時間稍慢,但需要進程控制
#include "net.h"
int listenfd;
void server_deal()
{
char *buf = NULL;
ssize_t size;
int clifd;
Accept(listenfd, NULL, NULL, &clifd);
printf("有新連接\n");
if ( (size = recvAll(clifd, (void**)&buf)) > 0)
sendAll(clifd, buf, size);
freePtr(buf);
Close(clifd);
}
int main()
{
Tcp_listen("INADDR_ANY", 9999, 5, &listenfd);
while (1)
{
server_deal();
}
return 0;
}
每個客戶fork出一個子進程並發的去處理請求,總服務器時間稍短,fork子進程比較耗費CPU時間
#include "net.h"
int listenfd;
int clifd;
void server_deal()
{
char *buf = NULL;
ssize_t size;
if ( (size = recvAll(clifd, (void**)&buf)) > 0)
sendAll(clifd, buf, size);
freePtr(buf);
Close(clifd);
exit(0);
}
int main()
{
Tcp_listen("INADDR_ANY", 9999, 5, &listenfd);
while (1)
{
Accept(listenfd, NULL, NULL, &clifd);
printf("有新連接\n");
if (fork() == 0)
{
Close(listenfd);
server_deal();
}
Close(clifd);
}
return 0;
}
與之前的每一個客戶請求臨時fork一個進程處理不同,在啟動的時候就fork出一些子進程,優點是節省了臨時fork的開銷,缺點是父進程在啟動階段要先知道預先派生的子進程數,如果連接較多而無可用子進程,那麼客戶請求超過了連接排隊數就可能會被忽略
#include "net.h"
const int PROCESS_COUNT = 5;
int listenfd;
void server_deal()
{
int clifd;
char *buf = NULL;
ssize_t size;
Accept(listenfd, NULL, NULL, &clifd);
printf("子進程%ld有新連接\n", (long)getpid());
if ( (size = recvAll(clifd, (void**)&buf)) > 0)
sendAll(clifd, buf, size);
freePtr(buf);
Close(clifd);
}
int main()
{
Tcp_listen("INADDR_ANY", 9999, 5, &listenfd);
for (int i=0; i<PROCESS_COUNT; ++i)
{
if (fork() == 0)
{
while (1)
{
server_deal();
}
}
}
while (1);
return 0;
}
因為某些內核實現中不允許多個進程引用對同一個監聽套接字調用accept,所以對accept加鎖成為原子操作為對上一種模型的改進
#include "net.h"
const int PROCESS_COUNT = 5;
int listenfd;
int lock_fd;
struct flock lock_it, unlock_it;
void my_lock_init(const char *pathname)
{
char lock_file[1024];
strncpy(lock_file, pathname, sizeof(lock_file));
lock_fd = Mkstemp(lock_file);
Unlink(lock_file);
lock_it.l_type = F_WRLCK;
lock_it.l_whence = SEEK_SET;
lock_it.l_start = 0;
lock_it.l_len = 0;
unlock_it.l_type = F_UNLCK;
unlock_it.l_whence = SEEK_SET;
unlock_it.l_start = 0;
unlock_it.l_len = 0;
}
void my_lock_wait()
{
while (fcntl(lock_fd, F_SETLKW, &lock_it) < 0)
{
if (errno == EINTR)
continue;
else
printErrExit("my_lock_wait error");
}
}
void my_lock_release()
{
while (fcntl(lock_fd, F_SETLKW, &unlock_it) < 0)
{
if (errno == EINTR)
continue;
else
printErrExit("my_lock_release error");
}
}
void server_deal()
{
int clifd;
char *buf = NULL;
ssize_t size;
my_lock_wait();
Accept(listenfd, NULL, NULL, &clifd);
printf("子進程%ld有新連接\n", (long)getpid());
my_lock_release();
if ( (size = recvAll(clifd, (void**)&buf)) > 0)
sendAll(clifd, buf, size);
freePtr(buf);
Close(clifd);
}
int main()
{
Tcp_listen("INADDR_ANY", 9999, 5, &listenfd);
my_lock_init("/tmp/lock.XXXXXX");
for (int i=0; i<PROCESS_COUNT; ++i)
{
if (fork() == 0)
{
while (1)
{
server_deal();
}
}
}
while (1);
return 0;
}
與上一模型類似,采用多進程間共享線程鎖進行的方式對預先派生進程服務器的改進
#include "net.h"
const int PROCESS_COUNT = 5;
int listenfd;
pthread_mutex_t *mptr;
void my_lock_init()
{
int fd;
pthread_mutexattr_t mattr;
fd = Open("/dev/zero", O_RDWR, 0);
mptr = (pthread_mutex_t*)Mmap(0, sizeof(pthread_mutex_t),
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
Close(fd);
pthread_mutexattr_init(&mattr);
pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(mptr, &mattr);
}
void my_lock_wait()
{
pthread_mutex_lock(mptr);
}
void my_lock_release()
{
pthread_mutex_unlock(mptr);
}
void server_deal()
{
int clifd;
char *buf = NULL;
ssize_t size;
my_lock_wait();
Accept(listenfd, NULL, NULL, &clifd);
printf("子進程%ld有新連接\n", (long)getpid());
my_lock_release();
if ( (size = recvAll(clifd, (void**)&buf)) > 0)
sendAll(clifd, buf, size);
freePtr(buf);
Close(clifd);
}
int main()
{
Tcp_listen("INADDR_ANY", 9999, 5, &listenfd);
my_lock_init();
for (int i=0; i<PROCESS_COUNT; ++i)
{
if (fork() == 0)
{
while (1)
{
server_deal();
}
}
}
while (1);
return 0;
}
主進程中accept後將已連接的套接字通過進程間通信的方式傳遞給預先派生的空閒進程,預先派生的進程處理完成後向主進程發送消息,主進程負責維護所有預先派生進程的狀態以及可用數目
#include "net.h"
#define THREAD_COUNT 5
typedef struct
{
pid_t pid;
int pipefd;
int status;
long count;
} Child;
int listenfd;
int navail;
Child carr[THREAD_COUNT];
int tmp_conn_count;
void sig_int(int sig)
{
int i;
int sum = 0;
sum += tmp_conn_count;
printf("tmp_conn_count:%d\n", tmp_conn_count);
for (i=0; i<THREAD_COUNT; i++)
{
sum += carr[i].count;
printf("carr[%d]'s conn is %ld\n", i, carr[i].count);
}
printf("sum is %d\n", sum);
exit(-1);
}
void server_deal(int i)
{
int ret;
int clifd;
char *buf = NULL;
char c = 'w';
int size;
struct strrecvfd recv_stru;
while (1)
{
recvfd(STDERR_FILENO, &clifd);
if ( (size = recvAll(clifd, (void**)&buf)) > 0)
sendAll(clifd, buf, size);
Close(clifd);
freePtr(buf);
buf = NULL;
write(STDERR_FILENO, &c, 1);
}
}
void child_make(int i)
{
int sockfd[2];
pid_t pid;
Socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
//Socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd);
if ( (pid = fork()) > 0)
{
Close(sockfd[1]);
carr[i].pipefd = sockfd[0];
carr[i].status = 0;
carr[i].count = 0;
carr[i].pid = pid;
}
else
{
if (dup2(sockfd[1], STDERR_FILENO) < 0)
printErrExit("dup2 error");
Close(sockfd[1]);
Close(sockfd[0]);
Close(listenfd);
carr[i].pipefd = sockfd[1];
server_deal(i);
}
}
void temp_child(int clifd)
{
char *buf = NULL;
int size;
if (fork() > 0)
{
Close(clifd);
++tmp_conn_count;
}
else
{
if ( (size = recvAll(clifd, (void**)&buf)) > 0)
sendAll(clifd, buf, size);
Close(clifd);
freePtr(buf);
exit(0);
}
}
int main()
{
int maxfd;
fd_set rset, master;
int nsel;
int clifd;
int i;
printf("pid:%d\n", getpid());
Tcp_listen("INADDR_ANY", 9999, 5, &listenfd);
FD_ZERO(&rset);
FD_SET(listenfd, &master);
maxfd = listenfd;
tmp_conn_count = 0;
for (i=0; i<THREAD_COUNT; i++)
{
child_make(i);
FD_SET(carr[i].pipefd, &master);
if (maxfd < carr[i].pipefd)
maxfd = carr[i].pipefd;
}
navail = THREAD_COUNT;
Signal(SIGINT, sig_int);
while (1)
{
printf("navail: %d\n", navail);
rset = master;
nsel = Select(maxfd+1, &rset, NULL, NULL, NULL);
if (FD_ISSET(listenfd, &rset))
{
Accept(listenfd, NULL, NULL, &clifd);
if (navail > 0)
{
for (i=0; i<THREAD_COUNT; i++)
if (carr[i].status == 0)
break;
//向子進程傳遞連接上來的套接字描述符
sendfd(carr[i].pipefd, clifd);
carr[i].status = 1;
--navail;
}
else
{
temp_child(clifd);
}
if (--nsel == 0)
continue;
}
for(int i=0; i<THREAD_COUNT; i++)
{
if (FD_ISSET(carr[i].pipefd, &rset))
{
char c;
read(carr[i].pipefd, &c, sizeof(c));
carr[i].count++;
carr[i].status = 0;
++navail;
if (--nsel == 0)
break;
}
}
}
return 0;
}
對於每一個客戶請求創建一個線程來處理,與多進程並發服務器相比,創建線程比創建進程的開銷更低
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-10/107871p2.htm