第八周课下作业2

课上练习3的daytime服务器分别用多进程和多线程实现成并发服务器并测试

基于多进程的并发服务器的编译和调试

(改编自书上代码echoserverp.c)

  • 伪代码
/* 单客户端单进程,统一accept */ 
/* 服务器主进程 */
  socket();
  bind();
  listen();
  while(1)
  {
    accept();
    fork();//子进程
   
  }
  close(); //关闭服务器端套接字
  
/* 服务器子进程1 */  
 echo();//进行相关的输出处理
 recv();
 process();
 send();
 close();//关闭客户端套接字
 /* 服务器子进程2。。。*/

..................
  • 代码
/* 
 * echoserverp.c - A concurrent echo server based on processes
 */
/* $begin echoserverpmain */
#include "csapp.h"
#include <time.h>
#include <string.h>
#define MAXRECVLEN 1024
void echo(int connfd)
{
    
    size_t n; 
    char buf[MAXLINE]; 
    rio_t rio;
    time_t ticks = time(NULL);

    Rio_readinitb(&rio, connfd);
    while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {
	/*printf("server received %d bytes\n", n);*/
        printf("server received %d bytes\n,%s",n,buf);
        printf("服务器实现人:20155203杜可欣\n当前时间:%.24s\r\n",ctime(&ticks));
        snprintf(buf,MAXRECVLEN,"当前时间:%.24s\r\n",ctime(&ticks));
	Rio_writen(connfd, buf, n);
    }



}

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;

    if (argc != 2) {
	fprintf(stderr, "usage: %s <port>\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); /* Child closes its listening socket */
	    echo(connfd);    /* Child services client */
	    Close(connfd);   /* Child closes connection with client */
	    exit(0);         /* Child exits */
	}
	Close(connfd); /* Parent closes connected socket (important!) */
    }
}
/* $end echoserverpmain */

基于多线程的并发服务器的编译和调试

(改编自书上代码echosevert.c)

  • 用多线程代替多进程的意义
多进程方式使用fork生成子进程存在一些问题。
首先,fork占用大量的资源,内存映像要从父进程拷贝到子进程,所有描述符要在子进程中复制;
其次,fork子进程后,需要用进程间通信在父进程和子进程间传递信息,从子进程返回信息给父进程需要做较多的工作。
多线程有助于解决以上两个问题。
线程是进程内的独立执行实体和调度单元,又称为“轻量级”进程(lightwightprocess);
创建线程比进程快10~100倍。一个进程内的所有线程共享相同的内存空间、全局变量等信息(这种机制又带来了同步问题)。
  • 函数说明

1.pthread_create()函数

pthread_create()函数用于创建新线程。当一个程序开始运行时,系统产生一个称为初始线程或主线程的单个线程,额外的线程需要由pthread_create()函数创建。
2. thread()

符合老师上课所讲的“万能函数”用于启动线程

#include<pthread.h>
intpthread_create(pthread_t *tid, const pthread_attr_t *attr, void*(*func)(void *), void *arg);
返回:成功时为0;出错时非0
  • 代码
/* 
 * echoservert.c - A concurrent echo server based on processes
 */
/* $begin echoserverpmain */
#include "csapp.h"
#include <time.h>
#include <string.h>
#define MAXRECVLEN 1024
void echo(int connfd)
{
    
    size_t n; 
    char buf[MAXLINE]; 
    rio_t rio;
    time_t ticks = time(NULL);

    Rio_readinitb(&rio, connfd);
    while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {
	/*printf("server received %d bytes\n", n);*/
        printf("server received %d bytes\n,%s",n,buf);
        printf("服务器实现人:20155203杜可欣\n当前时间:%.24s\r\n",ctime(&ticks));
        snprintf(buf,MAXRECVLEN,"当前时间:%.24s\r\n",ctime(&ticks));
	Rio_writen(connfd, buf, n);
    }
    
}

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 <port>\n", argv[0]);
	exit(0);
    }
    port = atoi(argv[1]);

    listenfd = Open_listenfd(port);
    while (1) {
	connfdp = Malloc(sizeof(int));
	*connfdp = Accept(listenfd, (SA *) &clientaddr, &clientlen);
	Pthread_create(&tid, NULL, thread, connfdp);
    }
}

/* thread routine */
void *thread(void *vargp) 
{  
    int connfd = *((int *)vargp);
    Pthread_detach(pthread_self()); 
    Free(vargp);
    echo(connfd);
    Close(connfd);
    return NULL;
}
/* $end echoservertmain */

课上测试客户端请求三次时间

(改编自书上迭代服务器代码)

码云链接

思考与体会

说实在的我还是没有太搞懂这个多线程是什么意思只是根据在网上看的一些资料有了一些皮毛的认识。希望老师在上课的时候可以多讲一些这个内容。

posted @ 2017-11-12 12:07  20155203杜可欣  阅读(280)  评论(0编辑  收藏  举报