实验三 并发程序
任务一
实验内容和要求
- 学习使用Linux命令wc(1)
- 基于Linux Socket程序设计实现wc(1)服务器(端口号是你学号的后6位)和客户端
- 客户端传一个文本文件给服务器
- 服务器返加文本文件中的单词数
实验步骤
-
1.在命令行中使用命令
man 1 wc
查找wc(1)
-
2.命令格式:
wc [选项]文件...
-
3.命令功能:
统计指定文件中的字节数、字数、行数,并将统计结果显示输出。该命令统计指定文件中的字节数、字数、行数。如果没有给出文件名,则从标准输入读取。wc同时也给出所指定文件的总统计数。 -
4.命令参数:
-c 统计字节数。
-l 统计行数。
-m 统计字符数。这个标志不能与 -c 标志一起使用。
-w 统计字数。一个字被定义为由空白、跳格或换行字符分隔的字符串。
-L 打印最长行的长度。
-help 显示帮助信息
-version 显示版本信息
wc -w统计单词数:
wc -w 与我们平时统计单词数的方法有点不同,wc -w命令实现时:由' ',' ',' ',' '作为分隔符,而实际上我们统计单词的分隔符还有'!'等等,因此针对两种不同的分隔符实现两种计算方法 -
5.编写代码
客户端
/*client*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#define MYPORT 175230
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);
}
服务器端
/*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 175230
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);
}
- 6.实验结果
任务二
实验内容与要求
- 使用多线程实现wc服务器并使用同步互斥机制保证计数正确
- 对比单线程版本的性能,并分析原因
实验步骤
- 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
#define HELLO_WORLD_SERVER_PORT 175230
#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
", 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!
");
exit(1);
}
// 把客户端的socket和客户端的socket地址结构绑定
if (bind(client_socket, (struct sockaddr *) &client_addr, sizeof(client_addr))) {
printf("Client Bind Port Failed!
");
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!
");
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!
", ipaddr);
exit(1);
}
char file_name[FILE_NAME_MAX_SIZE + 1];
bzero(file_name, sizeof(file_name));
printf("Please Input File Name. ");
scanf("%s", file_name);
if ((fp = fopen(file_name, "r")) == NULL) {
printf("Failure to open %s
", 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("发送文件名失败
");
}
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("发送文件失败
");
}
bzero(buffer, sizeof(buffer));
i = 0;
}
}
if (i < BUFFER_SIZE) {
if ((send(client_socket, buffer, i, 0)) == -1) {
printf("发送文件失败
");
}
}
printf("发送%s完毕
", 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
", file_name);
exit(0);
}
while ((ch = fgetc(fp)) != EOF) {
if (ch == ' ' || ch == '
' || ch == ' ' || ch == '
')
flag = 0;
else {
if (flag == 0) {
flag = 1;
num++;
}
}
}
printf("单词个数为:%d
", num);
fclose(fp);
return num;
}
服务器
#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 175230
#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”:
");
if ((fp = fopen(file_name, "r")) == NULL) {
printf("Failure to open %s
", file_name);
exit(0);
}
while ((ch = fgetc(fp)) != EOF) {
if (ch == ' ' || ch == '
' || ch == ' ' || ch == '
')
flag = 0;
else {
if (flag == 0) {
flag = 1;
num++;
}
}
}
printf("单词个数为:%d
", 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!
");
exit(1);
}
// 把socket和socket地址结构绑定
if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr))) {
printf("Server Bind Port: %d Failed!
", HELLO_WORLD_SERVER_PORT);
exit(1);
}
// server_socket用于监听
if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE)) {
printf("Server Listen Failed!
");
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("连接到客户端
");
if (new_server_socket < 0) {
printf("Server Accept Failed!
");
}
//添加进程相关代码
pthread_t pid;
if (pthread_create(&pid, NULL, process_client, (void *) &new_server_socket) < 0) {
printf("pthread_create error
");
}
}
// 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失败
", buffer);
}
strcpy(file_name, buffer);
strcat(file_name, "-server");
if ((fp = fopen(file_name, "w")) == NULL) {
printf("Failure to open %s
", file_name);
exit(0);
}
while (length = recv(sockid, buffer, BUFFER_SIZE, 0)) {
if (length < 0) {
printf("接受文件出错
");
exit(0);
}
if (fwrite(buffer, sizeof(char), length, fp) < length) {
printf("写文件失败
");
}
bzero(buffer, BUFFER_SIZE);
}
fclose(fp);
printf("接受文件完毕
");
int number = 0;
number = mywc(file_name);
bzero(buffer, BUFFER_SIZE);
buffer[0] = number + 48;
bzero(buffer, sizeof(buffer));
printf("File Transfer Finished!
");
close(new_server_socket);
}
实验结果
任务三
实验内容与要求
- 交叉编译多线程版本服务器并部署到实验箱中
- C机作客户端测试wc服务器
- 提交测试截图
实验步骤
-1.实验箱与电脑互相ping通
实验结果
未能得出实验结果
实验中遇到的问题
-
1.问题一
在统计完单词数之后服务器端没有停止进程,继续监听,等待从客户端发来的信号,无法停止
解决方法:强行使用Ctrl+C强制结束进程 -
2.问题1:
Fatal error: stdafx.h : No such file or directory
问题1解决方案:建工程自带,将其删除即可;或者是手动添加头文件