2019-2020-1 20175325石淦铭《信息安全系统设计基础》实验三 《并发程序》
任务一:
- 学习使用Linux命令wc(1)
- 基于Linux Socket程序设计实现wc(1)服务器(端口号是你学号的后6位)和客户端
- 客户端传一个文本文件给服务器
- 服务器返加文本文件中的单词数
- 上方提交代码
- 附件提交测试截图,至少要测试附件中的两个文件
步骤:
- wc指令功能:统计指定文件中的字节数、字数、行数,并将统计结果显示输出。
- wc指令格式:wc [选项] 文件
- wc命令参数:
- c 统计字节数。
- l 统计行数。
- m 统计字符数,不能与 -c 标志一起使用。
- w 统计字数。
- L 打印最长行的长度。
- -help:显示帮助信息
- --version:显示版本信息
- 实现伪代码:
fd = fopen()
//打开文件;
fscanf()
//对文件的内容以字符串的形式进行读取
if..count++
//设置条件,当满足字符串条件时计数; wc -w
由' '
,' '
,' '
作为分隔符进行实现。- 服务器代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#define MYPORT 175325
void main()
{
int serverfd, clientfd;
struct sockaddr_in my_addr;
struct sockaddr_in remote_addr;
char buffer[BUFSIZ];
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family=AF_INET;
my_addr.sin_addr.s_addr=INADDR_ANY;
my_addr.sin_port=htons(MYPORT);
if((serverfd=socket(PF_INET, SOCK_STREAM, 0))==-1)
{
perror("socket");
}
if(bind(serverfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))<0)
{
perror("bind");
}
listen(serverfd, 5);
int addrlen=sizeof(struct sockaddr_in);
while(1)
{
if((clientfd=accept(serverfd, (struct sockaddr *)&remote_addr, &addrlen))<0)
{
perror("accept");
}
}
printf("accept client %s
", inet_ntoa(remote_addr.sin_addr));
int len, i;
long wordscount=0;
int flag=1;
while(1)
{
if((len=recv(clientfd, buffer, 1024, 0))>0)
{
for(i=0; i<len; i++)
{
if(flag==0)
{
switch(buffer[i])
{
case ' ':
wordscount++;
break;
case '
':
wordscount++;
break;
case '
':
wordscount++;
break;
default:
break;
}
}
if(buffer[i]== ' ' || buffer[i]=='
' || buffer[i]=='
') flag=1;
else flag=0;
}
}
if(len<1024) break;
}
send(clientfd, &wordscount, sizeof(long), 0);
close(clientfd);
}
close(serverfd);
}
- 客户端代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#define MYPORT 175325
void main(){
int clientfd;
struct sockaddr_in remote_addr;
char buffer[BUFSIZ];
memset(&remote_addr, 0 , sizeof(remote_addr));
remote_addr.sin_family=AF_INET;
remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
remote_addr.sin_port=htons(MYPORT);
if((clientfd=socket(PF_INET,SOCK_STREAM,0))<0){
perror("socket");
}
if(connect(clientfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr))<0){
perror("connect");
}
int len;
FILE *fp;
char path[20];
gets(path);
fp=fopen(path, "r");
char readch;
int i=0;
while((readch=fgetc(fp))!=EOF){
if(i<1024){
buffer[i]=readch;
i++;
}
else{
i=0;
int n=send(clientfd, buffer, 1024, 0);
}
}
fclose(fp);
if(i!=0)
send(clientfd, buffer, i, 0);
long wordscount;
recv(clientfd, &wordscount, sizeof(long), 0);
printf("%ld
", wordscount);
close(clientfd);
}
- 实验截图:
任务二:
- 使用多线程实现wc服务器并使用同步互斥机制保证计数正确
- 上方提交代码
- 下方提交测试
- 对比单线程版本的性能,并分析原因
步骤:
- 对比单线程版本的性能,分析原因如下:
单线程比较稳定易于实现,运行稳定。而采用多线程可能不会提高程序的执行速度,反而会降低速度,同时,同步应用程序的开发比较容易,但由于需要在上一个任务完成后才能开始新的任务,其效率通常比多线程应用程序低
- 重新编写支持多线程的服务器程序,然后编译,执行同上步
- 实验截图:
任务三:
- 交叉编译多线程版本服务器并部署到实验箱中
- PC机作客户端测试wc服务器
- 提交测试截图
- 关键代码:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
//指向结构体SECURITY_ATTRIBUTES的指针,表示指定新建线程的安全属性
DWORD dwStackSize,
//指定线程初始化时地址空间的大小
LPTHREAD_START_ROUTINE lpStartAddress,
//指定该线程的线程函数的地址
LPVOID lpParameter,
//将要传递给新建线程的命令行参数
DWORD dwCreationFlags,
//指定新建线程创建后是否立即执行
LPDWORD lpThreadId );
//表示新建线程的ID号
- 执行:
通过命令行即可实现交叉编译:/usr/local/toolchain/toolchain4.3.2/bin/arm-none-linux-gnuenbi-gcc -c dateserveri.c
/usr/local/toolchain/toolchain4.3.2/bin/arm-none-linux-gnuenbi-gcc -static -o dateserveri dateserveri.o csapp.a -lpthread