本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie
这是一个简单的回射服务器程序。它将客户发送的数据读入缓冲区并回射其中内容
/**
* TCP/IPv4 协议相关
* **/
#include "unp.h"
int
main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
//1.创建套接字,捆绑服务器的众所周知端口
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
//2.等待完成客户连接
for ( ; ; ) {
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
//3.并发服务器
if ( (childpid = Fork()) == 0) { /* 子进程 */
Close(listenfd); /* 关闭监听套接字*/
str_echo(connfd); /* 处理客户请求*/
exit(0);
}
Close(connfd); /* 父进程关闭连接套接字 */
}
}
#include "unp.h"
void
str_echo(int sockfd)
{
ssize_t n;
char buf[MAXLINE];
again:
// 读入缓冲区并回射其中内容
while ( (n = read(sockfd, buf, MAXLINE)) > 0)
Writen(sockfd, buf, n);
if (n < 0 && errno == EINTR)
goto again;
else if (n < 0)
err_sys("str_echo: read error");
}
/**
* TCP/IPv4 协议相关 收拾终止了的子进程
**/
#include "unp.h"
int
main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
void sig_chld(int);
//1.创建套接字,绑定众所周知的端口
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
//2.当fork 子进程时,必须捕获 SIGCHLD 信号, sig_chld 为信号处理函数
Signal(SIGCHLD, sig_chld); /* must call waitpid() */
//3.等待客户连接
for ( ; ; ) {
clilen = sizeof(cliaddr);
//4.当捕获信号时,必须处理被中断的系统调用
if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
if (errno == EINTR)
continue; /* back to for() */
else
err_sys("accept error");
}
//5.并发服务器
if ( (childpid = Fork()) == 0) { /* child process */
Close(listenfd); /* close listening socket */
str_echo(connfd); /* process the request */
exit(0);
}
Close(connfd); /* parent closes connected socket */
}
}
#include "unp.h"
void
sig_chld(int signo)
{
pid_t pid;
int stat;
while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
printf("child %d terminated\n", pid);
return;
}
/**
* TCP 使用 select
**/
#include "unp.h"
int
main(int argc, char **argv)
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
//1.创建套接字,绑定众所周知的端口
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
//2.初始化
maxfd = listenfd; /* initialize */
maxi = -1; /* index into client[] array */
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1; /* -1 indicates available entry */
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
/* end fig01 */
/* include fig02 */
for ( ; ; ) {
//3.阻塞于 select
//select 等待某个事件发生:或是新客户连接的建立,或是数据、FIN或 RST的到达
rset = allset; /* structure assignment */
nready = Select(maxfd+1, &rset, NULL, NULL, NULL);
//4.accept 新的连接
if (FD_ISSET(listenfd, &rset)) { /* new client connection */
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
#ifdef NOTDEF
printf("new client: %s, port %d\n",
Inet_ntop(AF_INET, &cliaddr.sin_addr, 4, NULL),
ntohs(cliaddr.sin_port));
#endif
//用 client 数组中第一个未用项记录这个已连接描述符
for (i = 0; i < FD_SETSIZE; i++)
if (client[i] < 0) {
client[i] = connfd; /* save descriptor */
break;
}
if (i == FD_SETSIZE)
err_quit("too many clients");
//在 allset 描述符中打开已连接套接字 connfd 的对应位
FD_SET(connfd, &allset); /* add new descriptor to set */
if (connfd > maxfd)
maxfd = connfd; /* for select */
if (i > maxi)
maxi = i; /* max index in client[] array */
//nready 是 select 函数的返回值,表示跨所有描述符集的已就绪的总位数
//就绪描述符数目减 1,若其值变为 0,就可以避免进入下一个 for 循环
if (--nready <= 0)
continue; /* no more readable descriptors */
}
//5.检查现有连接
//对于每个现在的客户连接,测试其描述符是否在 select 返回的描述符集中 --> ? select 有返回描述符集吗 ?
//如果是,就从客户读入一行文本并回射给它。
//如果关闭了连接,那么 read 将返回 0,要相应地更新数据结构
for (i = 0; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i]) < 0)
continue;
if (FD_ISSET(sockfd, &rset)) {
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
/*4connection closed by client */
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
} else
Writen(sockfd, buf, n);
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
/* end fig02 *//**
* TCP/IPv4 协议相关,使用poll,单个进程处理所有客户
* **/
/* include fig01 */
#include "unp.h"
#include <limits.h> /* for OPEN_MAX */
int
main(int argc, char **argv)
{
int i, maxi, listenfd, connfd, sockfd;
int nready;
ssize_t n;
char buf[MAXLINE];
socklen_t clilen;
//1.分配 pollfd 结构数组
struct pollfd client[OPEN_MAX];
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
//2.初始化
//把 client 数组的第一项用于监听套接字,并设置 events为 POLLRDNORM
//把其余各项的成员转为 -1
client[0].fd = listenfd;
client[0].events = POLLRDNORM;
for (i = 1; i < OPEN_MAX; i++)
client[i].fd = -1; /* -1 indicates available entry */
maxi = 0; /* max index into client[] array */
/* end fig01 */
/* include fig02 */
for ( ; ; ) {
//3.调用 poll,检查新的连接
nready = Poll(client, maxi+1, INFTIM);
//检查 client[0],即监听套接字上是否有新客户连接
if (client[0].revents & POLLRDNORM) { /* new client connection */
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
#ifdef NOTDEF
printf("new client: %s\n", Sock_ntop((SA *) &cliaddr, clilen));
#endif
//保存已连接套接字
for (i = 1; i < OPEN_MAX; i++)
if (client[i].fd < 0) {
client[i].fd = connfd; /* save descriptor */
break;
}
if (i == OPEN_MAX)
err_quit("too many clients");
client[i].events = POLLRDNORM;
if (i > maxi)
maxi = i; /* max index in client[] array */
if (--nready <= 0)
continue; /* no more readable descriptors */
}
//4.检查某个现有连接上的数据
// i 从 1,开始,因为 client[0]是监听套接字
for (i = 1; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i].fd) < 0)
continue;
if (client[i].revents & (POLLRDNORM | POLLERR)) {
//读取数据并回射
if ( (n = read(sockfd, buf, MAXLINE)) < 0) {
if (errno == ECONNRESET) {
/*4connection reset by client */
#ifdef NOTDEF
printf("client[%d] aborted connection\n", i);
#endif
Close(sockfd);
client[i].fd = -1;
} else
err_sys("read error");
//终止连接
} else if (n == 0) {
/*4connection closed by client */
#ifdef NOTDEF
printf("client[%d] closed connection\n", i);
#endif
Close(sockfd);
client[i].fd = -1;
} else
Writen(sockfd, buf, n);
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
/* end fig02 */
/**
* TCP 每个用户一个线程
* **/
#include "unpthread.h"
static void *doit(void *); /* each thread executes this function */
int
main(int argc, char **argv)
{
int listenfd, connfd;
pthread_t tid;
socklen_t addrlen, len;
struct sockaddr *cliaddr;
//1.创建监听套接字
if (argc == 2)
listenfd = Tcp_listen(NULL, argv[1], &addrlen);
else if (argc == 3)
listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
else
err_quit("usage: tcpserv01 [ <host> ] <service or port>");
cliaddr = Malloc(addrlen);
//2.创建线程
for ( ; ; ) {
len = addrlen;
connfd = Accept(listenfd, cliaddr, &len);
//accept 返回之后,改为调用 pthread_create 取代调用 fork
Pthread_create(&tid, NULL, &doit, (void *) connfd);
}
}
//3.线程函数
static void *
doit(void *arg)
{
//pthread_detach 使自身脱离主线程,这样主线程不用等待它
Pthread_detach(pthread_self());
str_echo((int) arg); /* same function as before */
//关闭已连接套接字
Close((int) arg); /* done with connected socket */
return(NULL);
}/**
* TCP 每个客户一个线程,可移植的参数传递
* **/
#include "unpthread.h"
static void *doit(void *); /* each thread executes this function */
int
main(int argc, char **argv)
{
int listenfd, *iptr;
thread_t tid;
socklen_t addrlen, len;
struct sockaddr *cliaddr;
//1.创建监听套接字
if (argc == 2)
listenfd = Tcp_listen(NULL, argv[1], &addrlen);
else if (argc == 3)
listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
else
err_quit("usage: tcpserv01 [ <host> ] <service or port>");
cliaddr = Malloc(addrlen);
//2.创建线程
for ( ; ; ) {
len = addrlen;
iptr = Malloc(sizeof(int));
*iptr = Accept(listenfd, cliaddr, &len);
Pthread_create(&tid, NULL, &doit, iptr);
}
}
//3.线程函数
static void *
doit(void *arg)
{
int connfd;
connfd = *((int *) arg);
free(arg);
Pthread_detach(pthread_self());
str_echo(connfd); /* same function as before */
Close(connfd); /* done with connected socket */
return(NULL);
}UNIX网络编程卷1 回射服务器程序 TCP服务器程序设计范式 四个版本
原文:http://blog.csdn.net/zhengsenlie/article/details/38826865