几种server模型

TCP測试用客户程序

每次执行客户程序,在命令行參数指定server的ip地址,port,发起连接的子进程数,和一个待发送的字符串数据,客户程序将模拟多个客户依据指定的子进程数创建子进程来并发的连接到server,并发送数据,server收到数据后都原样的回发给客户,是一点典型的回射server。

#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.迭代server

在处理完毕某个客户的请求之后才转向下一个客户。比較少见,尽管总的服务时间稍慢。但须要进程控制
#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多进程并发server

每一个客户fork出一个子进程并发的去处理请求,总server时间稍短。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预先派生子进程server

与之前的每个客户请求暂时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预先派生子进程server,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预先派生子进程server,accept使用线程上锁保护

与上一模型类似,採用多进程间共享线程锁进行的方式对预先派生进程server的改进
#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预先派生子进程server,主进程传递描写叙述符

主进程中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个子进程连接时。向server进程发送SIGINT信号查看各个进程服务数目的分布

几种server模型

几种server模型

7.TCP多线程并发server

对于每个客户请求创建一个线程来处理,与多进程并发server相比,创建线程比创建进程的开销更低
#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预先创建线程server,每一个线程各自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 

更多相关文章
  • Spring-几种RPC模型的使用与比较
    Spring中,用JMS搞RPC时会用到: org.springframework.jms.remoting.JmsInvokerServiceExporter org.springframework.jms.remoting.JmsInvokerProxyFactoryBean spring在实现 ...
  • MOS2010开发基础和集几种开发模型
    基础基本的部署结构图类似如下创建新的Web Application的原因:One key reason to create a new web application is to isolate content. Every time a new web application is created ...
  • Linux五种IO模型
    转载:http://blog.csdn.net/jay900323/article/details/18141217  Linux五种IO模型性能分析 目录(?)[-] 概念理解 Linux下的五种IO模型 阻塞IO模型 非阻塞IO模型  IO复用模型 信号驱动IO 异步IO模型 个IO模型的比较 ...
  • BonnMotion是一款基于java的移动场景产生和分析工具,常用来研究mobile ad hoc network的特征.其产生的移动场景可以导入到几款网络模拟器中进行模拟分析,例如:NS2,NS3,ONE,GloMoSim/QualNet,COOJA,MiXiM等等,它支持的也是比较常用的几种移 ...
  •       css的两种盒子模型:W3C标准盒子模型.IE盒子模型       两者的相同之处:都包含margin.border.padding.content       两者的不同之处:W3C标准盒子模型的content部分不包含其他部分:IE盒子模型的content部分包含border.pad ...
  • Apache三种MPM模型简介及应用
    MPM是apache的多道处理模块,用于定义apache对客户端请求的处理方式.在linux中apache常用的三种MPM模型分别是prefork.worker和event.prefork: 预先生成进程,一个请求用一个进程响应.Apache2.2版本默认使用prefork模型.优点:稳定可靠.执行 ...
  • Windows五种IO模型性能分析和Linux五种IO模型性能分析
    Windows五种IO模型性能分析和Linux五种IO模型性能分析 http://blog.csdn.net/jay900323/article/details/18141217 http://blog.csdn.net/jay900323/article/details/18140847 重叠I/ ...
  • LVS的三种转发模型及简单调优
    LVS的全称Linux vitual system,是由目前阿里巴巴的著名工程师章文嵩博士开发的一款开源软件.LVS工作在一台server上提供Directory(负载均衡器)的功能,本身并不提供服务,只是把特定的请求转发给对应的realserver(真正提供服务的主机),从而实现集群环境中的负载均 ...
一周排行
  • 在安装SQL server 2000的过程中,经常会出现如下提示:"以前的某个程序安装已在安装计算机上创建挂起的文件操作.运行安装程序之前必须重新启动计算机",而英文版安装时的提示为"A ...
  • 谈谈石豆官网改版的前端优化
    (注:本人现已不在石豆任职,且石豆官网又进行了一次新的改版,故本文章内容已不再匹配石豆现有 ...
  • VPN技术:PAT对VPN产生的影响
    适用场景:适用场景:   ,一个企业内网可能会有很成百上千台电脑需要上互联网,但一个企业可 ...
  •  在路由器转发任何数据包之前,路由表过程必须确定用于转发数据包的送出接口.我们将此过程称为路由解析.我们来看下具体的解析过程在R1的路由表中有下面这条静态路由:S 192.168.2.0/24 [1/0] via 1 ...
  • 第一部分:expect讲解expect可以让我们实现自动登录远程机器,并且可以实现自动远程执行命令.当然若是使用不带密码的密钥验证同样可以实现自动登录和自动远程执行命令.但当不能使用密钥验证的时候,我们就没有办法了. ...
  • 思科防火墙ASA配置案例
     思科防火墙ASA配置案例拓扑图要求:通过思科防火墙ASA使用内网用户可以访问外网与DMZ ...
  • 最近要给美国的SVN服务器做升级,由于这台服务器有一定年份了,在本公司也算是个三朝元老,保存了多少code啊!.为了保险起见,老板将迁移的工作郑重的交到我的手上.在欣欣然的同时也颤抖着.不过还好我已经对其轻车熟路,在 ...
  • 网络层协议学习过程
    网络层协议    IP ICMP  IGMP  ARP    IP协议是选最佳路径(RIP ...
  • Oracle字符集不一致
     Oracle 数据库字符集在项目中用到数据的移植,源数据库(db1)字符集 :ZHS16 ...
  • 刚刚在一个网站上看了一个好像是说仿2014手机QQ源码下载完整版(客户端+服务端),不过感觉还是要分享给大家吧.android源码手机QQ源码下载完整版(客户端+服务端)该文件包含手机QQ源码下载完整版(客户,服务端 ...