投票协议:发送和接收

来源:互联网 发布:深入浅出学java 编辑:程序博客网 时间:2024/06/02 05:29

通过流发送消息非常简单,只需要创建消息,调用toWire()方法,添加适当的成帧信息,再写入流。当然,接收消息就要按照相反的顺序执行。这个过程适用于TCP协议,而对于UDP协议,不需要显式地成帧,因为UDP协议中保留了消息的边界信息。为了对发送与接收过程进行展示,我们考虑投票服务的如下几点:1)维护一个候选人ID与其获得选票数的映射,2)记录提交的投票,3)根据其获得的选票数,对查询指定的候选人和为其投票的消息做出响应。首先,我们实现一个投票服务器所用到的服务。当接收到投票消息时,投票服务器将调用VoteService类的handleRequest() 方法对请求进行处理。

VoteService.java

0 import java.util.HashMap;

1 import java.util.Map;

2

3 public class VoteService {

4

5 // Map of candidates to number of votes

6 private Map<Integer, Long> results = new

HashMap<Integer, Long>();

7

8 public VoteMsg handleRequest(VoteMsg msg) {

9 if (msg.isResponse()) { // If response, just send it back

10 return msg;

11 }

12 msg.setResponse(true); // Make message a response

13 // Get candidate ID and vote count

14 int candidate = msg.getCandidateID();

15 Long count = results.get(candidate);

16 if (count == null) {

17 count = 0L; // Candidate does not exist

18 }

19 if (!msg.isInquiry()) {

20 results.put(candidate, ++count); // If vote, increment

count

21 }

22 msg.setVoteCount(count);

23 return msg;

24 }

25 }

 

 VoteService.java

 1.创建候选人ID与选票数量的映射:第6

对于查询请求,给定的候选人ID用来在映射中查询其获得的选票数量。对于投票请求,增加后的选票数又存回映射。

 2.handleRequest():第8-24

 返回响应:第9-12

 如果投票消息已经是一个响应信息,则直接发回而不对其进行处理和修改。否则,对其响应消息标志进行设置。

查找当前获得的选票总数:第13-18

 根据候选人ID从映射中获取其获得的选票总数。如果该候选人ID在映射中不存在,则将其获得的选票数设为0.

 如果有新的投票,则更新选票总数:第19-21

 如果之前候选人不存在,则创建新的映射,否则,只是简单地修改已有的映射。

 设置选票总数并返回消息:第22-23

 下面我们将展示如何实现一个TCP投票客户端,该客户端通过TCP套接字连接到投票服务器,在一次投票后发送一个查询请求,并接收查询和投票结果。

 VoteClientTCP.java

0 import java.io.OutputStream;

1 import java.net.Socket;

2

3 public class VoteClientTCP {

4

5 public static final int CANDIDATEID = 888;

6

7 public static void main(String args[]) throws Exception

{

8

9 if (args.length != 2) { // Test for correct # of args

10 throw new IllegalArgumentException("Parameter(s):

<Server> <Port>");

11 }

12

13 String destAddr = args[0]; // Destination address

14 int destPort = Integer.parseInt(args[1]); //

Destination port

15

16 Socket sock = new Socket(destAddr, destPort);

17 OutputStream out = sock.getOutputStream();

18

19 // Change Bin to Text for a different framing strategy

20 VoteMsgCoder coder = new VoteMsgBinCoder();

21 // Change Length to Delim for a different encoding

strategy

22 Framer framer = new

LengthFramer(sock.getInputStream());

23

24 // Create an inquiry request (2nd arg = true)

25 VoteMsg msg = new VoteMsg(false, true, CANDIDATEID, 0);

26 byte[] encodedMsg = coder.toWire(msg);

27

28 // Send request

29 System.out.println("Sending Inquiry (" +

encodedMsg.length + " bytes): ");

30 System.out.println(msg);

31 framer.frameMsg(encodedMsg, out);

32

33 // Now send a vote

34 msg.setInquiry(false);

35 encodedMsg = coder.toWire(msg);

36 System.out.println("Sending Vote (" +

encodedMsg.length + " bytes): ");

37 framer.frameMsg(encodedMsg, out);

38

39 // Receive inquiry response

40 encodedMsg = framer.nextMsg();

41 msg = coder.fromWire(encodedMsg);

42 System.out.println("Received Response (" +

encodedMsg.length

43 + " bytes): ");

44 System.out.println(msg);

45

46 // Receive vote response

47 msg = coder.fromWire(framer.nextMsg());

48 System.out.println("Received Response (" +

encodedMsg.length

49 + " bytes): ");

50 System.out.println(msg);

51

52 sock.close();

53 }

54 }

 

 VoteClientTCP.java

 1.参数处理:第9-14

 2.创建套接字,获取输出流:第16-17

 3.创建二进制编码器和基于长度的成帧器:第20-22

我们将使用一个编码器对投票消息进行编码和解码,这里为我们的协议选择的是二进制编码器。其次,由于TCP协议是一个基于流的服务,我们需要提供字节的帧。在此,我们使用LengthFramer类,它为每条消息添加一个长度前缀。注意,我们只需要改变具体的类,就能方便地转换成基于定界符的成帧方法和基于文本的编码方式,这里将VoteMsgCoderFramer换成VoteMsgTextCoderDelimFramer即可。 

4.创建和发送消息:第24-37 

创建,编码,成帧和发送查询请求,后面是为相同候选人的投票消息。

 5.获取和解析响应:第39-50

 我们使用nextMsg()方法用于返回下一条编码后的消息,并通过fromWire()方法对其进行解析/解码。

6.关闭套接字:第52

下面我们示范TCP版本的投票服务器。该服务器反复地接收新的客户端连接,并使用VoteService类为客户端的投票消息作出响应。

 VoteServerTCP.java

0 import java.io.IOException;

1 import java.net.ServerSocket;

2 import java.net.Socket;

3

4 public class VoteServerTCP {

5

6 public static void main(String args[]) throws Exception {

7

8 if (args.length != 1) { // Test for correct # of args

9 throw new IllegalArgumentException("Parameter(s): <Port>");

10 }

11

12 int port = Integer.parseInt(args[0]); // Receiving Port

13

14 ServerSocket servSock = new ServerSocket(port);

15 // Change Bin to Text on both client and server for different encoding

16 VoteMsgCoder coder = new VoteMsgBinCoder();

17 VoteService service = new VoteService();

18

19 while (true) {

20 Socket clntSock = servSock.accept();

21 System.out.println("Handling client at " + clntSock.

getRemoteSocketAddress());

22 // Change Length to Delim for a different framing strategy

23 Framer framer = new LengthFramer(clntSock.getInputStream());

24 try {

25 byte[] req;

26 while ((req = framer.nextMsg()) != null) {

27 System.out.println("Received message (" + req.length + " bytes)");

28 VoteMsg responseMsg = service.handleRequest(coder.fromWire(req));

29 framer.frameMsg(coder.toWire(responseMsg), clntSock.getOutputStream());

30 }

31 } catch (IOException ioe) {

32 System.err.println("Error handling client: " + ioe.getMessage());

33 } finally {

34 System.out.println("Closing connection");

35 clntSock.close();

36 }

37 }

38 }

39 }

 

VoteServerTCP.java

1.为服务器端建立编码器和投票服务:第15-17

2.反复地接收和处理客户端连接:第19-37

接收新的客户端,打印客户端地址:第20-21

为客户端创建成帧器:第23

从客户端获取消息并对其解码:第26-28

反复地向成帧器发送获取下一条消息的请求,直到其返回null,即指示了消息的结束。

处理消息,发送响应信息:第28-29

将解码后的消息传递给投票服务,以进行下一步处理。编码,成帧和回发响应消息。

UDP版本的投票客户端与TCP版本非常相似。需要注意的是,在UDP客户端中我们不需要使用成帧器,因为UDP协议为我们维护了消息的边界信息。对于UDP协议,我们使用基于文本的编码方式对消息进行编码,不过只要客户端与服务器能达成一致,也能够很方便地改成其他编码方式。

 

VoteClientUDP.java

0 import java.io.IOException;

1 import java.net.DatagramPacket;

2 import java.net.DatagramSocket;

3 import java.net.InetAddress;

4 import java.util.Arrays;

5

6 public class VoteClientUDP {

7

8 public static void main(String args[]) throws

IOException {

9

10 if (args.length != 3) { // Test for correct # of args

11 throw new IllegalArgumentException("Parameter(s):

<Destination>" +

12 " <Port> <Candidate#>");

13 }

14

15 InetAddress destAddr = InetAddress.getByName(args[0]);

// Destination addr

16 int destPort = Integer.parseInt(args[1]); //

Destination port

17 int candidate = Integer.parseInt(args[2]); // 0 <=

candidate <= 1000 req'd

18

19 DatagramSocket sock = new DatagramSocket(); // UDP

socket for sending

20 sock.connect(destAddr, destPort);

21

22 // Create a voting message (2nd param false = vote)

23 VoteMsg vote = new VoteMsg(false, false, candidate, 0);

24

25 // Change Text to Bin here for a different coding strategy

26 VoteMsgCoder coder = new VoteMsgTextCoder();

27

28 // Send request

29 byte[] encodedVote = coder.toWire(vote);

30 System.out.println("Sending Text-Encoded Request (" +

encodedVote.length

31 + " bytes): ");

32 System.out.println(vote);

33 DatagramPacket message = new

DatagramPacket(encodedVote, encodedVote.length);

34 sock.send(message);

35

36 // Receive response

37 message = new DatagramPacket(new

byte[VoteMsgTextCoder.MAX_WIRE_LENGTH],

38 VoteMsgTextCoder.MAX_WIRE_LENGTH);

39 sock.receive(message);

40 encodedVote = Arrays.copyOfRange(message.getData(), 0,

message.getLength());

41

42 System.out.println("Received Text-Encoded Response ("

+ encodedVote.length

43 + " bytes): ");

44 vote = coder.fromWire(encodedVote);

45 System.out.println(vote);

46 }

47 }

 

 VoteClientUDP.java

 1.设置DatagramSocket      和连接:第10-20 

通过调用connect()方法,我们不必1)为发送的每个数据报文指定远程地址和端口,也不必2)测试接收到的每个数据报文的源地址。

 2.创建选票和编码器:第22-26

 这次使用的是文本编码器,但我们也可以很容易地换成二进制编码器。注意这里我们不需要成帧器,因为只要每次发送都只有一个投票消息,UDP协议就已经为我们保留了边界信息。

3.向服务器发送请求消息:第28-34

4.接收,解码和打印服务器响应信息:第36-45

在创建DatagramPacket时,我们需要知道消息的最大长度,以避免数据被截断。当然,在对数据报文进行解码时,我们只使用数据报文中包含的实际字节,因此调用了Arrays.copyOfRange()方法来复制返回的数据报文中数组的子序列。

最后是UDP投票服务器,同样,也与TCP版本非常相似。

 VoteServerUDP.java

0 import java.io.IOException;

1 import java.net.DatagramPacket;

2 import java.net.DatagramSocket;

3 import java.util.Arrays;

4

5 public class VoteServerUDP {

6

7 public static void main(String[] args) throws

IOException {

8

9 if (args.length != 1) { // Test for correct # of args

10 throw new IllegalArgumentException("Parameter(s):

<Port>");

11 }

12

13 int port = Integer.parseInt(args[0]); // Receiving Port

14

15 DatagramSocket sock = new DatagramSocket(port); //

Receive socket

16

17 byte[] inBuffer = new

byte[VoteMsgTextCoder.MAX_WIRE_LENGTH];

18 // Change Bin to Text for a different coding approach

19 VoteMsgCoder coder = new VoteMsgTextCoder();

20 VoteService service = new VoteService();

21

22 while (true) {

23 DatagramPacket packet = new DatagramPacket(inBuffer,

inBuffer.length);

24 sock.receive(packet);

25 byte[] encodedMsg =

Arrays.copyOfRange(packet.getData(), 0,

packet.getLength());

26 System.out.println("Handling request from " +

packet.getSocketAddress() + " ("

27 + encodedMsg.length + " bytes)");

28

29 try {

30 VoteMsg msg = coder.fromWire(encodedMsg);

31 msg = service.handleRequest(msg);

32 packet.setData(coder.toWire(msg));

33 System.out.println("Sending response (" +

packet.getLength() + " bytes):");

34 System.out.println(msg);

35 sock.send(packet);

36 } catch (IOException ioe) {

37 System.err.println("Parse error in message: " +

ioe.getMessage());

38 }

39 }

40 }

41 }

 

VoteServerUDP.java

 1.设置:第17-20

为服务器创建接收缓存区,编码器,以及投票服务。

2.反复地接收和处理客户端的投票消息:第22-39

为接收数据报文创建DatagramPacket:第23

在每次迭代中将数据区重置为输入缓存区。

接收数据报文,抽取数据:第24-25

UDP替我们完成了成帧的工作!

解码和处理请求:第30-31

服务将响应返回给消息。

编码并发送响应消息:第32-35

 

相关下载:

Java_TCPIP_Socket编程(doc)

http://download.csdn.net/detail/undoner/4940239

 

文献来源:

UNDONER(小杰博客) :http://blog.csdn.net/undoner

LSOFT.CN(琅软中国) :http://www.lsoft.cn

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 刚买面霜打不开怎么办 可乐瓶盖鼓起来怎么办 暖壶塞子吸住了怎么办 茶兀瓶盖打不开怎么办 水杯盖太紧了拧不开怎么办 矿泉水瓶盖拧不开了怎么办 弩弦用手拉不上怎么办 茅台酒瓶口漏酒怎么办 化妆品盖子丢了怎么办 化妆品盖子碎了怎么办 自制水泵压力小怎么办 大学数学不会做怎么办 下雪了怎么办教案幼儿园小班 下水道被混凝土堵塞怎么办 日本足贴丢了胶布怎么办 牙齿被可乐腐蚀怎么办 三十岁满嘴无牙怎么办 水乳盖子打不开怎么办 蜂蜜罐子打不开了怎么办 蜂蜜盖子第二次拧不开怎么办 玻璃杯子拧不开盖子怎么办 玻璃杯水杯盖子拧不开怎么办 鞋子蝴蝶结掉了怎么办 蝴蝶翅膀受伤了怎么办 手被割了个口子怎么办 致炫方向盘重怎么办 黑檀7打不透怎么办 乒乓球底板太轻怎么办 狙击精英4卡怎么办 鼠标点一下变两下怎么办 工程干完不给钱怎么办 屋里有大蛾子怎么办 房间很多小飞虫怎么办 雷蛇键盘失灵怎么办 xp驱动 不支持win10怎么办 阿提拉全面战争统治度太低怎么办 微信号变成wxid怎么办 ipv4 ipv6未连接怎么办 土豆丝粘锅怎么办还面 土豆丝容易碎怎么办 胡萝卜的菱形块怎么办