首页 > 技术文章 > 2017-2018-1 20155204 实验三 实时系统

20155204wh 2017-11-19 16:57 原文

2017-2018-1 20155204 实验三 实时系统

任务一:

  • 学习使用Linux命令wc(1)
  • 基于LinuxSocket程序设计实现wc(1)服务器(端口号是你学号的后6位)和客户端
  • 客户端传一个文本文件给服务器
  • 服务器返加文本文件中的单词数

实验步骤

  • man 1 wc查看wc用法

  • 分析算法步骤:在文件中逐个读取字符,读到分隔符计数器加1。

  • 代码实现如下

  • 服务端

#include<netinet/in.h> // sockaddr_in 
#include<sys/types.h>  // socket 
#include<sys/socket.h> // socket 
#include<stdio.h>    // printf 
#include<stdlib.h>   // exit 
#include<string.h>   // bzero 
  
#define SERVER_PORT 8000 
#define LENGTH_OF_LISTEN_QUEUE 20 
#define BUFFER_SIZE 1024 
#define FILE_NAME_MAX_SIZE 512 
  
int main(void) 
{ 
  // 声明并初始化一个服务器端的socket地址结构 
  struct sockaddr_in server_addr; 
  bzero(&server_addr, sizeof(server_addr)); 
  server_addr.sin_family = AF_INET; 
  server_addr.sin_addr.s_addr = htons(INADDR_ANY); 
  server_addr.sin_port = htons(SERVER_PORT); 
  
  // 创建socket,若成功,返回socket描述符 
  int server_socket_fd = socket(PF_INET, SOCK_STREAM, 0); 
  if(server_socket_fd < 0) 
  { 
    perror("Create Socket Failed:"); 
    exit(1); 
  } 
  int opt = 1; 
  setsockopt(server_socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 
  
  // 绑定socket和socket地址结构 
  if(-1 == (bind(server_socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)))) 
  { 
    perror("Server Bind Failed:"); 
    exit(1); 
  } 
    
  // socket监听 
  if(-1 == (listen(server_socket_fd, LENGTH_OF_LISTEN_QUEUE))) 
  { 
    perror("Server Listen Failed:"); 
    exit(1); 
  } 
  
  while(1) 
  { 
 
    struct sockaddr_in client_addr; 
    socklen_t client_addr_length = sizeof(client_addr); 
  
  
    int new_server_socket_fd = accept(server_socket_fd, (struct sockaddr*)&client_addr, &client_addr_length); 
    if(new_server_socket_fd < 0) 
    { 
      perror("Server Accept Failed:"); 
      break; 
    } 
  
    char buffer[BUFFER_SIZE]; 
    bzero(buffer, BUFFER_SIZE); 
    if(recv(new_server_socket_fd, buffer, BUFFER_SIZE, 0) < 0) 
    { 
      perror("Server Recieve Data Failed:"); 
      break; 
    } 
  

    char file_name[FILE_NAME_MAX_SIZE+1]; 
    bzero(file_name, FILE_NAME_MAX_SIZE+1); 
    strncpy(file_name, buffer, strlen(buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer)); 
    printf("%s\n", file_name); 
  

    FILE *fp = fopen(file_name, "r"); 
    if(NULL == fp) 
    { 
      printf("File:%s Not Found\n", file_name); 
    } 
    else
    { 
      bzero(buffer, BUFFER_SIZE); 
      int length = 0; 
 
      while((length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0) 
      { 
        if(send(new_server_socket_fd, buffer, length, 0) < 0) 
        { 
          printf("Send File:%s Failed./n", file_name); 
          break; 
        } 
        bzero(buffer, BUFFER_SIZE); 
      } 
  

      fclose(fp); 
      printf("File:%s Transfer Successful!\n", file_name); 
    } 
    // 关闭与客户端的连接 
    close(new_server_socket_fd); 
  } 
  // 关闭监听用的socket 
  close(server_socket_fd); 
  return 0; 
} 
  • 客户端
#include<netinet/in.h>  // sockaddr_in 
#include<sys/types.h>  // socket 
#include<sys/socket.h>  // socket 
#include<stdio.h>    // printf 
#include<stdlib.h>    // exit 
#include<string.h>    // bzero 
  
#define SERVER_PORT 155204 
#define BUFFER_SIZE 1024 
#define FILE_NAME_MAX_SIZE 512 
  
int main() 
{ 

  struct sockaddr_in client_addr; 
  bzero(&client_addr, sizeof(client_addr)); 
  client_addr.sin_family = AF_INET; 
  client_addr.sin_addr.s_addr = htons(INADDR_ANY); 
  client_addr.sin_port = htons(0); 
  
  int client_socket_fd = socket(AF_INET, SOCK_STREAM, 0); 
  if(client_socket_fd < 0) 
  { 
    perror("Create Socket Failed:"); 
    exit(1); 
  } 
  
  if(-1 == (bind(client_socket_fd, (struct sockaddr*)&client_addr, sizeof(client_addr)))) 
  { 
    perror("Client Bind Failed:"); 
    exit(1); 
  } 
  
  struct sockaddr_in server_addr; 
  bzero(&server_addr, sizeof(server_addr)); 
  server_addr.sin_family = AF_INET; 
  if(inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) == 0) 
  { 
    perror("Server IP Address Error:"); 
    exit(1); 
  } 
  server_addr.sin_port = htons(SERVER_PORT); 
  socklen_t server_addr_length = sizeof(server_addr); 
  
  if(connect(client_socket_fd, (struct sockaddr*)&server_addr, server_addr_length) < 0) 
  { 
    perror("Can Not Connect To Server IP:"); 
    exit(0); 
  } 
 
  char file_name[FILE_NAME_MAX_SIZE+1]; 
  bzero(file_name, FILE_NAME_MAX_SIZE+1); 
  printf("Please Input File Name On Server:\t"); 
  scanf("%s", file_name); 
  
  char buffer[BUFFER_SIZE]; 
  bzero(buffer, BUFFER_SIZE); 
  strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name)); 

  if(send(client_socket_fd, buffer, BUFFER_SIZE, 0) < 0) 
  { 
    perror("Send File Name Failed:"); 
    exit(1); 
  } 
  

  FILE *fp = fopen(file_name, "w"); 
  if(NULL == fp) 
  { 
    printf("File:\t%s Can Not Open To Write\n", file_name); 
    exit(1); 
  } 
  

  bzero(buffer, BUFFER_SIZE); 
  int length = 0; 
  while((length = recv(client_socket_fd, buffer, BUFFER_SIZE, 0)) > 0) 
  { 
    if(fwrite(buffer, sizeof(char), length, fp) < length) 
    { 
      printf("File:\t%s Write Failed\n", file_name); 
      break; 
    } 
    bzero(buffer, BUFFER_SIZE); 
  } 

  printf("Send File:\t%s  Successful!\n", file_name); 
  close(fp); 
  close(client_socket_fd);
  char *argv[]={"wc","-w",file_name,0};
  execvp("wc",argv);
  return 0; 
} 


任务二

  • 多线程实现wc服务器时,会出现多个客户端同时像服务器传送文件的情况,所以仅仅在服务器端创建一个recvfile.txt用于接收文件是远远不够的,需要根据发送的不同文件名创建新的接收文件。
  • 步骤分析

建立一个while循环,不断接收来自客户端的连接;
在循环内创建一个线程
将接收文件、调用mywc函数等过程抽象为一个万能函数,void process_client(void newnew_server_socket)
在线程中调用这个函数,并将用于接收客户端连接的accept函数返回值newnew_server_socket的地址作为参数。

  • 代码实现
    客户端
/*
 * echoclient.c - An echo client
 */
/*
 * echoclient.c - An echo client
 */
/* $begin echoclientmain */
#include "csapp.h"
#include<stdio.h>
#include<stdlib.h>
int wc(char *name)
{
 char ch;
 FILE *fp;
 long count=0;
 char s[21];
 
 
 if ((fp=fopen("test1.txt","r+"))==NULL)
 {
  fprintf(stderr,"不能打开文件%s\n",name);
  exit(EXIT_FAILURE);
 }
while(fscanf(fp,"%s",s)!=EOF)
        count++;
 
 fclose(fp);
 printf("File %s has %ld characters\n",name,count);
 return 0;
}
int main(int argc, char **argv) 
{
    int clientfd, port,n,count;
    char *host, buf[MAXLINE];
    rio_t rio;

    if (argc != 3) {
	fprintf(stderr, "usage: %s <host> <port>\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) {
        if((num=recv(sockfd,buf,MAXDATASIZE,0))==-1)
    {
        printf("recv() error\n");
        exit(1);
    }
    buf[num-1]='\0';
    
        
	Rio_writen(clientfd, buf, strlen(buf));
	Rio_readlineb(&rio, buf, MAXLINE);
	Fputs(buf, stdout);
    }
    Close(clientfd);
    exit(0);
}
/* $end echoclientmain */
/* $begin echoclientmain */
//#include "csapp.h"
/*int main(int argc, char **argv) 
{
    int clientfd, port;
    char *host, buf[MAXLINE];
    char *name;
    rio_t rio;
    FILE *fp;
    if (argc != 4) {
	fprintf(stderr, "usage: %s <host> <port> <filename>\n", argv[0]);
	exit(0);
    }
    host = argv[1];
    port = atoi(argv[2]);
    name = argv[3];
    clientfd = Open_clientfd(host, port);
    Rio_readinitb(&rio, clientfd);

    fp=fopen(name,"r+");
    while (Fgets(buf, MAXLINE,fp) != NULL) {
	Rio_writen(clientfd, buf, strlen(buf));
	Rio_readlineb(&rio, buf, MAXLINE);
	Fputs(buf, stdout);
    }
    Close(clientfd);
    exit(0);
}
/* $end echoclientmain */

服务端

/* 
/* 
 * echoservert_pre.c - A prethreaded concurrent echo server
 */
/* $begin echoservertpremain */
#include "csapp.h"
#include<stdio.h>
#include<stdlib.h>
static int byte_cnt;  /* byte counter */
static sem_t mutex;
#define NTHREADS  4
#define SBUFSIZE  16
typedef struct {
    int *buf;          /* Buffer array */         
    int n;             /* Maximum number of slots */
    int front;         /* buf[(front+1)%n] is first item */
    int rear;          /* buf[rear%n] is last item */
    sem_t mutex;       /* Protects accesses to buf */
    sem_t slots;       /* Counts available slots */
    sem_t items;       /* Counts available items */
} sbuf_t;
void echo_cnt(int connfd);
void *thread(void *vargp);
int wc(char *name)
{
 char ch;
 FILE *fp;
 long count=0;
 char s[21];
 
 
 if ((fp=fopen(name,"r+"))==NULL)
 {
  fprintf(stderr,"不能打开文件\n");
  exit(EXIT_FAILURE);
 }
while(fscanf(fp,"%s",s)!=EOF)
        count++;
 
 fclose(fp);
 printf("File %s has %ld characters\n",name,count);
 return 0;
}

sbuf_t sbuf; /* shared buffer of connected descriptors */

int main(int argc, char **argv) 
{
    int i, listenfd, connfd, 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]);
    sbuf_init(&sbuf, SBUFSIZE);
    listenfd = Open_listenfd(port);

    for (i = 0; i < NTHREADS; i++)  /* Create worker threads */
	Pthread_create(&tid, NULL, thread, NULL);

    while (1) { 
	connfd = Accept(listenfd, (SA *) &clientaddr, &clientlen);
	sbuf_insert(&sbuf, connfd); /* Insert connfd in buffer */
    }
}

static void init_echo_cnt(void)
{
    Sem_init(&mutex, 0, 1);
    byte_cnt = 0;
}

void echo_cnt(int connfd) 
{
    int n,x; 
    long int count;
    char buf[MAXLINE]; 
    char name[MAXLINE]
    rio_t rio;
    static pthread_once_t once = PTHREAD_ONCE_INIT;

    Pthread_once(&once, init_echo_cnt);
    Rio_readinitb(&rio, connfd);
    while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {
	P(&mutex);
	byte_cnt += n;
        /*x = sizeof(buf);
        buf[x] = 0;
        count = wc(buf);*/
	printf("thread %d received %d (%d total) bytes on fd %d\n", 
	       (int) pthread_self(), n, byte_cnt, connfd);
        //name = buf;
	V(&mutex);
        //sprint(buf,"%s:%ld characters".count);

	Rio_writen(connfd, buf, n);
    }
}

void sbuf_init(sbuf_t *sp, int n)
{
    sp->buf = Calloc(n, sizeof(int)); 
    sp->n = n;                       /* Buffer holds max of n items */
    sp->front = sp->rear = 0;        /* Empty buffer iff front == rear */
    Sem_init(&sp->mutex, 0, 1);      /* Binary semaphore for locking */
    Sem_init(&sp->slots, 0, n);      /* Initially, buf has n empty slots */
    Sem_init(&sp->items, 0, 0);      /* Initially, buf has zero data items */
}
/* $end sbuf_init */

/* Clean up buffer sp */
/* $begin sbuf_deinit */
void sbuf_deinit(sbuf_t *sp)
{
    Free(sp->buf);
}
/* $end sbuf_deinit */

/* Insert item onto the rear of shared buffer sp */
/* $begin sbuf_insert */
void sbuf_insert(sbuf_t *sp, int item)
{
    P(&sp->slots);                          /* Wait for available slot */
    P(&sp->mutex);                          /* Lock the buffer */
    sp->buf[(++sp->rear)%(sp->n)] = item;   /* Insert the item */
    V(&sp->mutex);                          /* Unlock the buffer */
    V(&sp->items);                          /* Announce available item */
}
/* $end sbuf_insert */

/* Remove and return the first item from buffer sp */
/* $begin sbuf_remove */
int sbuf_remove(sbuf_t *sp)
{
    int item;
    P(&sp->items);                          /* Wait for available item */
    P(&sp->mutex);                          /* Lock the buffer */
    item = sp->buf[(++sp->front)%(sp->n)];  /* Remove the item */
    V(&sp->mutex);                          /* Unlock the buffer */
    V(&sp->slots);                          /* Announce available slot */
    return item;
}

void *thread(void *vargp) 
{  
    Pthread_detach(pthread_self()); 
    while (1) { 
	int connfd = sbuf_remove(&sbuf); /* Remove connfd from buffer */
	echo_cnt(connfd);                /* Service client */
	Close(connfd);
    }
}
/* $end echoservertpremain */

实验中遇到的问题

问题1: 提示找不到csapp.h文件。
回答1:深入理解计算机系统 头文件 csapp.h 如何在linux里使用学习这篇博客找到答案。

实验体会

编写网络程序之前,我对网
路通信可以说一无所知,
根本不知道那些网路上的程序是怎么通过网
路进行通信的。在本次实验中,终于揭开了他们神秘的面纱。当然在
实验中出现了很多的插曲,
我是在
Linux
环境下编写的程序,
很多技
术方面的问题都无法解决。
查阅书籍和网上咨询才弄懂了他们的来龙
去脉,最终是将我的程序编写完了。开始接触网路编程时,那是一头
雾水,
根本不知从何下手。
在看了辅导教员给我们程序之后才有了很
多的起色,才能够自己编写程序。在网络编程中,我最深的体会就是
对具体的内部细节不熟悉,
导致在调试的过程中感觉到很无力。
在很
多函数的作用下,经常就是一团乱麻。之后,我每次编写之前就是将
用到的函数的用法温习一下,最后还是能够解决很多的问题的。

推荐阅读