• 投票协议:发送和接收


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

     

  • 相关阅读:
    二叉树重建leetcode
    leetcode 字符串
    leetcode first missing positive,覆盖区间
    leetcode 较难题
    Linq分页
    Linq 数据库通用的操作类
    .Net3.5扩展方法实现对象JSON序列化
    js 判断输入内容(主要针对汉字)的字节长度
    div 内table 居中
    Linq增、删、改、查
  • 原文地址:https://www.cnblogs.com/wuyida/p/6301074.html
Copyright © 2020-2023  润新知