几种server模型

几种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 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/115437.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 【c#】把Dataset类型转为List<T>

    【c#】把Dataset类型转为List<T>【c#】把Dataset类型转为List<T>

  • Ace在线代码编辑器使用「建议收藏」

    Ace在线代码编辑器使用「建议收藏」这边文章https://blog.csdn.net/liuxiao723846/article/details/106732401介绍了Ace的使用,本文围绕Ace的api深入介绍其具体功能。官网api使用文档:https://ace.c9.io/#nav=howto1、基本配置:ace有许多的配置项可供选择,通过这些配置项可以打造自己的个性编辑器你可以通过setTheme来设置主题,需要注意的是主题文件要存在,并且需要与ace.js同级,命名规则为theme-主题名.jsedito.

  • task scheduler什么意思_女贞子的功效与作用

    task scheduler什么意思_女贞子的功效与作用前言本文隶属于专栏《1000个问题搞定大数据技术体系》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!本专栏目录结构和参考文献请见1000个问题搞定大数据技术体系正文TaskScheduler的核心任务是提交TaskSet到集群运算并汇报结果。(1)为TaskSet创建和维护一个TaskSetManager,并追踪任务的本地性以及错误信息。(2)遇到Straggle任务时,会放到其他节点进行重试。(3)向DAGScheduler

    2022年10月11日
  • python贪吃蛇游戏代码详解外加中文_Python贪吃蛇代码

    python贪吃蛇游戏代码详解外加中文_Python贪吃蛇代码感觉游戏审核新政实施后,国内手游市场略冷清,是不是各家的新游戏都在排队等审核。媒体们除了之前竞相追捧《PokemonGo》热闹了一把,似乎也听不到什么声音了。直到最近几天,突然听见好几人都提到同一个游戏,网上还有人表示朋友圈被它刷屏了。(不过现在微信已经悍然屏蔽了它的分享)这个游戏就是现在iOS免费榜排名第一的《贪吃蛇大作战》。一个简单到不行的游戏,也不知道怎么就火了。反正一款游戏火了,各路媒体…

  • 日语自动词和他动词是什么意思_日语自动词他动词总结

    日语自动词和他动词是什么意思_日语自动词他动词总结http://coffeejp.com/bbs/thread-182243-1-1.html自动词:强调自发的行为他动词:强调他人驱使的行为拿到两个动词后按照它们的构词形式可以进行区分,基本可以分为4种方法(1)两个动词都以「る」结尾时:看「る」前的那个假名(汉字除外),如果是「あ」段假名,一般是自动词;如果是「え」段假名,一般是他动词(2)两个动词一个以「る」结尾,另一个不是以「る」结尾…

    2022年10月29日
  • android.app.Fragment$InstantiationException的原因分析

    android.app.Fragment$InstantiationException的原因分析每个Fragment必须要有一个无参构造方法,这样该Fragment在Activity恢复状态的时候才可以被实例化。强烈建议,Fragment的子类不要有其他含参构造方法,因为这些构造方法在Fragment重新实例化时不会被调用。取而代之的方式是,通过setArguments(Bundle)设置参数,然后通过getArguments获得参数。

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号