本节是关于linux网络编程各种模型的总的概述。
当开发一个linux服务器程序时,我们有如下若干方法可供选择:
1.单进程迭代服务器:它的缺点是在当前客户被处理完之前,新到达的客户无法被服务
2.单进程select服务器:进程内使用select同时服务多个客户
3.多进程并发服务器:它为每个客户fork一个子进程来提供服务,这是传统服务器通常做法
4.多线程并发服务器:它为每个客户创建一个线程来提供服务
5.预先派生子进程:服务器启动后就派生一组子进程,形成一个子进程池,每来一个新客户,
就从进程池内取一个可用进程来为它服务。
6.预先创建线程:服务器启动后就创建一组线程,形成一个线程池。每个客户由池中的一个线程为其提供服务。
当开发一个linux客户程序时,有如下的设计方法可供选择
1.停等模式客户程序:缺点,当它等待用户输入而阻塞时,无法监视网络事件,二是停等模式批处理效率极低
2.改用select同时监视用户输入与网络事件,并用shutdown来正确的处理批量输入的问题
3.改用非阻塞I/O客户程序
4.创建子进程,父进程处理客户到服务器的数据,子进程处理服务器到客户的数据
5.创建线程,主线程处理客户到服务器的数据,子线程处理服务器到客户的数据
下面若干节需要用到一些公共的读写函数,如下:
下面readline.c 文件提供了readline以及Readline函数,我们会用它来读取socket中的一行数据
/* include readline */
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>
#define MAXLINE 4096
static int read_cnt;
static char *read_ptr;
static char read_buf[MAXLINE];
static ssize_t
my_read(int fd, char *ptr)
{
if (read_cnt <= 0) {
again:
if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
if (errno == EINTR)
goto again;
return(-1);
} else if (read_cnt == 0)
return(0);
read_ptr = read_buf;
}
read_cnt--;
*ptr = *read_ptr++;
return(1);
}
ssize_t
readline(int fd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = vptr;
for (n = 1; n < maxlen; n++) {
if ( (rc = my_read(fd, &c)) == 1) {
*ptr++ = c;
if (c == '\n')
break; /* newline is stored, like fgets() */
} else if (rc == 0) {
*ptr = 0;
return(n - 1); /* EOF, n - 1 bytes were read */
} else
return(-1); /* error, errno set by read() */
}
*ptr = 0; /* null terminate like fgets() */
return(n);
}
ssize_t
readlinebuf(void **vptrptr)
{
if (read_cnt)
*vptrptr = read_ptr;
return(read_cnt);
}
/* end readline */
ssize_t
Readline(int fd, void *ptr, size_t maxlen)
{
ssize_t n;
if ( (n = readline(fd, ptr, maxlen)) < 0)
{
perror("readline error");
exit(1);
}
return(n);
}
下面的writen函数多次调用write实现了写n个字节到socket中
/*Write n bytes to fd*/
ssize_t writen(int fd, const void *vptr,size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while(nleft > 0)
{
if((nwritten = write(fd,ptr,nleft)) < 0)
{
if(errno == EINTR)
{
continue;
}else if(nleft == n)
{
return -1;
}else
{
break;
}
}else if(nwritten == 0)
{
break;
}else
{
nleft -= nwritten;
ptr += nwritten;
}
}
return n-nleft;
}
下面是readn函数
ssize_t /* Read "n" bytes from a descriptor. */
readn(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ( (nread = read(fd, ptr, nleft)) < 0) {
if (errno == EINTR)
nread = 0; /* and call read() again */
else
return(-1);
} else if (nread == 0)
break; /* EOF */
nleft -= nread;
ptr += nread;
}
return(n - nleft); /* return >= 0 */
}