网络编程课程设计报告--模拟火车售票退票系统

网络编程课程设计报告--模拟火车售票退票系统本文简介:Java网络编程报告姓名:蒋怡学号:1107300134题目:模拟火车售票退票系统一、作业要求:模拟火车售票退票系统实现一个服务器为多个客户服务,要求1、服务器用线程池,线程容量为4,座位数为60个,即01-60号座.2、客户通过网络发送请求可以退票可以买票,先来先服务,买票还是退票由随机数决定,退
网络编程课程设计报告--模拟火车售票退票系统本文内容:
Java网络编程报告
姓名:
蒋怡
学号:
1107300134
题目:
模拟火车售票退票系统
一、作业要求:
模拟火车售票退票系统实现一个服务器为多个客户服务,要求
1、
服务器用线程池,线程容量为4,座位数为60个,即01-60号座.
2、
客户通过网络发送请求可以退票可以买票,先来先服务,买票还是退票由随机数决定,退票必须是该用户买过的有效票,先买的票先退。若退票时该用户已没有买到的票,则改为买票。若服务器票已售完,则需等待,先来先服务,哪个客户先来,服务器将先为哪个客户服务。
3、
服务器每次接收一个客户请求需打印该客户的端口号、IP和该用户是买票还是退票,处理该请求之前目前剩余的票所有座号,处理之后剩余的座号也要打印出来,并延迟一个随机处理时间,以模拟对每个客户处理的时间不同。将处理结果发给客户。
4、
客户收到结果后打印到屏幕。
5、
注意资源共享的问题,适当时可用同步代码,不允许用同步方法。注意线程之间的协作。
演示时开放4-5个用户,并演示一次退票无效的情况(即要退的票在服务器中还没有卖出去,要求2是正常情况)
2、
主要设计思路:
1、该程序包括以下几个类:
1)、EchoClient.java
2)、EchoServer.java
3)、Node.java
定义了线性表的一个节点的结构,并对节点进行初始化
4)、LList.java
接口类,包含以下几个方法:
boolean
isEmpty();
//
判断线性表是否为空
int
length();
//
返回线性表长度
T
get(int
i);
//
返回第i(i>0)个元素
void
insert(int
i,T
x);
//
插入x作为第i个元素
void
insert1(T
x);
//按顺序插入一个数到链表中
T
remove(int
i);
//
删除第i个元素并返回被删对象
void
append(T
x);
//
在线性表最后插入x元素
5)、SingleLinkList.java
实现接口LList。
6)、Customer.java
定义了choise,cus_tickets两个属性和choice()方法,其中
choise是一个随机产生的0或1,用来决定客户买票或退票。
cus_list是一个线性表,用来存储客户所买到的所有票。
7)、Tickets.java
定义了number和list两个属性,其中number用来表示服务器售出的票号,list是一个线性表,用来存储剩余火车票。包含了售票票方法sell()和退票方法return_ticket()。
2、
思路及流程图
1)、首先客户端通过调用Customer类的choice()方法,由choice()方法来决定客户是买票还是退票。若choise==1,则客户买票,若choise==0,则客户退票。流程图如下:
客户买票
choice=(int)(Math.random()*2)
choise==1
客户退票票
是
否
2)
、若客户买票,则通过输出流将买票信息发送给服务器端。若客户退票,则通过“customer.cus_list.isEmpty()”这个语句判断客户是否有票可退,若客户有票可退,则通过输出流将退票信息及所退票号发送给服务器端;若客户无票可退,则改为买票,通过输出流将信息发送给服务器端。流程图如下:
否
是
客户退票
客户拥有的票是否为空
发送退票信息给服务器端
发送买票信息给服务器端
客户买票
发送买票信息给服务器端
3)
、服务器端通过输入流接收客户端的信息,接收信息后,随机产生一个时间,线程休眠,模拟网络延迟。然后判断客户是买票还是退票,若是买票,则调用Tickets类中的sell()方法进行售票处理;若是退票,则调用Tickets类中的return_ticket()方法进行退票处理。流程图如下:
接收客户端的信息
线程休眠一段时间
客户是否买票
tickets.sell()
Tickets.return
_ticket()
否
是
4)、若客户是买票的,则通过“list.isEmpty()”判断是否有票可售,若有票可售,则进行售票处理(即将list线性表中的第一个节点删除,表示此票已售出),处理后将信息反馈给客户;客户收到服务器端的信息后,将反馈信息打印输出,同时将所买到的票添加到cus_list线性表的最后。
若无票可售,则线程等待,将线程加入等待队列,当线程被唤醒后,进行售票处理,处理后将信息反馈给客户。客户收到服务器端的信息后,输出反馈的信息,同时,将所买到的票添加到cus_list线性表的最后。
客户收到服务器端反馈信息
打印输出信息
cus_list.append()
否
是否有票可售
售票处理
是
售票
线程等待
线程被唤醒后进行售票处理
将线程加入等待队列
将反馈信息发送到客户端
5)
、若客户是退票的,首先判断客户所退的票是否是已售出的票,若不是,则非法退票,退票失败,将反馈信息发送给客户端;若是,则进行退票处理(将所要退的票按大小添加到线性表list中),退票处理后,将反馈信息发送给客户端,然后该线程将已经退了的票从cus_list中删除(即cus_list.remove())。判断是否有线程在等待队列中,若有,则将队列中的第一个线程唤醒,进行售票处理,然后将反馈信息发送给客户端。
退票是否成功
打印输出退票成功信息
cus_list.remove()
是
输出退票失败信息
客户端收到服务器端的反馈信息
否
等待队列是否空
退票
该票是否已售出
否
非法退票,退票失败
是
退票处理
将所退的票售给第一个等待的线程
将反馈信息发送到客户端
3、
关键代码
for(i=1;i
socketList=new
ArrayList();
//排队序列
public
EchoServer()
throws
IOException
{
serverSocket
=
new
ServerSocket(port);
serverSocket.setReceiveBufferSize(50);
//创建线程池
executorService=
Executors.newFixedThreadPool(
POOL_SIZE);
System.out.println(“服务器启动“);
}
public
void
service()
{
while
(true)
{
Socket
socket=null;
try
{
socket
=
serverSocket.accept();
executorService.execute(new
Handler(socket,socketList));
}catch
(IOException
e)
{
e.printStackTrace();
}
}
}
public
static
void
main(String
args[])throws
IOException
{
new
EchoServer().service();
}
}
class
Handler
implements
Runnable{
private
Socket
socket;
private
Tickets
ticket=new
Tickets();
List
socketList=new
ArrayList();
//排队序列
public
Handler(Socket
socket,List
socketList){
this.socket=socket;
this.socketList=socketList;
}
private
PrintWriter
getWriter(Socket
socket)throws
IOException{
OutputStream
socketOut
=
socket.getOutputStream();
return
new
PrintWriter(socketOut,true);
}
2)
、EchoServer.java
private
BufferedReader
getReader(Socket
socket)throws
IOException{
InputStream
socketIn
=
socket.getInputStream();
return
new
BufferedReader(new
InputStreamReader(socketIn));
}
public
String
echo(String
msg)
{
return
“echo:“+
msg;
}
@SuppressWarnings(“static-access“)
public
void
run(){
try
{
System.out.println(“New
connection
accepted
“+
socket.getInetAddress()
+
“:“+socket.getPort());
BufferedReader
br
=getReader(socket);
PrintWriter
pw
=
getWriter(socket);
String
msg
=
null;
String
cus_number=null;
while
((msg=br.readLine())
!=
null)
{
//
模拟售票的网络延迟
try
{
Thread.sleep((long)
(Math.random()*3000));
//产生一个随机处理时间
}
catch
(InterruptedException
e)
{
e.printStackTrace();
}
if(msg.equals(“buy“))
{
ticket.sell(socketList,socket,pw);
}
else
if(msg.equals(“refund“))
{
ticket.return_ticket(cus_number=br.readLine(),socket,socketList,pw);
}
}
}catch
(IOException
e)
{
e.printStackTrace();
}finally
{
try{
if(socket!=null)socket.close();
}catch
(IOException
e)
{e.printStackTrace();}
}
}
}
public
class
Tickets
{
String
number;//售出票的序号
static
String
tickets[]={“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“};
static
SingleLinkList
list
=
new
SingleLinkList(tickets);
//用list链表存储火车票
public
void
sell(List
socketList,Socket
socket,PrintWriter
pw
)
{
synchronized
(list)
{
System.out.println(socket.getPort()+“客户买票“);
System.out.print(“客户买票前剩余火车票为:“);
System.out.println(this.list);
//打印还剩多少张票可卖
if(list.isEmpty())//若无票可售,则将线程加入等待队列
{
System.out.println(“暂时无票!排队中“);
socketList.add(socket);
System.out.println(“客户:“+socket.getInetAddress()+“:“+socket.getPort()+“,队列长度:“+socketList.toArray().length);
System.out.println(“等待队列的第一个客户为:“+socketList.get(0).getPort()+“\n“);
try
{
list.wait();
}
catch
(InterruptedException
e)
{
//
TODO
Auto-generated
catch
block
e.printStackTrace();
}
number=list.remove(1);
System.out.println(“票号:“+number+“被队首:“+socketList.get(0).getPort()+“预定成功\n“);
socketList.remove(0);
pw.println(“客户买到的票的票号为:“+number);//将客户买到票的信息传回给客户
pw.println(number);
//将客户买到的票号传给客户
}
else
{
//若符合条件进行售票
number=list.remove(1);
//每次票号最小的票售出,将已售出的票从火车票链表中移除
System.out.println(“售出票的序号为
“+
number
);//打印售出票的信息
System.out.print(“客户买票后剩余火车票为:“);
System.out.println(list+“\n“);//打印售票后所剩余的火车票
pw.println(“客户买到的票的票号为:“+number);//将客户买到票的信息传回给客户
pw.println(number);
//将客户买到的票号传给客户
}
}
}
3)
、Tickets.java
public
void
return_ticket(String
cus_number,Socket
socket,List
socketList,PrintWriter
pw)
{
//将客户所退的票按顺序添加的票号里面
synchronized
(list)
{
int
i;
for(i=1;i<=list.length();i++)
{
if(list.get(i).equals(cus_number))//判断客户所退的票是否是服务器以售出的票,若不是,则非法退票
{
pw.println(“非法退票!退票失败!“);
System.out.println(“非法退票!退票失败!\n“);
return;
}
}
list.insert1(cus_number);
//将客户退的票按顺序插入到火车票链表中
System.out.println(socket.getPort()+“客户退票,所退票号为:“+cus_number);
System.out.print(“客户退票后剩余火车票为:“);
System.out.println(list+“\n“);
pw.println(“客户退票,所退票号为:“+cus_number);//将客户退票信息传回给客户
if(!socketList.isEmpty())//退票后判断队列中是否有客户在等待买票,若是,则将所退的票买给队列中的第一个客户
{
list.notify();
//唤醒线程
}
}
}
}
4、
程序运行截图
1)、服务器端截图
当票已售完时,客户请求买票就将客户加入一个等待队列,如果有另一个客户来退票,则将所退的票售给等待队列中的第一个客户。
当所
非法退票情况演示:控制客户退票号为20的票,因为20号票还未售出,所以退票失败!
2)、客户端截图
客户请求退票时,客户无票可退,转为买票情况。
5、
实验总结
通过本次实验,掌握了Server
Socket的用法和多线程编程的的原理、还有同步代码块的使用、线程等待和唤醒的使用,在实验过程中遇到了很多不明白的问题,通过找书、与同学讨论都一一解决了。第一次实验的时候,基本上不知道从何处入手,但是通过慢慢的摸索和研究,一步一步地将一个个小问题解决,才能将程序编写出来。在调试过程中,遇到了很多奇奇怪怪的问题,很多时候是因为自己的考虑不够全面和逻辑出来的错误所引起的,在同学的帮助下,把这些问题都一一解决了。
