2017-2018-1 20155219 《信息安全系统设计基础》实验三——实时系统

  • 任务一

1.学习使用Linux命令wc(1)

2.基于Linux Socket程序设计实现wc服务器(端口号是你学号的后6位)和客户端

3.客户端传一个文本文件给服务器

4.服务器返加文本文件中的单词数
实验步骤

命令wc(1)

1.命令格式:

wc [选项]文件...

2.命令功能:

统计指定文件中的字节数、字数、行数,并将统计结果显示输出。该命令统计指定文件中的字节数、字数、行数。如果没有给出文件名,则从标准输入读取。wc同时也给出所指定文件的总统计数。

3.命令参数:

-c 统计字节数。

-l 统计行数。

-m 统计字符数。这个标志不能与 -c 标志一起使用。

-w 统计字数。一个字被定义为由空白、跳格或换行字符分隔的字符串。

-L 打印最长行的长度。

-help 显示帮助信息

学到的知识点

  • 更加清楚了客户端与服务器之间的编程关系

  • 学习了基于c语言的多线程socket编程。

  • 学习了线程之间的分工合作

  • 了解了线程间的互斥机制保

  • 任务1客户端

#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <stdio.h>        // for printf
#include <stdlib.h>        // for exit
#include <string.h>        // for bzero
#include <time.h>
#define HELLO_WORLD_SERVER_PORT    6666 
#define BUFFER_SIZE 1024
char* itoa(int num,char* str,int radix);
void talk_to_server(char ** argv)
{
    //设置一个socket地址结构client_addr,代表客户机internet地址, 端口
    struct sockaddr_in client_addr;
    bzero(&client_addr,sizeof(client_addr)); //把一段内存区的内容全部设置为0
    client_addr.sin_family = AF_INET;    //internet协议族
    client_addr.sin_addr.s_addr = htons(INADDR_ANY);//INADDR_ANY表示自动获取本机地址
    client_addr.sin_port = htons(0);    //0表示让系统自动分配一个空闲端口
    //创建用于internet的流协议(TCP)socket,用client_socket代表客户机socket
    int client_socket = socket(AF_INET,SOCK_STREAM,0);
    time_t t;
    if( client_socket < 0)
    {
        printf("Create Socket Failed!\n");
        exit(1);
    }
    //把客户机的socket和客户机的socket地址结构联系起来
    if( bind(client_socket,(struct sockaddr*)&client_addr,sizeof(client_addr)))
    {
        printf("Client Bind Port Failed!\n"); 
        exit(1);
    }

    //设置一个socket地址结构server_addr,代表服务器的internet地址, 端口
    struct sockaddr_in server_addr;
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    if(inet_aton(argv[1],&server_addr.sin_addr) == 0) //服务器的IP地址来自程序的参数
    {
        printf("Server IP Address Error!\n");
        exit(1);
    }
    server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
    socklen_t server_addr_length = sizeof(server_addr);
    //向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接
    if(connect(client_socket,(struct sockaddr*)&server_addr, server_addr_length) < 0)
    {
        printf("Can Not Connect To %s!\n",argv[1]);
        exit(1);
    }

    char buffer[BUFFER_SIZE];
    bzero(buffer,BUFFER_SIZE);
    //从服务器接收数据到buffer中
    int i=0;
    int length = recv(client_socket,buffer,BUFFER_SIZE,0);
    if(length < 0)
    {
        printf("Recieve Data From Server %s Failed!\n", argv[1]);
        exit(1);
    }
    printf("From Server %s :\t",argv[1]);
    /*
    while(buffer[i]!='\n')
    {
        printf("%c",buffer[i]);
        i++;
    }
    printf("\n");
    i++;
    int b=buffer[i]-48+buffer[++i]-48;
    printf("%d\n",b);*/
    length = recv(client_socket,(void *)&t,sizeof(time_t),0);
    if(length < 0)
    {
        printf("Recieve Data From Server %s Failed!\n", argv[1]);
        exit(1);
    }
    printf("当前时间: %s \n",ctime(&t));
 
    bzero(buffer,BUFFER_SIZE);
    //从服务器接收数据到buffer中
    
    length= recv(client_socket,buffer,BUFFER_SIZE,0);
    if(length < 0)
    {
        printf("Recieve Data From Server %s Failed!\n", argv[1]);
        exit(1);
    }
    printf("%s",buffer);
    
    bzero(buffer,BUFFER_SIZE);
    strcat(buffer,argv[2]);
    /*itoa(b,buffer,10);
       */ 
  
    //向服务器发送buffer中的数据
    send(client_socket,buffer,BUFFER_SIZE,0);
    //关闭socket
    close(client_socket);
}

char* itoa(int num,char *str,int radix)//number->string
{/*索引表*/
char index[]="0123456789ABCDEF";
unsigned unum;/*中间变量*/
int i=0,j,k;
/*确定unum的值*/
if(radix==10&&num<0)/*十进制负数*/
{
unum=(unsigned)-num;
str[i++]='-';
}
else unum=(unsigned)num;/*其他情况*/
/*转换*/
do{
str[i++]=index[unum%(unsigned)radix];
unum/=radix;
}while(unum);
str[i]='\0';
/*逆序*/
if(str[0]=='-')k=1;/*十进制负数*/
else k=0;
char temp;
for(j=k;j<=(i-1)/2;j++)
{
temp=str[j];
str[j]=str[i-1+k-j];
str[i-1+k-j]=temp;
}
return str;
}


int main(int argc, char **argv)
{
    if (argc != 3)
    {
        printf("Usage: .\%s ServerIPAddress\n",argv[0]);
        exit(1);
    }
    talk_to_server(argv);

    return 0;
}

  • 服务器
#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <stdio.h>        // for printf
#include <stdlib.h>        // for exit
#include <string.h>        // for bzero
#include <unistd.h>        // for fork
#include <sys/signal.h> // for signal
#include <sys/wait.h>    // for wait
#include <time.h>
#include <arpa/inet.h>
#define HELLO_WORLD_SERVER_PORT    6666 
#define LENGTH_OF_LISTEN_QUEUE  20
#define BUFFER_SIZE 1024
char* itoa(int num,char *str,int radix);
int CountWordsOfEuropeanTxtFile(char *szFileName);  
int CountWordsInOneLine(const char *szLine);
void reaper(int sig)
{
    int status;
    //调用wait3读取子进程的返回值,使zombie状态的子进程彻底释放
    while(wait3(&status,WNOHANG,(struct rusage*)0) >=0)
        ;
}
int main(int argc, char **argv)
{

    //设置一个socket地址结构server_addr,代表服务器internet地址, 端口
    struct sockaddr_in server_addr;
    char a,b;
    bzero(&server_addr,sizeof(server_addr)); //把一段内存区的内容全部设置为0
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htons(INADDR_ANY);
    server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);

    time_t t;
    t=time(NULL);

    printf("服务器开始工作!\n");

    //创建用于internet的流协议(TCP)socket,用server_socket代表服务器socket
    int server_socket = socket(AF_INET,SOCK_STREAM,0);
    if( server_socket < 0)
    {
        printf("Create Socket Failed!\n");
        exit(1);
    }
    
    //把socket和socket地址结构联系起来
    if( bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr)))
    {
        printf("Server Bind Port : %d Failed!\n", HELLO_WORLD_SERVER_PORT); 
        exit(1);
    }
    
    //server_socket用于监听
    if ( listen(server_socket, LENGTH_OF_LISTEN_QUEUE) )
    {
        printf("Server Listen Failed!"); 
        exit(1);
    }
    //通知操作系统,当收到子进程的退出信号(SIGCHLD)时,执行reaper函数,释放zombie状态的进程
    (void)signal(SIGCHLD,reaper);
    
    while (1) //服务器端要一直运行
    {
        //定义客户端的socket地址结构client_addr
        struct sockaddr_in client_addr;
        socklen_t length = sizeof(client_addr);

        //接受一个到server_socket代表的socket的一个连接
        //如果没有连接请求,就等待到有连接请求--这是accept函数的特性
        //accept函数返回一个新的socket,这个socket(new_server_socket)用于同连接到的客户的通信
        //new_server_socket代表了服务器和客户端之间的一个通信通道
        //accept函数把连接到的客户端信息填写到客户端的socket地址结构client_addr中

        int new_server_socket = accept(server_socket,(struct sockaddr*)&client_addr,&length);
        if ( new_server_socket < 0)
        {
            printf("Server Accept Failed!\n");
            break;
        }
        int child_process_pid = fork(); //fork()后,子进程是主进程的拷贝
        //在主进程和子进程中的区别是fork()的返回值不同.
        if(child_process_pid == 0 )//如果当前进程是子进程,就执行与客户端的交互
        {
            close(server_socket); //子进程中不需要被复制过来的server_socket
            char buffer[BUFFER_SIZE];
             /*bzero(buffer, BUFFER_SIZE);//clean
            strcpy(buffer,"20155219实现");
            strcat(buffer,"\n"); 
           printf("enter two numbers:\n");
            scanf("%c %c",&a,&b);
            
            strcat(buffer,&a); 
            strcat(buffer,&b); //C语言字符串连接              
            */
            //发送buffer中的字符串到new_server_socket,实际是给客户端
            send(new_server_socket,buffer,BUFFER_SIZE,0);
            printf("服务器实现者学号:20155219\n");
            printf("当前时间: %s\n",ctime(&t));
            send(new_server_socket,(void *)&t,sizeof(time_t),0);

            bzero(buffer,BUFFER_SIZE);
            //接收客户端发送来的信息到buffer中
            length = recv(new_server_socket,buffer,BUFFER_SIZE,0);
            if (length < 0)
            {
                printf("Server Recieve Data Failed!\n");
                exit(1);
            }
            printf("\n%s\n",buffer);
         int k=0;   
         char name[10];
         while(*(buffer+k)!='\0')
         {
name[k]=*(buffer+k);  
k++;
}
    printf("文件中单词个数%d\n",CountWordsOfEuropeanTxtFile(name));
    
    bzero(buffer, BUFFER_SIZE);//clean
            strcpy(buffer,"20155219实现");
            strcat(buffer,"文件中单词个数"); 
            strcat(buffer,itoa(CountWordsOfEuropeanTxtFile(name),buffer,10)); 
            strcat(buffer,"\n"); 
            send(new_server_socket,buffer,BUFFER_SIZE,0);
            
            
            //关闭与客户端的连接
            close(new_server_socket); 
            exit(0);         
        }
        else if(child_process_pid > 0)     //如果当前进程是主进程 
            close(new_server_socket);    //主进程中不需要用于同客户端交互的new_server_socket
    }
    //关闭监听用的socket
    close(server_socket);
    return 0;
}
/***********************************************\ 
函数名称: 
    CountWordsOfEuropeanTxtFile 
功能描述: 
    统计文件中单词个数 
函数参数: 
    char *szFileName 文件名 
返回值: 
    int 词的个数 
\**********************************************/  
int CountWordsOfEuropeanTxtFile(char *szFileName)  
{  
    int nWords = 0;//词计数变量,初始值为0  
    FILE *fp; //文件指针  
    char carrBuffer[1024];//每行字符缓冲,每行最多1024个字符  
  
    //打开文件  
    if ((fp = fopen(szFileName,  "r")) == NULL)  
    {  
        return -1;  //文件打开不成功是返回-1  
    }  
  
    while (!feof(fp))//如果没有读到文件末尾   
    {  
        //从文件中读一行  
        if (fgets(carrBuffer, sizeof(carrBuffer),fp) != NULL)  
            //统计每行词数  
            nWords += CountWordsInOneLine(carrBuffer);  
    }  
  
    //关闭文件  
    fclose(fp);  
  
    return nWords;  
}  
/* 
 *  函数名称: 
 *      CountWordsInOneLine 
 *  功能:统计一行正文中词的个数 
 *  参数:const char *szLine 一行正文 
 *  返回值: 
 *  int 单词数 
 */  
int CountWordsInOneLine(const char *szLine)  
{  
    int nWords = 0;  
    int i=0;  
    for (;i<strlen(szLine);i++)  
    {  
        if (*(szLine+i)!=' '&&(*(szLine+i+1)==' '||*(szLine+i+1)=='\n'))  
        {  
            nWords++;  
            while ((*(szLine+i)!=' ')&&(*(szLine+i+1)!='\0'))  
            {  
                i++;  
            }  
        }  
  
    }  
            //printf("%d\t",nWords);  
      
    return nWords;  
}  

char* itoa(int num,char *str,int radix)//number->string
{/*索引表*/
char index[]="0123456789ABCDEF";
unsigned unum;/*中间变量*/
int i=0,j,k;
/*确定unum的值*/
if(radix==10&&num<0)/*十进制负数*/
{
unum=(unsigned)-num;
str[i++]='-';
}
else unum=(unsigned)num;/*其他情况*/
/*转换*/
do{
str[i++]=index[unum%(unsigned)radix];
unum/=radix;
}while(unum);
str[i]='\0';
/*逆序*/
if(str[0]=='-')k=1;/*十进制负数*/
else k=0;
char temp;
for(j=k;j<=(i-1)/2;j++)
{
temp=str[j];
str[j]=str[i-1+k-j];
str[i-1+k-j]=temp;
}
return str;
}

实现如下图:

image

image

任务二

使用多线程实现wc服务器并使用同步互斥机制保证计数正确

服务器端代码如下所示:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <pthread.h>  
  
#define PORT 155228
#define BACKLOG 5  
#define MAXDATASIZE 1000
  
void process_cli(int connfd, struct sockaddr_in client);  
void *function(void* arg);  
struct ARG {  
int connfd;  
struct sockaddr_in client;  
}; 
  
main()  
{  
int listenfd,connfd;  
pthread_t  tid;  
struct ARG *arg;  
struct sockaddr_in server;  
struct sockaddr_in client;  
socklen_t  len;  
  
if ((listenfd =socket(AF_INET, SOCK_STREAM, 0)) == -1) {  
perror("Creatingsocket failed.");  
exit(1);  
}  
  
int opt =SO_REUSEADDR;  
setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));  
  
bzero(&server,sizeof(server));  
server.sin_family=AF_INET;  
server.sin_port=htons(PORT);  
server.sin_addr.s_addr= htonl (INADDR_ANY);  
if (bind(listenfd,(struct sockaddr *)&server, sizeof(server)) == -1) {  
perror("Bind()error.");  
exit(1);  
}  
  
if(listen(listenfd,BACKLOG)== -1){  
perror("listen()error\n");  
exit(1);  
}  
  
len=sizeof(client);  
while(1)  
{  
if ((connfd =accept(listenfd,(struct sockaddr *)&client,&len))==-1) {  
perror("accept() error\n");  
exit(1);  
}  
arg = (struct ARG *)malloc(sizeof(struct ARG));  
arg->connfd =connfd;  
memcpy((void*)&arg->client, &client, sizeof(client));  
  
if(pthread_create(&tid, NULL, function, (void*)arg)) {  
perror("Pthread_create() error");  
exit(1);  
}  
}  
close(listenfd);  
}  
  
void process_cli(int connfd, struct sockaddr_in client)  
{  
int num;  
char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];  
  
printf("Yougot a connection from %s. \n ",inet_ntoa(client.sin_addr) );  
num = recv(connfd,cli_name, MAXDATASIZE,0);  
if (num == 0) {  
close(connfd);  
printf("Clientdisconnected.\n");  
return;  
}  
cli_name[num - 1] ='\0';  
printf("Client'sname is %s.\n",cli_name);  
  
while (num =recv(connfd, recvbuf, MAXDATASIZE,0)) {  
recvbuf[num] ='\0';  
printf("Receivedclient( %s ) message: %s",cli_name, recvbuf);  
int i;  
for (i = 0; i <num - 1; i++) {  
if((recvbuf[i]>='a'&&recvbuf[i]<='z')||(recvbuf[i]>='A'&&recvbuf[i]<='Z'))  
{  
recvbuf[i]=recvbuf[i]+ 3;  
if((recvbuf[i]>'Z'&&recvbuf[i]<='Z'+3)||(recvbuf[i]>'z'))  
recvbuf[i]=recvbuf[i]- 26;  
}  
sendbuf[i] =recvbuf[i];  
}  
sendbuf[num -1] = '\0';  
send(connfd,sendbuf,strlen(sendbuf),0);  
}  
close(connfd);  
}  
  
void *function(void* arg)  
{  
struct ARG *info;  
info = (struct ARG*)arg;  
process_cli(info->connfd,info->client);  
free (arg);  
pthread_exit(NULL);  
}  

客户端代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 155228
#define BACKLOG 5
#define MAXDATASIZE 1000
void process_cli(int  connfd, struct sockaddr_in client);

main()
{
int  listenfd, connfd;
pid_t  pid;
struct  sockaddr_in  server;
struct sockaddr_in  client;
int  len;

if ((listenfd =socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Creatingsocket failed.");
exit(1);
}

int opt =SO_REUSEADDR;
setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_port=htons(PORT);
server.sin_addr.s_addr= htonl (INADDR_ANY);
if (bind(listenfd,(struct sockaddr *)&server, sizeof(server)) == -1) {
perror("Bind()error.");
exit(1);
}

if(listen(listenfd,BACKLOG)== -1){
perror("listen() error\n");
exit(1);
}
len=sizeof(client);

while(1)
{
if ((connfd =accept(listenfd,(struct sockaddr *)&client,&len))==-1) {
perror("accept() error\n");
exit(1);
}
if ((pid=fork())>0){
close(connfd);
continue;
}
else if (pid==0) {
close(listenfd);
process_cli(connfd, client);
exit(0);
}
else {
printf("fork()error\n");
exit(0);
}
}
close(listenfd);
}

void process_cli(int connfd, struct sockaddr_in client)
{
int num;
char  recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];
printf("Yougot a connection from %s. ",inet_ntoa(client.sin_addr) );
num = recv(connfd,cli_name, MAXDATASIZE,0);
if (num == 0)
{
close(connfd);
printf("Client disconnected.\n");
return;
}
cli_name[num - 1] ='\0';
printf("Client'sname is %s.\n",cli_name);

while (num =recv(connfd, recvbuf, MAXDATASIZE,0)) {
recvbuf[num] ='\0';
printf("Receivedclient( %s ) message: %s",cli_name, recvbuf);
int i = 0;
for (i = 0;i < num - 1; i++) {
if((recvbuf[i]>='a'&&recvbuf[i]<='z')||(recvbuf[i]>='A'&&recvbuf[i]<='Z'))
{
recvbuf[i]=recvbuf[i]+ 3;
if((recvbuf[i]>'Z'&&recvbuf[i]<='Z'+3)||(recvbuf[i]>'z'))
recvbuf[i]=recvbuf[i]- 26;
}
sendbuf[i] =recvbuf[i];
}
sendbuf[num - 1]= '\0';

send(connfd,sendbuf,strlen(sendbuf),0);
}
close(connfd);
}

实现结果如下图:
image