1,实现的基本功能:
客户端:发送一行文本给服务器,服务器显示收到的字节数,并返回收到的内容给客户端。
2,一个单进程的实现实例:
file echo.c:
-
#include "csapp.h"
-
-
void echo(int connfd)
-
{
-
size_t n;
-
char buf[MAXLINE];
-
rio_t rio;
-
-
Rio_readinitb(&rio, connfd);
-
while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0)
-
{
-
printf("server received %d bytes\n", n);
-
Rio_writen(connfd, buf, n);
-
}
-
}
file echoclient.c:
-
#include "csapp.h"
-
-
int main(int argc, char **argv)
-
{
-
int clientfd, port;
-
char *host, buf[MAXLINE];
-
rio_t rio;
-
-
if (argc != 3)
-
{
-
fprintf(stderr, "usage: %s \n", argv[0]);
-
exit(0);
-
}
-
host = argv[1];
-
port = atoi(argv[2]);
-
-
clientfd = Open_clientfd(host, port);
-
Rio_readinitb(&rio, clientfd);
-
-
while (Fgets(buf, MAXLINE, stdin) != NULL)
-
{
-
Rio_writen(clientfd, buf, strlen(buf));
-
Rio_readlineb(&rio, buf, MAXLINE);
-
Fputs(buf, stdout);
-
}
-
Close(clientfd);
-
exit(0);
-
}
file: echoserver.c
-
#include "csapp.h"
-
-
void echo(int connfd);
-
-
int main(int argc, char **argv)
-
{
-
int listenfd, connfd, port, clientlen;
-
struct sockaddr_in clientaddr;
-
struct hostent *hp;
-
char *haddrp;
-
if (argc != 2)
-
{
-
fprintf(stderr, "usage: %s \n", argv[0]);
-
exit(0);
-
}
-
port = atoi(argv[1]);
-
-
listenfd = Open_listenfd(port);
-
while (1)
-
{
-
clientlen = sizeof(clientaddr);
-
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
-
-
-
hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
-
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
-
haddrp = inet_ntoa(clientaddr.sin_addr);
-
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
-
-
echo(connfd);
-
Close(connfd);
-
}
-
exit(0);
-
}
注:此时,只能一次处理一个客户端,当第二个客户端加入时,被阻塞。
使用:
服务端:
./echoserver 8080
server connected to kmplayer (127.0.0.1)
server received 16 bytes
server received 14 bytes
server received 5 bytes
客户端:
./echoserver 8080
server connected to kmplayer (127.0.0.1)
server received 16 bytes
server received 14 bytes
server received 5 bytes
3,
基于进程的并发echo服务器:
-
#include "csapp.h"
-
-
void echo(int connfd);
-
-
void sigchld_handler(int sig)
-
{
-
while (waitpid(-1, 0, WNOHANG) > 0)
-
;
-
return;
-
}
-
-
int main(int argc, char **argv)
-
{
-
int listenfd, connfd, port, clientlen = sizeof(struct sockaddr_in);
-
struct sockaddr_in clientaddr;
-
struct hostent *hp;
-
char *haddrp;
-
if (argc != 2)
-
{
-
fprintf(stderr, "usage: %s \n", argv[0]);
-
exit(0);
-
}
-
port = atoi(argv[1]);
-
Signal(SIGCHLD, sigchld_handler);
-
-
listenfd = Open_listenfd(port);
-
while (1)
-
{
-
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
-
-
if (Fork() == 0)
-
{
-
Close(listenfd);
-
hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
-
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
-
haddrp = inet_ntoa(clientaddr.sin_addr);
-
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
-
echo(connfd);
-
Close(connfd);
-
exit(0);
-
}
-
-
Close(connfd);
-
}
-
exit(0);
-
}
4,基于I/O多路复用,实现了标准输入和客户端的echo服务器
-
-
-
-
-
#include "csapp.h"
-
-
void echo(int connfd);
-
-
void command(void);
-
-
int main(int argc, char **argv)
-
{
-
int listenfd, connfd, port, clientlen = sizeof(struct sockaddr_in);
-
struct sockaddr_in clientaddr;
-
struct hostent *hp;
-
char *haddrp;
-
fd_set read_set,ready_set;
-
if (argc != 2)
-
{
-
fprintf(stderr, "usage: %s \n", argv[0]);
-
exit(0);
-
}
-
port = atoi(argv[1]);
-
listenfd = Open_listenfd(port);
-
-
FD_ZERO(&read_set);
-
FD_SET(STDIN_FILENO, &read_set);
-
FD_SET(listenfd, &read_set);
-
while (1)
-
{
-
ready_set=read_set;
-
Select(listenfd+1, &ready_set, NULL, NULL, NULL);
-
-
if(FD_ISSET(STDIN_FILENO, &ready_set))
-
{
-
printf("server connected to local stdin.\n");
-
command();
-
}
-
if(FD_ISSET(listenfd, &ready_set))
-
{
-
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
-
hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
-
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
-
haddrp = inet_ntoa(clientaddr.sin_addr);
-
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
-
echo(connfd);
-
Close(connfd);
-
}
-
}
-
exit(0);
-
}
-
-
void command(void)
-
{
-
char buf[MAXLINE];
-
if (!Fgets(buf, MAXLINE, stdin))
-
exit(0);
-
printf("%s", buf);
-
}
注:一旦服务器连接到一个客户端,就会连续回送输入行,直到客户端关闭。
期间,你键入命令到标准输入将不会得到响应。
5,基于I/O多路复用,实现了多个客户端的echo服务器
-
-
-
-
-
#include "csapp.h"
-
-
typedef struct
-
{
-
int maxfd;
-
fd_set read_set;
-
fd_set ready_set;
-
int nready;
-
int maxi;
-
int clientfd[FD_SETSIZE];
-
rio_t clientrio[FD_SETSIZE];
-
}pool;
-
-
void init_pool(int listenfd, pool* p);
-
void add_client(int connfd, pool* p);
-
void check_clients(pool* p);
-
-
int byte_cnt=0;
-
-
int main(int argc, char **argv)
-
{
-
int listenfd, connfd, port, clientlen = sizeof(struct sockaddr_in);
-
struct sockaddr_in clientaddr;
-
struct hostent *hp;
-
static pool pool;
-
char *haddrp;
-
fd_set read_set,ready_set;
-
if (argc != 2)
-
{
-
fprintf(stderr, "usage: %s \n", argv[0]);
-
exit(0);
-
}
-
port = atoi(argv[1]);
-
listenfd = Open_listenfd(port);
-
init_pool(listenfd, &pool);
-
-
while (1)
-
{
-
pool.ready_set = pool.read_set;
-
pool.nready = Select(pool.maxfd+1, &pool.ready_set, NULL, NULL, NULL);
-
if(FD_ISSET(listenfd, &pool.ready_set))
-
{
-
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
-
-
hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
-
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
-
haddrp = inet_ntoa(clientaddr.sin_addr);
-
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
-
add_client(connfd, &pool);
-
}
-
check_clients(&pool);
-
}
-
exit(0);
-
}
-
void init_pool(int listenfd, pool* p)
-
{
-
int i;
-
p->maxi = -1;
-
for(i=0; i
-
p->clientfd[i] = -1;
-
p->maxfd = listenfd;
-
FD_ZERO(&p->read_set);
-
FD_SET(listenfd, &p->read_set);
-
}
-
void add_client(int connfd, pool* p)
-
{
-
int i;
-
p->nready--;
-
for (i = 0; i < FD_SETSIZE; i++)
-
if (p->clientfd[i] < 0)
-
{
-
p->clientfd[i] = connfd;
-
Rio_readinitb(&p->clientrio[i], connfd);
-
FD_SET(connfd, &p->read_set);
-
if (connfd > p->maxfd)
-
p->maxfd = connfd;
-
if (i > p->maxi)
-
p->maxi = i;
-
break;
-
}
-
if(i == FD_SETSIZE)
-
app_error("add_client error: Too many clients");
-
}
-
void check_clients(pool* p)
-
{
-
int i, connfd, n;
-
char buf[MAXLINE];
-
rio_t rio;
-
for (i = 0; (i <= p->maxi) && (p->nready > 0); i++)
-
{
-
connfd = p->clientfd[i];
-
rio = p->clientrio[i];
-
if ((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0)
-
{
-
byte_cnt +=n;
-
printf("Server received %d (%d total) bytes on fd %d\n",
-
n, byte_cnt, connfd);
-
Rio_writen(connfd, buf, n);
-
}
-
else
-
{
-
Close(connfd);
-
FD_CLR(connfd, &p->read_set);
-
p->clientfd[i] = -1;
-
}
-
}
-
}
注:客户端只有交替输入时,才可以正确处理
6,基于多线程,实现了多个客户端的echo服务器
-
-
-
-
-
#include "csapp.h"
-
-
void Pthread_create(pthread_t *tidp, pthread_attr_t *attrp,
-
void * (*routine)(void *), void *argp)
-
{
-
int rc;
-
-
if ((rc = pthread_create(tidp, attrp, routine, argp)) != 0)
-
posix_error(rc, "Pthread_create error");
-
}
-
-
-
void Pthread_cancel(pthread_t tid)
-
{
-
int rc;
-
-
if ((rc = pthread_cancel(tid)) != 0)
-
posix_error(rc, "Pthread_cancel error");
-
}
-
-
-
void Pthread_join(pthread_t tid, void **thread_return)
-
{
-
int rc;
-
-
if ((rc = pthread_join(tid, thread_return)) != 0)
-
posix_error(rc, "Pthread_join error");
-
}
-
-
-
-
void Pthread_detach(pthread_t tid)
-
{
-
int rc;
-
if ((rc = pthread_detach(tid)) != 0)
-
posix_error(rc, "Pthread_detach error");
-
}
-
-
-
-
void Pthread_exit(void *retval)
-
{
-
pthread_exit(retval);
-
}
-
-
-
-
pthread_t Pthread_self(void)
-
{
-
return pthread_self();
-
}
-
-
void Pthread_once(pthread_once_t *once_control, void (*init_function)()) {
-
pthread_once(once_control, init_function);
-
}
-
-
-
void echo(int connfd);
-
void* thread(void* vargp);
-
-
int main(int argc, char **argv)
-
{
-
int listenfd, *connfdp, port, clientlen = sizeof(struct sockaddr_in);
-
struct sockaddr_in clientaddr;
-
pthread_t tid;
-
if (argc != 2)
-
{
-
fprintf(stderr, "usage: %s \n", argv[0]);
-
exit(0);
-
}
-
port = atoi(argv[1]);
-
listenfd = Open_listenfd(port);
-
-
while (1)
-
{
-
connfdp = (int*)Malloc(sizeof(int));
-
*connfdp =Accept(listenfd, (SA *)&clientaddr, &clientlen);
-
Pthread_create(&tid, NULL, thread, connfdp);
-
}
-
exit(0);
-
}
-
-
void* thread(void* vargp)
-
{
-
int connfd = *((int*)vargp);
-
printf("server connected to a client.\n");
-
Free(vargp);
-
echo(connfd);
-
Close(connfd);
-
return NULL;
-
}