几种服务器模型
几种服务器模型
TCP测试用客户程序
每次运行客户程序,在命令行参数指定服务器的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;
- }
1.迭代服务器
在处理完成某个客户的请求之后才转向下一个客户,比较少见,虽然总的服务时间稍慢,但需要进程控制
- #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;
- }
2.TCP多进程并发服务器
每个客户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;
- }
3.TCP预先派生子进程服务器
与之前的每一个客户请求临时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;
- }
4.TCP预先派生子进程服务器,accept使用文件上锁保护
因为某些内核实现中不允许多个进程引用对同一个监听套接字调用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;
- }
5.TCP预先派生子进程服务器,accept使用线程上锁保护
与上一模型类似,采用多进程间共享线程锁进行的方式对预先派生进程服务器的改进
- #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;
- }
6.TCP预先派生子进程服务器,主进程传递描述符
主进程中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;
- }
客户程序创建30个子进程连接时,向服务器进程发送SIGINT信号查看各个进程服务数目的分布
7.TCP多线程并发服务器
对于每一个客户请求创建一个线程来处理,与多进程并发服务器相比,创建线程比创建进程的开销更低
- #include "net.h"
- int listenfd;
- void* server_deal(void *arg)
- {
- int clifd = *((int*)arg);
- printf("clifd: %d\n", clifd);
- char *buf = NULL;
- ssize_t size;
- if ( (size = recvAll(clifd, (void**)&buf)) > 0)
- sendAll(clifd, buf, size);
- freePtr(buf);
- freePtr(arg);
- Close(clifd);
- }
- int main()
- {
- Tcp_listen("INADDR_ANY", 9999, 5, &listenfd);
- while (1)
- {
- int clifd;
- pthread_t tid;
- int *arg = NULL;
- Accept(listenfd, NULL, NULL, &clifd);
- printf("有新连接\n");
- arg = (int*)Malloc(sizeof(int));
- *arg = clifd;
- Pthread_create(&tid, NULL, server_deal, arg);
- }
- return 0;
- }
更多详情见请继续阅读下一页的精彩内容:
|
评论暂时关闭