Linux综合应用

c语言编程模拟一个有应用背景的客户端、服务器交互类应用,包含文件读写功能。

一、实验内容

1.开发情况介绍

背景及需求说明

图书馆中有名称不同的图书,每种图书的数量有限,学生可以向借阅书籍,且每个学生借阅图书的数量有限。图书馆和学生分别作为系统的服务器和客户,学生端通过向图书馆服务器发送自己的姓名和书籍的方式来借阅书籍。同时可以进行书籍的归还和图书馆剩余书籍信息的查看。

主要功能

本人设计的项目为图书馆图书借阅简易系统,主要实现的功能如下:
图书馆菜单的显示,学生借书、还书,查看图书信息,借书记录的录入以及对各种不同情况的分析和处理。

文件组成

图书馆服务器程序:server.c(代码行数)
学生客户端程序:client.c (代码行数)
书籍信息记录的文本文件:BookInfo.txt
编译后的可执行文件

2.程序演示

起始图书馆书籍信息

(1)菜单显示

在文件目录下先后在两个终端窗口运行serverclient程序,当客户端连接上服务器后会显示图书馆菜单界面,输入序号会执行相关操作

(2)借书

在客户端终端输入序号1执行借书操作
没有借阅记录的学生会录入学生的姓名,返回菜单界面:

书名不存在会重新返回菜单界面且不录入名字:

之前有借阅记录会查询该学生的当前借阅书籍的数量,数量限制为5本

超过5本会有提醒

当要借的书籍在图书馆没有剩余时会提示书籍目前不可借

(3)还书

输入序号2可以执行还书操作,输姓名后程序会查询是否有你的借书记录
查询到借书记录后则会提示你输入所借阅书籍的书名

输入书名错误或没有借阅该书会提示重新输入,知道输入自己已经借阅书籍的正确名字为止

没有借书记录的会返回菜单界面

(4)图书情况显示

输入序号3会客户端将当前图书馆剩余书籍情况更新记录在同目录下的文本文BookInfo.txt中,客户端会从该文件中读取更新后的信息,并打印在服务器命令窗口。
执行该操作后服务器会提示信息已经更新

打开BookInfo.txt文件可以看到更新后的书籍信息

客户端也可以看到书籍信息

(5)退出系统
在客户端输入序号4即可退出系统

(6)提示及注意事项

  • 因为可能存在端口占用和网络连接问题,多以可能需要试着多运行几次,试着在客户端键入ctrl+c,或者等一段时间在运行程序,直至与客户端,出现菜单界面。(因为自己的计算机上偶尔会出现这样的问题)
  • 如果客户端成功连接服务器,在程序运行过程中,请不要随意键入ctrl+c,否则服务器会出现死循环。(目前还没有解决该问题)
  • 输入姓名和书名时请不要输入空格

3.实验结果总结

本次实验实验总的来说基本上完成预期的效果,但是也存在很多问题。
优点
大致上能够完成学生向图书馆借书、还书的过程的模拟,实现的界面简单,易懂
缺点
程序在终端上运行,没有美观的界面。
就如上面注意事项中所提到的,程序可能存在端口占用和网络连接问题,所以就会存在客户端连接不上服务器的问题。
在程序运行过程中如果在客户端ctrl+c,服务器就会陷入死循环。
学生信息的录入不全面,如果优化的话,可以考虑将学号加入一起作为识别信息。
书籍的数量、信息等不够全面,可以加入更多的书籍和更详细的信息。
实现的相关功能不全面,只有简单的借书,还书操作。

三、编程工作总结

本次实验需要设想一个客户端和服务器的交互类应用场景,在思考应用背景的时候我就很犹豫,害怕应用的交互功能过于复杂,害怕代码量过大,怕编写到一半发现根本实现不了而放弃,思考很久才最后确定图书管理系统这个应用,并且系统的设定和最后要实现想功能在编写过程中也进行很多修改和简化。所以我意识到系统前期的设计方面一定要进行全面分析,包括自身能力的评估,时间的限制,以及各模块的相互调用和交互的实现也需要考虑在内。
之前对于文件的IO操作还不是很熟悉,这次也了解了fopenfclosefreadfwrite等函数的参数构成,基本功能的实现,加深了对于文件读写功能的认识。
本来想将服务器和客户端建立TCP连接的过程,服务器包括套接字设置、端口绑定、设置监听、接收客户端连接,客户端包括设置套接字、绑定端口发送连接请求等分别封装成两端的函数connection(具体可以在附录中查看代码或截图,connection()函数代码我还保留着)。但调用时客户端总是过了一分钟左右后有反应,在客户端connect()函数出出错,显示连接时间超时,查看很多方法修改试了很多次,都没有成功。所以暂时将这些过程和函数写在main函数中偏偏就能连接成功,实在不知道为什么,所以现在只能写在主函数了(可能现在用connection()函数可以成功连接了,但是我没有试),降低了程序结构性。
虽然最后的程序编写出来依旧看上去很简陋,但是这也是自己第一次完成代码量稍大的项目对于自己来说也是一定程度的锻炼吧,但其实自己还是很期待完整的完成一次团队项目

代码

服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>

#define BOOKSIZE 2048
#define STUSIZE 1024
char buf[1024]; //接收缓存区
char sendbuf[1024]; //发送缓存区
int BookIndex=0; //用于书本数组的下标
int StuIndex=0; //用于学生数组的下标
struct Book
{
char book_name[32];
char book_author[32];
int book_count; //书籍数量
};
typedef struct Book book;

struct Student
{
char name[32];
char *stu_book[5]; //每个学生的个人借书名单
int count;
};
typedef struct Student stu;
book *BookList[BOOKSIZE] = {0}; //结构体指针数组,存放各个结构体的首地址。
stu *StuList[STUSIZE] = {0}; //结构体指针数组

void InitBook() //初始化5本书
{
BookList[BookIndex]=(book *)malloc(sizeof(book) * 1);
if (NULL==BookList[BookIndex])
{
printf("malloc failure!\n");
}
strcpy(BookList[BookIndex]->book_name,"book1");
strcpy(BookList[BookIndex]->book_author,"Jack");
BookList[BookIndex]->book_count=5;
BookIndex++;

BookList[BookIndex]=(book *)malloc(sizeof(book) * 1);
if (NULL==BookList[BookIndex])
{
printf("malloc failure!\n");
}
strcpy(BookList[BookIndex]->book_name,"book2");
strcpy(BookList[BookIndex]->book_author,"Tom");
BookList[BookIndex]->book_count=3;
BookIndex++;

BookList[BookIndex]=(book *)malloc(sizeof(book) * 1);
if (NULL==BookList[BookIndex])
{
printf("malloc failure!\n");
}
strcpy(BookList[BookIndex]->book_name,"book3");
strcpy(BookList[BookIndex]->book_author,"Jerry");
BookList[BookIndex]->book_count=2;
BookIndex++;

BookList[BookIndex]=(book *)malloc(sizeof(book) * 1);
if (NULL==BookList[BookIndex])
{
printf("malloc failure!\n");
}
strcpy(BookList[BookIndex]->book_name,"book4");
strcpy(BookList[BookIndex]->book_author,"Herbert");
BookList[BookIndex]->book_count=10;
BookIndex++;

BookList[BookIndex]=(book *)malloc(sizeof(book) * 1);
if (NULL==BookList[BookIndex])
{
printf("malloc failure!\n");
}
strcpy(BookList[BookIndex]->book_name,"book4");
strcpy(BookList[BookIndex]->book_author,"John");
BookList[BookIndex]->book_count=7;
BookIndex++;
}

int connection()
{
int flag=1; //连接成功的标志
int s_fd,c_fd; //服务器和客户套接字标识符
int s_len,c_len; //消息长度
//服务器和客户套接字地址
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
s_len=sizeof(s_addr);
c_len=sizeof(c_addr);
//创建套接字
if( (s_fd = socket(AF_INET,SOCK_STREAM,0) )< 0)
{
perror("socket error\n");
flag = -1;
}
s_addr.sin_family=AF_INET; //设置服务器套接字地址的地址域
s_addr.sin_port=htons(8077); //设置服务器套接字端口
s_addr.sin_addr.s_addr=INADDR_ANY; //设置服务器套接字地址
//将套接字与端口绑定
if( bind(s_fd,(struct sockaddr*)&s_addr,sizeof(s_addr)) < 0)
{
perror("bind error\n");
flag = -1;
}
//设置监听
if( listen(s_fd,5) < 0 )
{
perror("listen error\n");
flag = -1;
}
printf("-----\n");
//接收客户端连接
if( (c_fd = accept(s_fd,(struct sockaddr*)&c_addr,(socklen_t *__restrict)&c_len)) < 0 )
{
perror("accept error\n");
flag = -1;
}
if( flag < 0 ) c_fd=-1;
return c_fd;
}

//显示菜单界面
void showmenu()
{
int i;
char *menu[] = {"1.借书\t\t2.还书\n"
"3.图书情况\t4.退出系统\n"};
for (i=0;i<sizeof(menu)/sizeof(menu[0]);i++) printf("%s\n",menu[i]);
}

//服务器借书函数
void BorrowBook(int fd)
{
char stu_name[32] = {0}; //学生姓名
char book_name[32] = {0}; //书名

read(fd,stu_name,sizeof(stu_name));
int i, j;
//搜索学生名单,判断该学生是否借过书
for (i = 0; i < StuIndex; i++)
{
if (strcmp(StuList[i]->name,stu_name)==0) //该学生之前借过书
{
if (StuList[i]->count>=5) //一个人最多借5本
{
strcpy(sendbuf,"可借数目已经达到上限,无法借出\n\n");
write(fd,sendbuf,sizeof(sendbuf));
return;
}
else //借过书,且数量没有超过5本
{
strcpy(sendbuf,"您之前借过书,请输入书名\n");
write(fd,sendbuf,sizeof(sendbuf));
read(fd,book_name,sizeof(book_name));
//sleep(2);
for (j=0;j<BookIndex;j++)//判断书是否可借
{
if (strcmp(BookList[j]->book_name,book_name)==0)
{
if (BookList[j]->book_count==0)
{
strcpy(sendbuf,"剩余0本,不可借\n");
write(fd,sendbuf,sizeof(sendbuf));
return;
}
else
{
break;
}
}
if (j==BookIndex-1)
{
strcpy(sendbuf,"书名不存在\n");
write(fd,sendbuf,sizeof(sendbuf));
return;
}
}
BookList[j]->book_count--; //借书成功,库存减少

int k;
//将节约借阅的书籍加入该学生的借书名单中
for(k=0;k<StuList[i]->count+1;k++)
{

if(StuList[i]->stu_book[k]==0)
{
StuList[i]->stu_book[k] = (char *)malloc(sizeof(char) * 1);
strcpy(StuList[i]->stu_book[k],book_name);
}
}
StuList[i]->count++;
strcpy(sendbuf,"借书成功\n");
write(fd,sendbuf,sizeof(sendbuf));
return;
}
}
}


//如果遍历数组结束,没有匹配到,说明没有借过书
strcpy(sendbuf,"您之前没有借过书,请输入书名\n");
write(fd,sendbuf,sizeof(sendbuf));
//sleep(2);
read(fd,book_name,sizeof(book_name));
for(i=0;i<BookIndex;i++) //判断书本是否存在
{
if(strcmp(BookList[i]->book_name,book_name)==0)
{
if(BookList[i]->book_count==0)
{
strcpy(sendbuf,"剩余0本,不可借\n");
write(fd,sendbuf,sizeof(sendbuf));
return;
}
else
{
BookList[i]->book_count--; //减少一本
break;
}
}
if (i==BookIndex-1)
{
strcpy(sendbuf,"书名不存在\n");
write(fd,sendbuf,sizeof(sendbuf));
return;
}
}
//录入借书学生的信息
StuList[StuIndex]=(stu *)malloc(sizeof(stu) * 1);
int count0;
for(count0=0;count0<5;count0++) StuList[StuIndex]->stu_book[count0]=0;
StuList[StuIndex]->stu_book[0]=(char *)malloc(sizeof(char) * 1);
StuList[StuIndex]->count=0;
strcpy(StuList[StuIndex]->name,stu_name);
strcpy(StuList[StuIndex]->stu_book[0],book_name);
StuList[StuIndex]->count++;

StuIndex++;
strcpy(sendbuf,"借书成功\n");
write(fd,sendbuf,sizeof(sendbuf));
}

//服务器还书函数
void ReturnBook(int fd)
{
char stu_name[32]; //学生姓名
char book_name[32]; //书名
read(fd,stu_name,sizeof(stu_name));
int i,j,k;
//搜索借书记录
for(i=0;i<StuIndex;i++)
{
if(strcmp(StuList[i]->name,stu_name)==0&&StuList[i]->count!=0) //有该学生的借书记录
{
strcpy(sendbuf,"请输入书名:");
write(fd,sendbuf,sizeof(sendbuf));
fun:
if(read(fd,book_name,sizeof(book_name))<0)
{
perror("read error\n");
exit(1);
}
for(j=0;j<BookIndex;j++) //判断书本是否存在
{
if(strcmp(BookList[j]->book_name,book_name)==0)
{
for(k=0;k<StuList[i]->count;k++)
{
if(strcmp(StuList[i]->stu_book[k],book_name)==0)
{
StuList[i]->stu_book[k]=0;
free(StuList[i]->stu_book[k]);
break;
}
else
{
strcpy(sendbuf,"书名输入错误,请重新输入:");
write(fd,sendbuf,sizeof(sendbuf));
goto fun;
}
}
//还书成功
StuList[i]->count--;
BookList[j]->book_count++;
strcpy(sendbuf,"还书成功\n\n");
write(fd,sendbuf,sizeof(sendbuf));
return;
}
//输入书名错误
if(j==BookIndex-1 )
{
strcpy(sendbuf,"书名输入错误,请重新输入:");
write(fd,sendbuf,sizeof(sendbuf));
goto fun;
}
}
}
break;
}
//没有该学生的借书记录
if(i==StuIndex-1||StuIndex==0)
{
strcpy(sendbuf,"没有你的借书记录\n\n");
write(fd,sendbuf,sizeof(sendbuf));
}
printf("\n");
}

//将书籍信息写入BookInfo.txt文件中
void ShowBook()
{
char *book_info[] = {"书名","作者","剩余本数"};
int i;
FILE *fp;
fp = fopen("BookInfo.txt","w+");
fprintf(fp,"%s\t\t%s\t\t%s\n",book_info[0],book_info[1],book_info[2]);
for(i=0;i<BookIndex;i++)
{
fprintf(fp,"%s\t\t%s\t\t%d\n",BookList[i]->book_name,BookList[i]->book_author,BookList[i]->book_count);
}
fclose(fp);
printf("图书信息已写入BookInfo.txt文件\n\n");
}

int main()
{
InitBook();
//int fd = connection();

int s_fd,c_fd; //服务器和客户套接字标识符
int s_len,c_len; //消息长度
//服务器和客户套接字地址
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
s_len=sizeof(s_addr);
c_len=sizeof(c_addr);
//创建套接字
if( (s_fd = socket(AF_INET,SOCK_STREAM,0) )< 0)
{
perror("socket error\n");
exit(1);
}
s_addr.sin_family=AF_INET; //设置服务器套接字地址的地址域
s_addr.sin_port=htons(8077); //设置服务器套接字端口8077
s_addr.sin_addr.s_addr=INADDR_ANY; //设置服务器套接字地址
//将套接字与端口绑定
if( bind(s_fd,(struct sockaddr*)&s_addr,sizeof(s_addr)) < 0)
{
perror("bind error\n");
exit(1);
}
//设置监听
if( listen(s_fd,10) < 0 )
{
perror("listen error\n");
exit(1);
}
//接收客户端连接
if( (c_fd = accept(s_fd,(struct sockaddr*)&c_addr,(socklen_t *__restrict)&c_len)) < 0 )
{
perror("accept error\n");
exit(1);
}
char buf[1024];
char sendbuf[1024]="已接收到消息"; //服务器收到消息后的返回信息
while (1)
{
showmenu();
//接收消息
if( (read(c_fd,buf,sizeof(buf))) < 0)
{
perror("read error\n");
exit(1);
}
int choice = strlen(buf)==1 ? buf[0] - '0' : 0;
if(choice<=4&&choice>=1) printf("收到的请求序号:%d\n",choice);
else printf("错误请求\n");
switch(choice)
{
case 1: //借书
BorrowBook(c_fd);
break;
case 2: //还书
ReturnBook(c_fd);
break;
case 3: //图书情况
ShowBook();
break;
case 4: //退出系统
exit(0);
break;
}
}
close(c_fd);
close(s_fd);
return 0;
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
char sendbuf[1024]; //发送缓存区
char buf[1024]; //接收缓存区

int connection()
{
int fd,newfd;
struct sockaddr_in addr; //客户端套接字地址
//创建套接字
if( (fd=socket(AF_INET,SOCK_STREAM,0)) < 0 )
{
perror("socket error\n");
exit(1);
}
addr.sin_family=AF_INET; //设置客户端套接地址的地址域
addr.sin_port=htons(8077); //设置客户端套接字端口8077
//发送连接请求
if( ( newfd=connect(fd,(struct sockaddr*)&addr,sizeof(addr)) ) < 0 )
{
perror("connect error\n");
exit(1);
}

return fd;
}

//显示菜单界面
void showmenu()
{
int i;
char *menu[] = {"1.借书\t\t2.还书\n"
"3.图书情况\t4.退出系统\n"};
for (i=0;i<sizeof(menu)/sizeof(menu[0]);i++) printf("%s\n",menu[i]);
}

//客户借书函数
void BorrowBook(int fd)
{
char stu_name[32] = {0};
char book_name[32] = {0};
printf("请输入姓名:\n");
scanf("%s", stu_name);
write(fd,stu_name,sizeof(stu_name));
//sleep(1);
read(fd,buf,sizeof(buf));
printf("%s",buf);
if( strcmp(buf,"可借数目已经达到上限,无法借出\n\n") == 0) return;
else
{
scanf("%s",book_name);
write(fd,book_name,sizeof(book_name));
//sleep(1);
read(fd,buf,sizeof(buf));
printf("%s",buf);
}
printf("\n");
}

//客户借书函数
void ReturnBook(int fd)
{
char stu_name[32] = {0};
char book_name[32] = {0};
printf("请输入姓名:");
scanf("%s",stu_name);
write(fd,stu_name,sizeof(stu_name));
read(fd,buf,sizeof(buf));
printf("%s",buf);
if(strcmp(buf,"没有你的借书记录\n\n") == 0) return;
else
{
scanf("%s",book_name);
write(fd,book_name,sizeof(book_name));
while(1)
{
if(read(fd,buf,sizeof(buf))<0)
{
perror("read error\n");
exit(1);
}
printf("%s",buf);
if(strcmp(buf,"还书成功\n\n") == 0) break;
scanf("%s",book_name);
write(fd,book_name,sizeof(book_name));
}
}
}

//打印BookInfo.txt文件里的书籍信息
void ShowBook(int fd)
{
sleep(1);
FILE *fp;
fp = fopen("BookInfo.txt","rb");
char buf[2048]={0};
fread(buf,sizeof(buf),1,fp);
printf("%s\n",buf);
}

int main()
{
//int fd = connection();
int fd,newfd;
struct sockaddr_in addr; //客户端套接字地址
//创建套接字
if( (fd=socket(AF_INET,SOCK_STREAM,0)) < 0 )
{
perror("socket error\n");
exit(1);
}
addr.sin_family=AF_INET; //设置客户端套接地址的地址域
addr.sin_port=htons(8077); //设置客户端套接字端口8077
//发送连接请求
if( ( newfd=connect(fd,(struct sockaddr*)&addr,sizeof(addr)) ) < 0 )
{
perror("connect error\n");
exit(1);
}
char buf[1024];
char sendbuf[1024]; //服务器收到消息后的返回信息
while(1)
{
showmenu();
printf("请输入相应的操作序号:");
scanf("%s",sendbuf);
send(fd,sendbuf,sizeof(sendbuf),0); //发送选择信息
int choice = strlen(sendbuf)==1 ? sendbuf[0] - '0' : 0;
switch(choice)
{
case 1: //借书
BorrowBook(fd);
break;
case 2: //还书
ReturnBook(fd);
break;
case 3: //图书情况
ShowBook(fd);
break;
case 4: //退出系统
exit(1);
break;
default://输入错误
printf("输入序号错误\n\n");
break;
}
}
close(fd);
}