几种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信号查看各个进程服务数目的分布
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
几种server模型的更多相关文章
- 聊聊 Linux 中的五种 IO 模型
本文转载自: http://mp.weixin.qq.com/s?__biz=MzAxODI5ODMwOA==&mid=2666538919&idx=1&sn=6013c451 ...
- ESPlatform 支持的三种群集模型 —— ESFramework通信框架 4.0 进阶(09)
对于最多几千人同时在线的通信应用,通常使用单台服务器就可以支撑.但是,当同时在线的用户数达到几万.几十万.甚至百万的时候,我们就需要很多的服务器来分担负载.但是,依据什么规则和结构来组织这些服务器,并 ...
- Reactor三种线程模型与Netty线程模型
文中所讲基本都是以非阻塞IO.异步IO为基础.对于阻塞式IO,下面的编程模型几乎都不适用 Reactor三种线程模型 单线程模型 单个线程以非阻塞IO或事件IO处理所有IO事件,包括连接.读.写.异常 ...
- 2018.5.4 Unix的五种IO模型
阻塞非阻塞和异步同步 同步和异步关注的是消息通信机制,关注两个对象之间的调用关系. 阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态,关注单一程序. Unix的五种IO模型 以下基于Li ...
- HTTPD三种工作模型
HTTPD三种工作模型 MPM是apache的多道处理模块,用于定义apache对客户端请求的处理方式.在linux中apache常用的三种MPM模型分别是prefork.worker和event. ...
- 【经典】5种IO模型 | IO多路复用
上篇回顾:静态服务器+压测 3.2.概念篇 1.同步与异步 同步是指一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成. 异步是指不需要等待被依赖的任务完成,只是通 ...
- 7.3 5种IO模型与IO复用
5种IO模型分别如下: 1.阻塞IO模型 当上层应用app1调用recv系统调用时,如果对等方没有发送数据(缓冲区没有数据),上层app1将阻塞(默认行为,被linux内核阻塞). 当对等方发送了数据 ...
- Client/Server 模型 与socket
Client/Server 模型 Sockets 是以 Client 和 Server 交互通信方式来使用的.典型的系统配置是把 Server 放在一台机器中,而把 Client 放在另一台机器中, ...
- Spring - 几种RPC模型的使用与比较
Spring中,用JMS搞RPC时会用到: org.springframework.jms.remoting.JmsInvokerServiceExporter org.springframework ...
随机推荐
- 阿里云容器服务--配置自定义路由服务应对DDOS攻击
阿里云容器服务--配置自定义路由服务应对DDOS攻击 摘要: 容器服务中,除了slb之外,自定义路由服务(基于HAProxy)也可以作为DDOS攻击的一道防线,本文阐述了几种方法来应对普通规模的DDO ...
- Linux系统性能监控
系统的性能指标主要包括CPU.内存.磁盘I/O.网络几个方面. 1. CPU性能 (1)利用vmstat命令监控系统CPU 该命令可以显示关于系统各种资源之间相关性能的简要信息,这里我们主要用它来看C ...
- oracle 统计语句 与常见函数的归纳(未完待续)
一.统计语句 1. count count(*)与count(0)语句的区别: count(*)统计所有数量 count(0)统计第一列不为空的 2. 两个统计量的减法 select (select ...
- window下版本控制工具Git 客户端安装
安装使用 1.下载msysgit http://code.google.com/p/msysgit/ 2.下载tortoisegit客户端安装 http://code.google.com/p/tor ...
- python easy_install centos 下安装过程和原理解析
一.easy_install 安装过程 其安装过程有很多种,我也找了很多的例子,但是结果都不太好,以下方法的结果是不错的. easy_install与yum类似,使用easy_install,可以轻松 ...
- 解决A program file was not specified in the launch configuration.问题
问题描述: 在eclipse 中开发c++或c是比较麻烦的事情,刚刚配置好mingw32和cdt和环境变量后,新建一个hello world的c++项目还是会出现问题.主要是在编译的时候会提示 ...
- 让UITableViewCell的分隔线宽度等于屏幕的宽度
3种方法: 1.自定义cell: 1).取消系统的分隔线 2).添加一个高度为1,宽度为屏幕宽度的UIView作为cell的子视图,通过设置frame或者约束使这个UIView始终在cell的底部 3 ...
- thymeleaf的url属性
一.使用表达式形式:@{...} 例如: <a th:href="@{http://localhost:8080/gtvg/order/details}">view&l ...
- codeforces 630J Divisibility
J. Divisibility time limit per test 0.5 seconds memory limit per test 64 megabytes input standard in ...
- 精通Linux的“kill”命令
无论你使用哪种操作系统,你一定会遇到某个行为失常的应用,它把自己锁死并拒绝关闭.在Linux(还有Mac),你可以用一个"kill"命令强制终结它.在这个教程中,我们将展示给你多种 ...