2019-2020-1 20175223 20175232 20175233 实验三 实时系统
实验三 并发程序
小组成员
20175223 姚明宇
20175232 司浩楠
20175233 严顺尧
实验三-并发程序-1
- 学习使用Linux命令wc(1)
- 基于Linux Socket程序设计实现wc(1)服务器(端口号是你学号的后6位)和客户端
- 客户端传一个文本文件给服务器
- 服务器返加文本文件中的单词数
- 学习使用Linux命令wc(1)
Linux系统中的wc(Word Count)命令的功能为统计指定文件中的字节数、字数、行数,并将统计结果显示输出。
命令参数:
-c 统计字节数。
-l 统计行数。
-m 统计字符数。这个标志不能与 -c 标志一起使用。
-w 统计字数。一个字被定义为由空白、跳格或换行字符分隔的字符串。
-L 打印最长行的长度。
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#define MYPORT 175233
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\n", wordscount);
close(clientfd);
}
server.c
/*server*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#define MYPORT 175233
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\n", 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 '\n':
wordscount++;
break;
case '\r':
wordscount++;
break;
default:
break;
}
}
if(buffer[i]== ' ' || buffer[i]=='\n' || buffer[i]=='\r') flag=1;
else flag=0;
}
}
if(len<1024) break;
}
send(clientfd, &wordscount, sizeof(long), 0);
close(clientfd);
}
close(serverfd);
}
运行结果
实验三-并发程序-2
实验步骤
- 使用多线程实现wc服务器并使用同步互斥机制保证计数正确
- 上方提交代码
- 下方提交测试
- 对比单线程版本的性能,并分析原因
client2.c
#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
#define HELLO_WORLD_SERVER_PORT 175233
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
int mywc(char file_name[], int choose);
int main(int argc, char **argv) {
char *ipaddr = "127.0.0.1";
FILE *fp;
if (argc != 1) {
printf("Usage: ./%s ServerIPAddress\n", ipaddr);
exit(1);
}
// 设置一个socket地址结构client_addr, 代表客户机的internet地址和端口
struct sockaddr_in client_addr;
bzero(&client_addr, sizeof(client_addr));
client_addr.sin_family = AF_INET; // internet协议族
client_addr.sin_addr.s_addr = htons(INADDR_ANY); // INADDR_ANY表示自动获取本机地址
client_addr.sin_port = htons(0); // auto allocated, 让系统自动分配一个空闲端口
// 创建用于internet的流协议(TCP)类型socket,用client_socket代表客户端socket
int client_socket = socket(AF_INET, SOCK_STREAM, 0);
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;
// 服务器的IP地址来自程序的参数
if (inet_aton(ipaddr, &server_addr.sin_addr) == 0) {
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", ipaddr);
exit(1);
}
char file_name[FILE_NAME_MAX_SIZE + 1];
bzero(file_name, sizeof(file_name));
printf("Please Input File Name.\t");
scanf("%s", file_name);
if ((fp = fopen(file_name, "r")) == NULL) {
printf("Failure to open %s\n", file_name);
exit(0);
}
char buffer[BUFFER_SIZE];
bzero(buffer, sizeof(buffer));
strcpy(buffer, file_name);
if (send(client_socket, buffer, BUFFER_SIZE, 0) == -1) {
printf("发送文件名失败\n");
}
char ch;
int i = 0;
while ((ch = fgetc(fp)) != EOF) {
buffer[i++] = ch;
if (i >= BUFFER_SIZE) {
if ((send(client_socket, buffer, BUFFER_SIZE, 0)) == -1) {
printf("发送文件失败\n");
}
bzero(buffer, sizeof(buffer));
i = 0;
}
}
if (i < BUFFER_SIZE) {
if ((send(client_socket, buffer, i, 0)) == -1) {
printf("发送文件失败\n");
}
}
printf("发送%s完毕\n", file_name);
mywc(file_name, 2);
// 向服务器发送buffer中的数据,此时buffer中存放的是客户端需要接收的文件
//以下接收服务器发来的单词个数
bzero(buffer, sizeof(buffer));
fclose(fp);
close(client_socket);
return 0;
}
int mywc(char file_name[], int choose) {
FILE *fp;
char ch;
int flag = 0, num = 0;
if ((fp = fopen(file_name, "r")) == NULL) {
printf("Failure to open %s\n", file_name);
exit(0);
}
while ((ch = fgetc(fp)) != EOF) {
if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r')
flag = 0;
else {
if (flag == 0) {
flag = 1;
num++;
}
}
}
printf("单词个数为:%d\n", num);
fclose(fp);
return num;
}
server2.c
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#define HELLO_WORLD_SERVER_PORT 175233
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
void *process_client(void *new_server_socket);
int mywc(char file_name[]) {
char ch;
int flag = 0, num = 0;
FILE *fp;
printf("实现“wc -w”:\n");
if ((fp = fopen(file_name, "r")) == NULL) {
printf("Failure to open %s\n", file_name);
exit(0);
}
while ((ch = fgetc(fp)) != EOF) {
if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r')
flag = 0;
else {
if (flag == 0) {
flag = 1;
num++;
}
}
}
printf("单词个数为:%d\n", num);
fclose(fp);
return num;
}
int main(int argc, char **argv) {
// set socket's address information
// 设置一个socket地址结构server_addr,代表服务器internet的地址和端口
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(HELLO_WORLD_SERVER_PORT);
// create a stream socket
// 创建用于internet的流协议(TCP)socket,用server_socket代表服务器向客户端提供服务的接口
int server_socket = socket(PF_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!\n");
exit(1);
}
// 服务器端一直运行用以持续为客户端提供服务
while (1) {
// 定义客户端的socket地址结构client_addr,当收到来自客户端的请求后,调用accept
// 接受此请求,同时将client端的地址和端口等信息写入client_addr中
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
int new_server_socket = accept(server_socket, (struct sockaddr *) &client_addr, &length);
printf("连接到客户端\n");
if (new_server_socket < 0) {
printf("Server Accept Failed!\n");
}
//添加进程相关代码
pthread_t pid;
if (pthread_create(&pid, NULL, process_client, (void *) &new_server_socket) < 0) {
printf("pthread_create error\n");
}
}
// close(server_socket);
}
void *process_client(void *new_server_socket) {
int sockid = *(int *) new_server_socket;
FILE *fp;
//接受来自客户端的文件
char buffer[BUFFER_SIZE];
char file_name[FILE_NAME_MAX_SIZE];
bzero(buffer, sizeof(buffer));
int length = 0;
if (recv(sockid, buffer, BUFFER_SIZE, 0) == -1) {
printf("接受文件名%s失败\n", buffer);
}
strcpy(file_name, buffer);
strcat(file_name, "-server");
if ((fp = fopen(file_name, "w")) == NULL) {
printf("Failure to open %s\n", file_name);
exit(0);
}
while (length = recv(sockid, buffer, BUFFER_SIZE, 0)) {
if (length < 0) {
printf("接受文件出错\n");
exit(0);
}
if (fwrite(buffer, sizeof(char), length, fp) < length) {
printf("写文件失败\n");
}
bzero(buffer, BUFFER_SIZE);
}
fclose(fp);
printf("接受文件完毕\n");
int number = 0;
number = mywc(file_name);
bzero(buffer, BUFFER_SIZE);
buffer[0] = number + 48;
bzero(buffer, sizeof(buffer));
printf("File Transfer Finished!\n");
close(new_server_socket);
}
分析:
多线程处理可以同时运行多个过程,一定程度上提高响应速度,在多核的情况下还是更能充分利用CPU资源的。
实验三-并发程序-3
- 要求:交叉编译多线程版本服务器并部署到实验箱中
- PC机作客户端测试wc服务器
- 提交测试截图
运行截图
学习到的知识点:
多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O,OEMBIOS等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。每个程序执行时都会产生一个进程,而每一个进程至少要有一个主线程。这个线程其实是进程执行的一条线索,除了主线程外还可以给进程增加其它的线程,也即增加其它的执行线索,由此在某种程度上可以看成是给一个应用程序增加了多任务功能。