2017-2018-1 20155222 201552228 实验三-并发程序
2017-2018-1 20155222 201552228 实验三-并发程序
实验内容
实验三-并发程序-1
学习使用Linux命令wc(1)
基于Linux Socket程序设计实现wc(1)服务器(端口号是你学号的后6位)和客户端
客户端传一个文本文件给服务器
服务器返加文本文件中的单词数
上方提交代码
附件提交测试截图,至少要测试附件中的两个文件
实验三-并发程序-2
使用多线程实现wc服务器并使用同步互斥机制保证计数正确
上方提交代码
下方提交测试
对比单线程版本的性能,并分析原因
实验三-并发程序-3
交叉编译多线程版本服务器并部署到实验箱中
PC机作客户端测试wc服务器
提交测试截图
实验要求
-
提交实验报告博客,一组写一篇,实验中贡献小的写博客,贡献多的可以给出同组同学的博客链接。
-
博客标题:2017-2018-1 学号1 学号2 实验三 实时系统
-
实验目的,实验步骤
-
实验中的问题及解决过程
-
新学到的知识点
实验步骤
Linux系统中的wc(Word Count)命令
功能为统计指定文件中的字节数、字数、行数,并将统计结果显示输出。
- 命令格式:
wc [选项]文件...
-
命令功能:统计指定文件中的字节数、字数、行数,并将统计结果显示输出。该命令统计指定文件中的字节数、字数、行数。如果没有给出文件名,则从标准输入读取。wc同时也给出所指定文件的总统计数。
-
命令参数
-
-c 统计字节数。
-
-l 统计行数。
-
-m 统计字符数。这个标志不能与 -c 标志一起使用。
-
-w 统计字数。一个字被定义为由空白、跳格或换行字符分隔的字符串。
-
-L 打印最长行的长度。
-
-help 显示帮助信息
-
--version 显示版本信息
字数统计函数
int getnumberofwords(char readfile[63335])
//函数返回readfile字符串中的字数
{
int i,n=0,flag=1;
for(i = 0; readfile[i]; i ++)//字符串不为空就继续循环
{
if(flag == 1)//如果上个字符不是空格
{
if(readfile[i] != ' ')//如果这个字符为空格
{
n++;//字数统计加1
flag = 0;
}
}
else if(readfile[i] == ' ')
//如果上个字符是空格而且这个字符也是空格
flag = 1;
}
return n;//返回字符个数
}
文件读写函数
void readfile (char *readfile,char address[1024])
//将address的内容作为地址打开文件读取到readfile中
{
FILE *fp;
char ch;
int i,count;
if((fp=fopen(address,"rb"))==NULL)//文件打开
{
printf("failed to open file!\n");//文件打开失败
exit(0);
}
count=0;
while((ch=fgetc(fp))!=EOF && count<1024)//读取文件内容
{
if(isprint(ch))
{
readfile[count]=ch;
count++;
}
}
fclose(fp);//关闭文件
return 0;
}
Socket编程基本框架(服务器)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Winsock2.h>
#define MY_PORT 3434
int main()
{
char *buffer1="connect to server successful!";
char buffer2[1024]="no client connected to server!";
char buffer3[1024]="\0";
char buffer4[1024]="\0";
int bytes_recvd;
int m,n;
m=1;
//打开服务
WSADATA wsaData;
WSAStartup(MAKEWORD(1,1),&wsaData);
//初始化
struct sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MY_PORT);//htons:将主机的无符号短整型数字节顺序转换成网络字节顺序
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//htonl:将主机的无符合长整型数字节顺序转换成网络字节顺序。
//指定协议
SOCKET listen_sock, new_sock;
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
//根据指定的地址族、数据类型和协议来生成一个套接字的描述字(listen_sock )
//地址描述:AP_INET,指定socket类型:SOCK_STREAM,函数返回值为整型socket描述符。
bind(listen_sock, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));//将一个套接字和一个本地地址与绑定在一起
//listen_sock:socket描述符,(struct sockaddr *)&my_addr:指向sockaddr类型的指针
listen(listen_sock, 5);
//5:在请求队列中允许的最大请求数为5
//定义监听套接字
new_sock = accept(listen_sock, NULL, NULL);
//listen_sock:被监听的socket描述符
//发送数据
send(new_sock, buffer1, strlen(buffer1), 0);
//new_sock:用于传输数据的socket描述符,buffer:是一个指向要发送数据的指针,strlen(buffer):以字节为单位的数据的长度。
//文件接收
bytes_recvd = recv(new_sock, buffer2, sizeof(buffer2), 0);
printf("Server received message(%d bytes): %s\n", bytes_recvd, buffer2);
memset(buffer3, 0, sizeof(buffer3));
bytes_recvd = recv(new_sock, buffer3, sizeof(buffer3), 0);
printf("client:%s\n",buffer3);
memset(buffer4, 0, sizeof(buffer4));
printf("server:");
gets(buffer4);
send(new_sock, buffer4, strlen(buffer4), 0);
printf("server disconnected to client successful!");
//关闭套接字
closesocket(new_sock);
closesocket(listen_sock);
WSACleanup();
while(1)
{
}
return 0;
}
服务器和客户端代码
//服务器
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MY_PORT 155228
int readfilewords(char readfile[65536]);
int getnumberofwords(char readfile[63335]);
void main()
{
char *buffer1="connect to server successful!";
char buffer2[1024]="no client connected to server!";
char buffer3[65536]="\0";
char buffer4[1024]="\0";
int bytes_recvd;
int m,n;
//WSADATA wsaData;
//WSAStartup(MAKEWORD(1,1),&wsaData);
struct sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MY_PORT);//htons:将主机的无符号短整型数字节顺序转换成网络字节顺序
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//htonl:将主机的无符合长整型数字节顺序转换成网络字节顺序。
int listen_sock, new_sock;
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
//根据指定的地址族、数据类型和协议来生成一个套接字的描述字(listen_sock )
//地址描述:AP_INET,指定socket类型:SOCK_STREAM,函数返回值为整型socket描述符。
bind(listen_sock, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));//将一个套接字和一个本地地址与绑定在一起
//listen_sock:socket描述符,(struct sockaddr *)&my_addr:指向sockaddr类型的指针
listen(listen_sock, 5);
//5:在请求队列中允许的最大请求数为5
while(1)
{
new_sock = accept(listen_sock, NULL, NULL);
//listen_sock:被监听的socket描述符
send(new_sock, buffer1, strlen(buffer1), 0);
//new_sock:用于传输数据的socket描述符,buffer:是一个指向要发送数据的指针,strlen(buffer):以字节为单位的数据的长度。
bytes_recvd = recv(new_sock, buffer2, sizeof(buffer2), 0);
printf("Server received message(%d bytes): %s\n", bytes_recvd, buffer2);
memset(buffer3, 0, sizeof(buffer3));
bytes_recvd = recv(new_sock, buffer3, sizeof(buffer3), 0);
m=getnumberofwords(buffer3);
memset(buffer4, 0, sizeof(buffer4));
sprintf(buffer4,"%d",m);
send(new_sock, buffer4, strlen(buffer4), 0);
}
close(new_sock);
close(listen_sock);
//WSACleanup();
}
int getnumberofwords(char readfile[65536])
{
int i,n=0,flag=1;
for(i = 0; readfile[i]; i ++)
{
if(flag == 1)
{
if(readfile[i] != ' ')
{
n++;
flag = 0;
}
}
else if(readfile[i] == ' ')
flag = 1;
}
return n;
}
//客户端
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#define MY_PORT 155228
void readfile (char readfile[65536]);
int main() {
char buffer1[1024]="connect to server failed!";
char *buffer2;
char buffer3[65536]="\0";
char buffer4[1024]="\0";
int n;
int bytes_recvd;
//WSADATA wsaData;
//WSAStartup(MAKEWORD(1,1),&wsaData);
struct sockaddr_in remote_addr;
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(MY_PORT);
remote_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int conn_sock,new_sock;
conn_sock = socket(AF_INET, SOCK_STREAM, 0);
connect(conn_sock, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr));
bytes_recvd = recv(conn_sock, buffer1, sizeof(buffer1), 0);
printf("Client received message(%d bytes): %s\n", bytes_recvd, buffer1);
buffer2="client connected to server successful!";
send(conn_sock, buffer2, strlen(buffer2), 0);
//conn_sock:用于传输数据的socket描述符,buffer:是一个指向要发送数据的指针,strlen(buffer):以字节为单位的数据的长度。
memset(buffer3, 0, sizeof(buffer3));
readfile(buffer3);
//printf("client:");
//gets(buffer3);
send(conn_sock, buffer3, strlen(buffer3), 0);
memset(buffer4, 0, sizeof(buffer4));
bytes_recvd = recv(conn_sock, buffer4, sizeof(buffer4), 0);
printf("number of words:%s\n", buffer4);
close(new_sock);
close(conn_sock);
//WSACleanup();
return 0;
}
void readfile (char readfile[65536])
{
FILE *fp;
char ch;
int i,count;
if((fp=fopen("/home/besti20155228/20155228代码备份/1114exp3/test1.txt","rb"))==NULL)
{
printf("failed to open file!\n");
exit(0);
}
count=0;
while((ch=fgetc(fp))!=EOF && count<1024)
{
if(isprint(ch))
{
readfile[count]=ch;
count++;
}
}
fclose(fp);
return 0;
}
运行结果截图
多线程同步互斥机制
锁
- 在主线程中初始化锁为解锁状态
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
- 在编译时初始化锁为解锁状态
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//锁初始化
- 访问对象时的加锁操作与解锁操作
pthread_mutex_lock(&mutex)//加锁
pthread_mutex_unlock(&mutex)//释放锁
锁保护的并不是共享变量(或者说是共享内存),对于共享的内存而言,用户是无法直接对其保护的,因为那是物理内存,无法阻止其他程序的代码访问。事实上,锁之所以对关键区域进行了保护,是因为所有线程都遵循了一个规则,那就是在进入关键区域钱加同一把锁,在退出关键区域钱释放同一把锁
加锁是会带来额外的开销的,加锁的代码其运行速度,明显比不加锁的要慢一些,所以,在使用锁的时候,要合理,在不需要对关键区域进行保护的场景下,不要画蛇添足,为其加锁了
信号量
锁有一个很明显的缺点,那就是它只有两种状态:锁定与不锁定。
信号量本质上是一个非负数的整数计数器,它也被用来控制对公共资源的访问。当公共资源增加的时候,调用信号量增加函数sem_post()
对其进行增加,当公共资源减少的时候,调用函数sem_wait()来减少信号量。其实是可以把锁当作一个0-1信号量的。
它们是在/usr/include/semaphore.h中进行定义的,信号量的数据结构为sem_t, 本质上,它是一个long型整数
在使用semaphore之前,我们需要先引入头文件#include <semaphore.h>
初始化信号量: int sem_init(sem_t *sem, int pshared, unsigned int value);
成功返回0,失败返回-1
参数
- sem:指向信号量结构的一个指针
- pshared: 不是0的时候,该信号量在进程间共享,否则只能为当前进程的所有线程们共享
- value:信号量的初始值
- 信号量减1操作,当sem=0的时候该函数会堵塞
int sem_wait(sem_t *sem);
成功返回0,失败返回-1
参数
- sem:指向信号量的一个指针
信号量加1操作int sem_post(sem_t *sem);
参数与返回同上
销毁信号量int sem_destroy(sem_t *sem);
参数与返回同上
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define MAXSIZE 10
int stack[MAXSIZE];
int size = 0;
sem_t sem;
// 生产者
void provide_data(void) {
int i;
for (i=0; i< MAXSIZE; i++) {
stack[i] = i;
sem_post(&sem); //为信号量加1
}
}
// 消费者
void handle_data(void) {
int i;
while((i = size++) < MAXSIZE) {
sem_wait(&sem);
printf("乘法: %d X %d = %d\n", stack[i], stack[i], stack[i]*stack[i]);
sleep(1);
}
}
int main(void) {
pthread_t provider, handler;
sem_init(&sem, 0, 0); //信号量初始化
pthread_create(&provider, NULL, (void *)handle_data, NULL);
pthread_create(&handler, NULL, (void *)provide_data, NULL);
pthread_join(provider, NULL);
pthread_join(handler, NULL);
sem_destroy(&sem); //销毁信号量
return 0;
}
多线程服务器代码
#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 5228
#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);
}
运行结果截图
参考资料
linux中wc命令用法
C语言统计单词个数
Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)