博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
投票协议:发送和接收
阅读量:5148 次
发布时间:2019-06-13

本文共 9302 字,大约阅读时间需要 31 分钟。

通过流发送消息非常简单,只需要创建消息,调用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)

 

文献来源:

UNDONER(小杰博客) :

LSOFT.CN(琅软中国) :

 

转载于:https://www.cnblogs.com/wuyida/archive/2012/12/20/6301074.html

你可能感兴趣的文章
51Nod1367 完美森林 贪心
查看>>
Linux常用命令
查看>>
读书笔记——《黑客大曝光》(6/8)
查看>>
CentOS_6.5安装Nginx+PHP+MySQL
查看>>
laravel的门面模式
查看>>
javascript中cookie常用操作
查看>>
用cocos2d-html5做的消除类游戏《英雄爱消除》——概述
查看>>
BZOJ4448 SCOI2015 情报传递
查看>>
使用git提交内容到网盘
查看>>
[笔记] SQL性能优化 - 常用语句(一)
查看>>
openvino安装踩坑记
查看>>
html03
查看>>
LINQ语法详解
查看>>
The folder is already a source folder
查看>>
App 组件化/模块化之路——Android 框架组件(Android Architecture Components)使用指南
查看>>
Java里的日期和时间学习
查看>>
securecrt 上传下载
查看>>
公共技术点之 Java 反射 Reflection
查看>>
Android动态操作RelativeLayout里面的Button
查看>>
DICOM:DICOM3.0网络通信协议
查看>>