停车场系统(C语言网络编程TCP io复用模型)
实验要求
(1)、系统由三部分组成:分别包括:一个服务器,一个入口/出口管理程序,一个收银程序
(2)、服务器用指定的模型,其它部分可以采用任意模型
(3)、服务器功能:
(a) 接收并记录入口/出口管理程序发来的车牌号及记录车辆进入的时间,注意要检查车牌号不能与已经记录重复,如果重复,应通知入口/出口管理程序,不能开卡方行
(b) 计算车辆停留时间及费用,发给收银程序
(c) 通知入口/出口管理程序放行车辆,并删除车牌号
(4)、入口/出口管理程序功能
(a) (1)接收用户输入的车牌号发往服务器,再接收服务器应答消息,并显示是否方向车辆驶入(显示字符“欢迎光临”表示放行车辆驶入)
(b) 接收服务器放行消息,放行车辆(显示字符“谢谢惠顾”表示放行车辆)
(5)、收银程序功能:
(a) 将要离场车辆车牌发到服务器。
(b) 接收服务器发来的停车时间及费用,并显示
(c) 收银成功或失败的消息发往服务器
一、实现思路:
(1)、服务器采用io复用模型,用来接收出口入口程序和收银程序发来的内容,并采用利用缓冲区第一位作为表示来区分程序消息类别
(2)、出口入口程序采用创建多线程的方式,单独创建一个线程来给用户输入数据,主线程来接受服务器发来的消息
(3)、收银程序实现最为简单,利用最为普通的客户端模型即可
二、代码实现
1.服务器
代码如下(示例):
// 服务器.cpp : Defines the entry point for the console application.
//服务器
#include "stdafx.h"
#include <string.h>
#include<time.h>
//定义一个结构体,其中包含车牌号和对应的入库时间
struct Car{
char license[100][20];//存储车牌号
time_t start[100];//存储入库的时间
//int count;//记录车库中车子的数量
};
SOCKET client;
struct Car car;
int count = 0;
void Rukou(SOCKET s,char buf[]){
char data[2] = {0};
int flag = 1;
for(int j=0;j<100;j++){
if(strcmp(buf,car.license[j])==0){
flag = 0;
break;
}//strcmp
}//for
if(flag==1){
int len = strlen(buf);
for(int k=0;k<len;k++){
car.license[count][k] = buf[k];
}
car.start[count] = time(NULL);
count++;
data[0] = '2';
int c = send(s,data,1,0);
if(c<=0){
printf("发送失败!\n");
}
}//if(flag==1)
else{
data[0] = '3';
int b = send(s,data,1,0);
if(b<=0){
printf("发送失败!\n");
}
}//else if(flag==1)
}
//转换函数,将我们的时间和收费转为字符串存储到data数组中去
void trans(int hours,int min,int sec,int money,char *data){
int offset;
sprintf(data,"%d",hours);
offset = strlen(data);
data[offset] = 208;
data[offset+1] = 161;
data[offset+2] = 202;
data[offset+3] = 177;
offset = strlen(data);
sprintf(data+offset,"%d",min);
offset = strlen(data);
data[offset] = 183;
data[offset+1] = 214;
data[offset+2] = 214;
data[offset+3] = 211;
offset = strlen(data);
sprintf(data+offset,"%d",sec);
offset = strlen(data);
data[offset] = 195;
data[offset+1] = 235;
data[offset+2] = '#';
offset = strlen(data);
sprintf(data+offset,"%d",money);
offset = strlen(data);
data[offset] = 212;
data[offset+1] = 170;
data[offset+2] = '#';
}
void Chuku(SOCKET s,char buf[]){
int p;
int bet;
int money = 2;
int hours=0,min=0,sec=0;
char data[100] = {0};
time_t end;
for(int j=0;j<100;j++){
if(strcmp(buf,car.license[j])==0){
p = j;
break;
}//strcmp
}//for
end = time(NULL);
bet = end - car.start[p];
hours = bet / 3600;
bet = bet % 3600;
min = bet /60;
sec = bet % 60;
if(hours){//6块
money = money + 5 * hours;
}
if(min){//半小时3元
money = money + min / 10;
}
//printf("时间为:%d小时%d分钟%d秒\t",hours,min,sec);
trans(hours,min,sec,money,data);
int b = send(s,data,strlen(data),0);
if(b<=0){
printf("发送失败!");
}
else{
aa:
memset(data,0,100);
int c = recv(s,data,100,0);
if(c>0){
if(data[0]=='7'){
printf("%s收银成功\n",buf);
memset(data,0,100);
data[0] = '4';
int d = send(client,data,1,0);
if(d<=0){
printf("发送失败");
}
memset(car.license[p],0,20);
}
else if(data[0]=='8'){
printf("%s收银失败\n",buf);
goto aa;
}
}
}
}
int main(int argc, char* argv[])
{
//加载windows socket DDL
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2,2),&wsaData);
printf("*******************\n");
printf("* *\n");
printf("*停车场管理服务器 *\n");
printf("* *\n");
printf("*******************\n");
if(iResult==0){
printf("服务器启动成功\n");
}
else{
printf("服务器启动失败!\n");
}
//服务器的地址结构
struct sockaddr_in serveraddr;
int len1 = sizeof(serveraddr);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
serveraddr.sin_port = htons(4000);
//创建服务器套接字
SOCKET serversocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(serversocket==-1){
printf("绑定套接字失败!");
WSACleanup();
return 0;
}
//客户端的地址结构
struct sockaddr_in clientaddr;
int len2 = sizeof(serveraddr);
//利用bind绑定服务器套接字
int r;
r = bind(serversocket,(struct sockaddr *)&serveraddr,len1);
if(r!=0){
printf("服务器绑定失败!\n");
}
//利用listen将服务器转为被动监听模式
listen(serversocket,SOMAXCONN);
//缓冲区
char buf[100] = {0};
//car.count = 0;
//初始化i/o复用所用的bianl
fd_set fdRead,fdsocket;
FD_ZERO(&fdRead);
FD_ZERO(&fdsocket);
FD_SET(serversocket,&fdsocket);
//进行i/o复用的操作
while(1){
//利用fdRead来存储触发了时间的套接字
fdRead = fdsocket;
int r;
r = select(0,&fdRead,NULL,NULL,NULL);
//处理事件
if(r>0){
for(int i=0;i<fdsocket.fd_count;i++){
if(FD_ISSET(fdsocket.fd_array[i],&fdRead)){
if(fdsocket.fd_array[i]==serversocket){
SOCKET clientsocket = accept(serversocket,(struct sockaddr *)&clientaddr,&len2);
if(clientsocket==-1){
printf("接受失败!\n");
}
FD_SET(clientsocket,&fdsocket);
}//fdsocket.fd_array[i]==serversocket
else{
int l;
memset(buf,0,100);
l = recv(fdsocket.fd_array[i],buf,100,0);
if(buf[0]=='1'){//出口入口程序发来车牌号
Rukou(fdsocket.fd_array[i],buf+1);
}
else if(buf[0]=='9'){
printf("出口入口程序连接成功\n");
client = fdsocket.fd_array[i];
}
else if(buf[0]=='6'){
printf("收银程序连接成功\n");
}
else if(buf[0]=='5'){
Chuku(fdsocket.fd_array[i],buf+1);
}
}//else:fdsocket.fd_array[i]==serversocket
}//FD_ISSET
}//for
}//r>0
}
closesocket(serversocket);
WSACleanup();
return 0;
}
注意点:这里trans函数用来将停车的时间和收费几种放入数据中存储,最后一起发送
2.出口入口程序
代码如下(示例):
// 出口入口程序.cpp : Defines the entry point for the console application.
//出口入口程序
#include "stdafx.h"
char buf[100] = {0};
DWORD WINAPI Thread(LPVOID p){
printf("输入要进入车库车的车牌号:\n");
SOCKET s = (SOCKET)p;
while(1){
memset(buf,0,100);
buf[0] = '1';
scanf("%s",buf+1);
int l;
l = send(s,buf,strlen(buf+1)+1,0);
if(l<0){
printf("发送失败!\n");
}
else if(l==0){
printf("服务器关闭\n");
}
}
return 0;
}
int main(int argc, char* argv[])
{
//加载windows socket DDL
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2,2),&wsaData);
printf("*******************\n");
printf("* *\n");
printf("* 出口入口程序 *\n");
printf("* *\n");
printf("*******************\n");
if(iResult==0){
printf("出口入口程序启动成功\n");
}
else{
printf("出口入口程序启动失败\n");
return 0;
}
//创建套接字
SOCKET s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(s==-1){
printf("套接字绑定失败!\n");
WSACleanup();
return 0;
}
//指定服务器的地址结构
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
serveraddr.sin_port = htons(4000);
//利用connect连接服务器
int r;
r = connect(s,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(r==-1){
printf("连接服务器失败!\n");
closesocket(s);
WSACleanup();
return 0;
}
else{
printf("连接服务器成功\n");
}
//直接创建一个线程用来进行数据的发送操作
CreateThread(0,0,Thread,(LPVOID)s,0,0);
//循环接受服务器的消息
char data[100] = {0};
data[0] = '9';
send(s,data,1,0);
while(1){
int l;
memset(data,0,100);
l = recv(s,data,100,0);
if(l>0){
if(data[0]=='2'){
printf("*******************\n");
printf("* 欢迎光临 *\n");
printf("*******************\n");
}
else if(data[0]=='3'){
printf("*******************\n");
printf("* 禁止进入 *\n");
printf("*******************\n");
}
else if(data[0]=='4'){
printf("*******************\n");
printf("* 谢谢惠顾 *\n");
printf("*******************\n");
}
}
}
closesocket(s);
WSACleanup();
return 0;
}
这里就是将用户输入单独开辟了一个线程,这样可以即可发数据又可收数据
3.收银程序
// 收银程序.cpp : Defines the entry point for the console application.
//收银程序代码
#include "stdafx.h"
#include <string.h>
int main(int argc, char* argv[])
{
//加载windows socket DDL
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2,2),&wsaData);
printf("*******************\n");
printf("* *\n");
printf("* 收银程序 *\n");
printf("* *\n");
printf("*******************\n");
if(iResult == 0){
printf("收银程序启动成功\n");
}
else{
printf("收银程序启动失败\n");
}
//创建套接字
SOCKET s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(s==-1){
printf("套接字绑定失败!\n");
WSACleanup();
return 0;
}
//指定服务器的地址结构
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
serveraddr.sin_port = htons(4000);
//利用connect连接服务器
int r = connect(s,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(r==-1){
printf("连接服务器失败!\n");
closesocket(s);
WSACleanup();
return 0;
}
else{
printf("连接服务器成功\n");
}
//用户收发数据的缓冲区
char buf[100] = {0};
char min[20] = {0};
char money[20] = {0};
int flag;
//与服务器进行车子离库的交互操作
buf[0] = '6';
int a = send(s,buf,1,0);
if(a<=0){
printf("发送失败\n");
}
memset(buf,0,100);
while(1){
printf("请输入要离场的车牌号:");
buf[0] = '5';
scanf("%s",buf+1);
int l;
l = send(s,buf,strlen(buf+1)+1,0);
if(l>0){
memset(buf,0,100);
memset(min,0,20);
memset(money,0,20);
int j=0;
int t;
t = recv(s,buf,100,0);
if(t>0){
for(int i=0;buf[i]!='#';i++){
min[i] = buf[i];
}
i = i + 1;
for(;buf[i]!='#';i++){
money[j] = buf[i];
j++;
}
printf("停车的时间为:%s\t应收费:%s\n",min,money);
memset(buf,0,100);
aa:
printf("请输入是否收银成功(1、是,0、否):\n");
scanf("%d",&flag);
if(flag==1){
int d;
buf[0] = '7';
d = send(s,buf,1,0);
if(d<=0){
printf("发送失败!\n");
}
}
else if(flag == 0){
int b;
buf[0] = '8';
b = send(s,buf,1,0);
if(b<=0){
printf("发送失败!\n");
}
printf("请重新收费!!\n");
goto aa;
}
}
else if(t==0){
printf("服务器关闭!\n");
break;
}
else{
printf("接受失败!\n");
}
}
else if(l==0){
printf("服务器关闭!\n");
break;
}
else{
printf("发送失败!\n");
}
}
closesocket(s);
WSACleanup();
return 0;
}
运行截图
总结
以上就是实验的全部内容,总体难度偏容易;主要就是突破点就是消息种类的判断,出口入口程序套接字的储存和客户端如何实现多线程