几种服务器模型

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;
}

8.TCP预先创建线程服务器,每个线程各自accept

#include "net.h"

#define THREAD_COUNT 5
int listenfd;
pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;

void server_deal()
{
	int clifd;
	int len;
	char *buf = NULL;

	pthread_mutex_lock(&mylock);
	Accept(listenfd, NULL, NULL, &clifd);
	pthread_mutex_unlock(&mylock);
	if ( (len = recvAll(clifd, (void**)&buf)) > 0)
		sendAll(clifd, buf, len);
	Close(clifd);
}

void* handler(void *arg)
{
	while (1)
	{
		server_deal();
	}
}

int main()
{
	pthread_t tid;

	Tcp_listen("INADDR_ANY", 9999, 5, &listenfd);

	for (int i=0; i<THREAD_COUNT; ++i)
		Pthread_create(&tid, NULL, handler, NULL);

	while (1);
	return 0;
}

net.h头文件

#ifndef MY_NET_H
#define MY_NET_H  

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <stropts.h>

#define MAXLINE 4096
#define SA struct sockaddr
#define LISTENEQ 10

//清除数据
//ptr 指针
void freePtr(void *ptr)
{
	if (ptr != NULL)
		free(ptr);
}

//打印错误信息并终止进程
//errStr 错误字符串
void printErrExit(const char* errStr)
{
	if (errStr != NULL)
		perror(errStr);
	printf("进程pid:%d\n", getpid());
	exit(-1);
}

int Open(const char *pathname, int flags, mode_t mode)
{
	int fd;

	while ( (fd = open(pathname, flags, mode)) < 0)
	{
		if (errno == EINTR)
			continue;
		printErrExit("Open error");
	}

	return fd;
}

int Socketpair(int domain, int type, int protocol, int sv[2])
{
	if (socketpair(domain, type, protocol, sv) < 0)
		printErrExit("Socketpair error");
}

void Signal(int signum, sighandler_t handler)
{
	if (signal(signum, handler) == SIG_ERR)
		printErrExit("Signal error");
}

int Select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout)
{
	int ret;

	while ( (ret = select(nfds, readfds, writefds, exceptfds, timeout)) < 0)
	{
		if (errno == EINTR)
			continue;
		printErrExit("Select error");
	}

	return ret;
}

ssize_t Read(int fd, void *buf, size_t count)
{
	int ret;

	while ( (ret = read(fd, buf, count)) < 0)
	{
		if (errno == EINTR)
			continue;
		printErrExit("Read error");
	}

	return ret;
}

ssize_t Write(int fd, const void *buf, size_t count)
{
	int ret;

	while ( (ret = write(fd, buf, count)) < 0)
	{
		if (errno == EINTR)
			continue;
		printErrExit("Write error");
	}

	return ret;
}

void Dup2(int oldfd, int newfd)
{
	while (dup2(oldfd, newfd) < 0)
	{
		if (errno == EINTR)
			continue;
		printErrExit("Dup2 error");
	}
}

void *Mmap(void *addr, size_t length, int prot,
		   int flags, int fd, off_t offset)
{
	void *ptr;

	if ( (ptr = mmap(addr, length, prot, flags,
		  fd, offset)) == MAP_FAILED)
	    printErrExit("Mmap error");

	return ptr;
}

int Mkstemp(char *path)
{
	int lock_fd;

	if ( (lock_fd = mkstemp(path)) < 0)
		printErrExit("Mkstemp error");

	return lock_fd;
}

void Unlink(const char *pathname)
{
	if (unlink(pathname) < 0)
		printErrExit("Unlink error");
}

void* Malloc(size_t size)
{
	void *ret = NULL;

	if ( (ret = malloc(size)) == NULL)
		printErrExit("Malloc error");

	return ret;
}  

int sendfd(int fd, int fd_to_send)
{
	struct iovec iov;
	struct msghdr msg;
	struct cmsghdr *cmsg;
	char   buf = ' ';

	iov.iov_base = &buf;
	iov.iov_len = 1;

	cmsg = (struct cmsghdr*)malloc(CMSG_LEN(sizeof(int)));
	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;
	*(int*)CMSG_DATA(cmsg) = fd_to_send; 

	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = cmsg;
	msg.msg_controllen = CMSG_LEN(sizeof(int));
	msg.msg_flags = 0;

	while (sendmsg(fd, &msg, 0) < 0)
	{
		if (errno == EINTR)
			continue;

		return -1;
	}

	return 0;
}

int recvfd(int fd, int *fd_to_recv)
{
	struct iovec iov;
	struct msghdr msg;
	struct cmsghdr *cmsg;
	char   buf;
	int ret;

	iov.iov_base = &buf;
	iov.iov_len = 1;

	cmsg = (struct cmsghdr*)malloc(CMSG_LEN(sizeof(int)));
	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;

	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = cmsg;
	msg.msg_controllen = CMSG_LEN(sizeof(int));
	msg.msg_flags = 0;

	while ( (ret = recvmsg(fd, &msg, 0)) < 0)
	{
		if (errno == EINTR)
			continue;

		return -1;
	}

	if (ret == 0)
		return 0;

	*fd_to_recv = *(int*)CMSG_DATA((struct cmsghdr*)msg.msg_control);
	return *fd_to_recv;
}

//执行close
//fd 描述符
void Close(int fd)
{
	while (close(fd) < 0)
	{
		if (errno == EINTR)
			continue;
		printErrExit("Close error");
	}
}

//执行accept
//skfd 描述符
//addr struct sockaddr结构
//addrlen addr的大小
//ret 返回值
void Accept(int skfd, SA *addr, socklen_t *addrlen, int *ret)
{
	int clifd;

	if (ret == NULL)
		printErrExit("Accept error");

	while ((clifd = accept(skfd, addr, addrlen)) < 0)
	{
		if (errno == EINTR)
			continue;
		printErrExit("Accept error");
	}

	*ret = clifd;
}

//初始化struct sockaddr_in结构
//stru 指向要初始化的struct sockaddr_in结构的指针
//protoc 地址族
//addr   ip地址,可以是INADDR_ANY
//port   端口
//返回值:成功返回0,出错返回-1
//注意:不对protoc(地址族)参数进行检查
int init_sockaddr(struct sockaddr_in *stru, int protoc, const char *addr, unsigned int port)
{
	if (stru == NULL || addr == NULL)
		return -1;	

	if (port > 65535)
		return -1;

	memset(stru, 0, sizeof(struct sockaddr_in));

	if (strcmp(addr, "INADDR_ANY") == 0)
		(stru->sin_addr).s_addr = htonl(INADDR_ANY);
	else
	{
		if (inet_addr(addr) == INADDR_NONE)
			return -1;
		(stru->sin_addr).s_addr = inet_addr(addr);

	}

	stru->sin_family = protoc;
	stru->sin_port = htons(port);
	return 0;
}

void Init_sockaddr(struct sockaddr_in *stru, int protoc, const char *addr, unsigned int port)
{
	if (stru == NULL || addr == NULL)
		printErrExit("Init_sockaddr error");

	if (port > 65535)
		printErrExit("Init_sockaddr error");

	memset(stru, 0, sizeof(struct sockaddr_in));

	if (strcmp(addr, "INADDR_ANY") == 0)
		(stru->sin_addr).s_addr = htonl(INADDR_ANY);
	else
	{
		if (inet_addr(addr) == INADDR_NONE)
			printErrExit("Init_sockaddr error");

		(stru->sin_addr).s_addr = inet_addr(addr);
	}

	stru->sin_family = protoc;
	stru->sin_port = htons(port);
}

//建立一个TCP套接字并连接到指定ip地址和指定端口(阻塞版本,connect会一直阻塞,直到到达默认超时时间)
//addr ip地址
//port 端口
//返回值:连接成功返回描述符,出错返回-1
int tcp_connect(const char *addr, unsigned int port)
{
	int skfd;
	struct sockaddr_in saddr;

	if( (init_sockaddr(&saddr, AF_INET, addr, port)) < 0)
		return -1;

	if ( (skfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return -1;

	while (connect(skfd, (SA*)&saddr, sizeof(saddr)) < 0)
	{
		if (errno == EINTR)
			continue;
		else
		{
			close(skfd);
			return -1;
		}
	}

	return skfd;
}

void Tcp_connect(const char *addr, unsigned int port, int *ret)
{
	int skfd;
	struct sockaddr_in saddr;

	if (ret == NULL)
		printErrExit("Tcp_connect error");

	if (init_sockaddr(&saddr, AF_INET, addr, port) < 0)
		printErrExit("Tcp_connect error");

	if ( (skfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		printErrExit("Tcp_connect error");

	while (connect(skfd, (SA*)&saddr, sizeof(saddr)) < 0)
	{
		if (errno == EINTR)
			continue;
		else
		{
			close(skfd);
			printErrExit("Tcp_connect error");
		}
	}

	*ret = skfd;
}

//建立一个套接字,并且绑定,监听
//addr 要绑定的ip地址 INADDR_ANY或ipv4地址
//port 要监听的端口
//backlog listen函数的监听排队数
//返回值:成功返回套接字描述符,出错返回-1
int tcp_listen(const char *addr, unsigned int port, int backlog)
{
	int skfd;
	struct sockaddr_in saddr;

	if (init_sockaddr(&saddr, AF_INET, addr, port) < 0)
		return -1;

	if ( (skfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return -1;

	if (bind(skfd, (SA*)&saddr, sizeof(saddr)) < 0)
	{
		close(skfd);
		return -1;
	}

	if (listen(skfd, backlog) < 0)
	{
		close(skfd);
		return -1;
	}

	return skfd;
}

void Tcp_listen(const char *addr, unsigned int port, int backlog, int *ret)
{
	int skfd;
	struct sockaddr_in saddr;

	if (ret == NULL)
		printErrExit("Tcp_listen error");

	if (init_sockaddr(&saddr, AF_INET, addr, port) < 0)
		printErrExit("Tcp_listen error");

	if ( (skfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		printErrExit("Tcp_listen error");

	if (bind(skfd, (SA*)&saddr, sizeof(saddr)) < 0)
	{
		close(skfd);
		printErrExit("Tcp_listen error");
	}

	if (listen(skfd, backlog) < 0)
	{
		close(skfd);
		printErrExit("Tcp_listen error");
	}

	*ret = skfd;
}

//发送n个字节
//fd 描述符
//vptr指向要发送的数据
//n 要发送的字节数
//出错返回-1,否则返回发送的字节数
ssize_t writen(int fd, const void *vptr, size_t n)
{
	size_t nleft = n;
	ssize_t nwritten;
	const char *ptr = (const char*)vptr;

	while (nleft > 0)
	{
		if ( (nwritten = write(fd, ptr, nleft)) <= 0 )
		{
			if (nwritten < 0 && errno == EINTR)
				nwritten = 0;
			else
				return (-1);
		}

		nleft -= nwritten;
		ptr += nwritten;
	}

	return (n);
}

//读取指定的字节数
//fd 描述符
//ptr 指向存放数据的指针
//n 要接收的字节数
//对端关闭返回0,出错返回-1,否则返回接收的字节数
ssize_t readn(int fd, void *vptr, size_t n)
{
	char c;
	int ret;
	char *ptr = (char*)vptr;
	size_t i;
	ssize_t size = 0;

	if (vptr == NULL)
		return -1;

	for(i=0; i<n; i++)
	{
		ret = read(fd, &c, 1);

		if (ret == 0)
			return 0;
		else if (ret < 0 && errno == EINTR)
			i--;
		else if (ret == 1)
			ptr[i] = c;
		else
			return -1;
	}

	return n;
}

//发送指定的字节数的数据
//skfd 套接字描述符
//sendbuf 要发送的字符串
//size 要发送的字节数
//出错返回-1,否则返回发送的字节数
ssize_t sendAll(int skfd, const void* sendBuf, size_t size)
{
	const char *ptr = (const char*)sendBuf;

	if (sendBuf == NULL)
		return -1;

	if (writen(skfd, &size, sizeof(size)) == sizeof(size))
	{
		if (writen(skfd, ptr, size) == size)
			return size;
		else
			return -1;
	}

	return -1;
}

//接收指定字节数的数据
//skfd 描述符
//recvbuf 存放接收数据
//size 要接收的数据大小
//返回接收的字节数
//出错返回-1, 对端关闭返回0, 否则返回接收的字节数
ssize_t recvAll(int skfd, void **recvPtr)
{
	size_t len;
	ssize_t ret; 

	ret = readn(skfd, &len, sizeof(len));
	if (ret == 0)
		return 0;
	if (ret < 0)
		return -1;

	freePtr(*recvPtr);
	*recvPtr = Malloc(len + 1);
	ret = readn(skfd, *recvPtr, len);
	if (ret == 0)
	{
		freePtr(*recvPtr);
		return 0;
	}
	if (ret < 0)
	{
		freePtr(*recvPtr);
		return -1;
	}

	return ret;
}

void Pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                    void *(*start_routine) (void *), void *arg)
{
	if (pthread_create(thread, attr, start_routine, arg) != 0)
		printErrExit("Pthread_create error");
}

#endif 
时间: 2024-10-16 00:06:08

几种服务器模型的相关文章

Windows Socket五种I/O模型——代码全攻略(转)

Winsock 的I/O操作: 1. 两种I/O模式 阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序.套接字 默认为阻塞模式.可以通过多线程技术进行处理. 非阻塞模式:执行I/O操作时,Winsock函数会返回并交出控制权.这种模式使用 起来比较复杂,因为函数在没有运行完成就进行返回,会不断地返回 WSAEWOULDBLOCK错误.但功能强大.为了解决这个问题,提出了进行I/O操作的一些I/O模型,下面介绍最常见的三种: Windows Socket五种I/O模型——代码全攻

【转】常用服务器模型总结

UNPv3上一共总结了9种服务器模型分别是: 1.迭代服务器 2.并发服务器,为每个客户fork一个进程 3.预先派生子进程,每个子进程都调用accept,accept无上锁保护 4.预先派生子进程,以文件锁的方式保护accept 5.预先派生子进程,以线程互斥锁上锁的方式保护accept 6.预先派生子进程,由父进程向子进程传递套接口描述字 7.并发服务器,为每个客户请求创建一个线程 8.预先创建线程,以互斥锁上锁方式保护accept 9.预先创建线程,由主线程调用accept,并把每个客户连

(转载)Windows Socket五种I/O模型——代码全攻略

如果你想在Windows平台上构建服务器应用,那么I/O模型是你必须考虑的.Windows操作系统提供了选择(Select).异步选择(WSAAsyncSelect).事件选择(WSAEventSelect).重叠I/O(Overlapped I/O)和完成端口(Completion Port)共五种I/O模型.每一种模型均适用于一种特定的应用场景.程序员应该对自己的应用需求非常明确,而且综合考虑到程序的扩展性和可移植性等因素,作出自己的选择. 我会以一个回应反射式服务器(与<Windows网络

TCP并发服务器,每个客户一个子进程

在阅读完<unix 网络编程:卷一>之后,感觉作者真是unix下编程的大师级的人物.而对于我个人而言,每次阅读完一本技术书籍之后,一定还是得自己重新再写一遍程序(换点内容),复习书本中的内容(大致结构,或者说思想,相同),否则,你很难做到真的理解并掌握的地步. Okay,今天我带来的是服务器模型中的第一种,也是最基本最常用的一种模型–TCP并发服务器,每个客户一个子进程. 先简单介绍一下:TCP并发服务器,每个客户一个子进程,也就是说并发服务器调用fork派生一个子进程来处理每个子进程,使得服

协作半驻留式服务器程序开发框架 --- 基于 Postfix 服务器框架改造

一.概述 现在大家在和Java, PHP, .net写应用程序时,都会用到一些成熟的服务框架,所以开发效率是比较高的.而在用C/C++写服务器程序时,用的就五花八门了,有些人用ACE, 有些人用ICE(号称比ACE强许多),等等,这类服务器框架及库比较丰富,但入门门槛比较高,所以更多的人是自己直接写服务器程序,初始写时觉得比较简 单,可时间久了,便会觉得难以扩展,性能低,容易出错.其实,Postfix 作者为我们提供了一个高效.稳定.安全的服务器框架模型,虽然Postfix主要用作邮件系统的 m

Socket模型详解(转)

Socket模型详解(转) Socket模型详解 两种I/O模式 一.选择模型 二.异步选择 三.事件选择 四.重叠I/O模型 五.完成端口模型 五种I/O模型的比较 两种I/O模式 1. 两种I/O模式 阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序.套接字默认为阻塞模式.可以通过多线程技术进行处理. 非阻塞模式:执行I/O操作时,Winsock函数会返回并交出控制权.这种模式使用起来比较复杂,因为函数在没有运行完成就进行返回,会不断地返回 WSAEWOULDBLOCK错误

java的服务是每收到一个请求就新开一个线程来处理吗?tomcat呢?

首先,服务器的实现不止有这两种方式. 先谈谈题主说的这两种服务器模型: 1.收到一个请求就处理,这个时候就不能处理新的请求,这种为阻塞 这个是单线程模型,无法并发,一个请求没处理完服务器就会阻塞,不会处理下一个请求.一般的服务器不会使用这种方式实现. 2.收到一个请求就新开一个线程去处理任务,主线程返回,继续处理下一个任务,这种为非阻塞 首先纠正一个错误,这并不是非阻塞,它也是阻塞的.相对第一个模型来说,它解决了主线程阻塞的问题,有了一定程度的并发量,但是在每个新开的线程中还是阻塞的.如果100

涵魏唧傀rsvm

http://weibo.com/p2018.01.05P/1001604191077887263091柿重裳览殉KWU吵窃乔亮种 http://weibo.com/p2018.01.05P/1001604191076025009713趾拔苟热继DNL谕鬃碌刎佑 http://weibo.com/p2018.01.05P/1001604191083918703451懦乙缮创涤PLV适乔侣乔膊 http://weibo.com/p2018.01.05P/1001604191076968705644

寥瞄郭泵捺酵掷gbn

http://weibo.com/p2018.01.05P/1001604190868214001037驯儇掖换本QBA鬃矣刻妹揪 http://weibo.com/p2018.01.05P/1001604190868197253494放刭媳哪妹LVG购舅怂佑汉 http://weibo.com/p2018.01.05P/1001604190868000120066嚷览昂坎谮GCE适排亮乙偬 http://weibo.com/p2018.01.05P/1001604190882122332203