`
san_yun
  • 浏览: 2652146 次
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

一个自定义协议的例子

 
阅读更多

 

public class VoteMsg {
  private boolean isInquiry; // true if inquiry; false if vote
  private boolean isResponse;// true if response from server
  private int candidateID;   // in [0,1000]
  private long voteCount;    // nonzero only in response

  public static final int MAX_CANDIDATE_ID = 1000;

  public VoteMsg(boolean isResponse, boolean isInquiry, int candidateID, long voteCount)
      throws IllegalArgumentException {
    // check invariants
    if (voteCount != 0 && !isResponse) {
      throw new IllegalArgumentException("Request vote count must be zero");
    }
    if (candidateID < 0 || candidateID > MAX_CANDIDATE_ID) {
      throw new IllegalArgumentException("Bad Candidate ID: " + candidateID);
    }
    if (voteCount < 0) {
      throw new IllegalArgumentException("Total must be >= zero");
    }
    this.candidateID = candidateID;
    this.isResponse = isResponse;
    this.isInquiry = isInquiry;
    this.voteCount = voteCount;
  }

  public void setInquiry(boolean isInquiry) {
    this.isInquiry = isInquiry;
  }

  public void setResponse(boolean isResponse) {
    this.isResponse = isResponse;
  }

  public boolean isInquiry() {
    return isInquiry;
  }

  public boolean isResponse() {
    return isResponse;
  }

  public void setCandidateID(int candidateID) throws IllegalArgumentException {
    if (candidateID < 0 || candidateID > MAX_CANDIDATE_ID) {
      throw new IllegalArgumentException("Bad Candidate ID: " + candidateID);
    }
    this.candidateID = candidateID;
  }

  public int getCandidateID() {
    return candidateID;
  }

  public void setVoteCount(long count) {
    if ((count != 0 && !isResponse) || count < 0) {
      throw new IllegalArgumentException("Bad vote count");
    }
    voteCount = count;
  }

  public long getVoteCount() {
    return voteCount;
  }

  public String toString() {
    String res = (isInquiry ? "inquiry" : "vote") + " for candidate " + candidateID;
    if (isResponse) {
      res = "response to " + res + " who now has " + voteCount + " vote(s)";
    }
    return res;
  }
}

 

 

 

import java.io.IOException;

public interface VoteMsgCoder {
  byte[] toWire(VoteMsg msg) throws IOException;
  VoteMsg fromWire(byte[] input) throws IOException;
}

 

 

基于文本的表示方法
该协议指定使用 US-ASCII 字符集对文本进行编码。消息的开头是一个所谓的"魔术字符串",即一个字符序列,用于接收者快速将投票协议的消息和网络中随机到来的垃圾消息区分开。投票/查询布尔值被编码成字符形式,'v'表示投票消息,'i'表示查询消息。消息的状态,即是否为服务器的响应,由字符'R'指示。状态标记后面是候选人 ID,其后跟的是选票总数,它们都编码成十进制字符串。

 

VoteMsgTextCoder 类提供了一种基于文本的 VoteMsg 编码方法。

 

 

VoteMsgTextCoder.java

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;

public class VoteMsgTextCoder implements VoteMsgCoder {
  /*
   * Wire Format "VOTEPROTO" <"v" | "i"> [<RESPFLAG>] <CANDIDATE> [<VOTECNT>]
   * Charset is fixed by the wire format.
   */

  // Manifest constants for encoding
  public static final String MAGIC = "Voting";
  public static final String VOTESTR = "v";
  public static final String INQSTR = "i";
  public static final String RESPONSESTR = "R";

  public static final String CHARSETNAME = "US-ASCII";
  public static final String DELIMSTR = " ";
  public static final int MAX_WIRE_LENGTH = 2000;

  public byte[] toWire(VoteMsg msg) throws IOException {
    String msgString = MAGIC + DELIMSTR + (msg.isInquiry() ? INQSTR : VOTESTR)
        + DELIMSTR + (msg.isResponse() ? RESPONSESTR + DELIMSTR : "")
        + Integer.toString(msg.getCandidateID()) + DELIMSTR
        + Long.toString(msg.getVoteCount());
    byte data[] = msgString.getBytes(CHARSETNAME);
    return data;
  }

  public VoteMsg fromWire(byte[] message) throws IOException {
    ByteArrayInputStream msgStream = new ByteArrayInputStream(message);
    Scanner s = new Scanner(new InputStreamReader(msgStream, CHARSETNAME));
    boolean isInquiry;
    boolean isResponse;
    int candidateID;
    long voteCount;
    String token;

    try {
      token = s.next();
      if (!token.equals(MAGIC)) {
        throw new IOException("Bad magic string: " + token);
      }
      token = s.next();
      if (token.equals(VOTESTR)) {
        isInquiry = false;
      } else if (!token.equals(INQSTR)) {
        throw new IOException("Bad vote/inq indicator: " + token);
      } else {
        isInquiry = true;
      }

      token = s.next();
      if (token.equals(RESPONSESTR)) {
        isResponse = true;
        token = s.next();
      } else {
        isResponse = false;
      }
      // Current token is candidateID
      // Note: isResponse now valid
      candidateID = Integer.parseInt(token);
      if (isResponse) {
        token = s.next();
        voteCount = Long.parseLong(token);
      } else {
        voteCount = 0;
      }
    } catch (IOException ioe) {
      throw new IOException("Parse error...");
    }
    return new VoteMsg(isResponse, isInquiry, candidateID, voteCount);
  }
}

 

 

fromWire()方法首先检查"魔术"字符串,如果在消息最前面没有魔术字符串,则抛出一个异常。这里说明了在实现协议时非常重要的一点:永远不要对从网络来的任何输入进行任何假设。你的程序必须时刻为任何可能的输入做好准备,并能够很好地对其进行处理。在这个例子中,如果接收到的不是期望的消息,fromWire()方法将抛出一个异常,否则,就使用 Scanner 实例,根据空白符一个一个地获取字段。注意,消息的字段数与其是请求消息(由客户端发送)还是响应消息(由服务器发送)有关。如果输入流提前结束或格式错误,fromWire()方法将抛出一个异常。

二进制表示方法
下面我们将展示另一种对投票协议消息进行编码的方法。与基于文本的格式相反,二进制格式使用固定大小的消息。每条消息由一个特殊字节开始,该字节的最高六位为一个"魔术"值 010101。这一点少量的冗余信息为接收者收到适当的投票消息提供了一定程度的保证。该字节的最低两位对两个布尔值进行了编码。消息的第二个字节总是 0,第三、第四个字节包含了 candidateID 值。只有响应消息的最后 8 个字节才包含了选票总数信息。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

/* Wire Format
 *                                1  1  1  1  1  1
 *  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 * |     Magic       |Flags|       ZERO            |
 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 * |                  Candidate ID                 |
 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 * |                                               |
 * |         Vote Count (only in response)         |
 * |                                               |
 * |                                               |
 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 */
public class VoteMsgBinCoder implements VoteMsgCoder {

  // manifest constants for encoding
  public static final int MIN_WIRE_LENGTH = 4;
  public static final int MAX_WIRE_LENGTH = 16;
  public static final int MAGIC = 0x5400;
  public static final int MAGIC_MASK = 0xfc00;
  public static final int MAGIC_SHIFT = 8;
  public static final int RESPONSE_FLAG = 0x0200;
  public static final int INQUIRE_FLAG =  0x0100;

  public byte[] toWire(VoteMsg msg) throws IOException {
    ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
    DataOutputStream out = new DataOutputStream(byteStream); // converts ints

    short magicAndFlags = MAGIC;
    if (msg.isInquiry()) {
      magicAndFlags |= INQUIRE_FLAG;
    }
    if (msg.isResponse()) {
      magicAndFlags |= RESPONSE_FLAG;
    }
    out.writeShort(magicAndFlags);
    // We know the candidate ID will fit in a short: it's > 0 && < 1000 
    out.writeShort((short) msg.getCandidateID());
    if (msg.isResponse()) {
      out.writeLong(msg.getVoteCount());
    }
    out.flush();
    byte[] data = byteStream.toByteArray();
    return data;
  }

  public VoteMsg fromWire(byte[] input) throws IOException {
    // sanity checks
    if (input.length < MIN_WIRE_LENGTH) {
      throw new IOException("Runt message");
    }
    ByteArrayInputStream bs = new ByteArrayInputStream(input);
    DataInputStream in = new DataInputStream(bs);
    int magic = in.readShort();
    if ((magic & MAGIC_MASK) != MAGIC) {
      throw new IOException("Bad Magic #: " +
			    ((magic & MAGIC_MASK) >> MAGIC_SHIFT));
    }
    boolean resp = ((magic & RESPONSE_FLAG) != 0);
    boolean inq = ((magic & INQUIRE_FLAG) != 0);
    int candidateID = in.readShort();
    if (candidateID < 0 || candidateID > 1000) {
      throw new IOException("Bad candidate ID: " + candidateID);
    }
    long count = 0;
    if (resp) {
      count = in.readLong();
      if (count < 0) {
        throw new IOException("Bad vote count: " + count);
      }
    }
    // Ignore any extra bytes
    return new VoteMsg(resp, inq, candidateID, count);
  }
}
 


发送和接收

   通过流发送消息非常简单,只需要创建消息,调用 toWire()方法,添加适当的成帧信息,再写入流。当然,接收消息就要按照相反的顺序执行。这个过程适用于 TCP 协议,而对于    UDP 协议,不需要显式地成帧,因为 UDP 协议中保留了消息的边界信息,

   首先我们实现一个投票服务器所用到的服务。当接收到投票消息时,投票服务器将调用 VoteService 类的 handleRequest() 方法对请求进行处理。

import java.util.HashMap;
import java.util.Map;

public class VoteService {

  // Map of candidates to number of votes
  private Map<Integer, Long> results = new HashMap<Integer, Long>();

  public VoteMsg handleRequest(VoteMsg msg) {
    if (msg.isResponse()) { // If response, just send it back
      return msg;
    }
    msg.setResponse(true); // Make message a response
    // Get candidate ID and vote count
    int candidate = msg.getCandidateID();
    Long count = results.get(candidate);
    if (count == null) {
      count = 0L; // Candidate does not exist
    }
    if (!msg.isInquiry()) {
      results.put(candidate, ++count); // If vote, increment count
    }
    msg.setVoteCount(count);
    return msg;
  }
}

 

 

import java.io.OutputStream;
import java.net.Socket;

public class VoteClientTCP {

  public static final int CANDIDATEID = 888;

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

    if (args.length != 2) { // Test for correct # of args
      throw new IllegalArgumentException("Parameter(s): <Server> <Port>");
    }

    String destAddr = args[0]; // Destination address
    int destPort = Integer.parseInt(args[1]); // Destination port

    Socket sock = new Socket(destAddr, destPort);
    OutputStream out = sock.getOutputStream();

    // Change Bin to Text for a different framing strategy
    VoteMsgCoder coder = new VoteMsgBinCoder();
    // Change Length to Delim for a different encoding strategy
    Framer framer = new LengthFramer(sock.getInputStream());

    // Create an inquiry request (2nd arg = true)
    VoteMsg msg = new VoteMsg(false, true, CANDIDATEID, 0);
    byte[] encodedMsg = coder.toWire(msg);

    // Send request
    System.out.println("Sending Inquiry (" + encodedMsg.length + " bytes): ");
    System.out.println(msg);
    framer.frameMsg(encodedMsg, out);

    // Now send a vote
    msg.setInquiry(false);
    encodedMsg = coder.toWire(msg);
    System.out.println("Sending Vote (" + encodedMsg.length + " bytes): ");
    framer.frameMsg(encodedMsg, out);
    
    // Receive inquiry response
    encodedMsg = framer.nextMsg();
    msg = coder.fromWire(encodedMsg);
    System.out.println("Received Response (" + encodedMsg.length
               + " bytes): ");
    System.out.println(msg);

    // Receive vote response
    msg = coder.fromWire(framer.nextMsg());
    System.out.println("Received Response (" + encodedMsg.length
           + " bytes): ");
    System.out.println(msg);
    
    sock.close();
  }
}
 
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class VoteServerTCP {

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

    if (args.length != 1) { // Test for correct # of args
      throw new IllegalArgumentException("Parameter(s): <Port>");
    }

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

    ServerSocket servSock = new ServerSocket(port);
    // Change Bin to Text on both client and server for different encoding
    VoteMsgCoder coder = new VoteMsgBinCoder();
    VoteService service = new VoteService();

    while (true) {
      Socket clntSock = servSock.accept();
      System.out.println("Handling client at " + clntSock.getRemoteSocketAddress());
      // Change Length to Delim for a different framing strategy
      Framer framer = new LengthFramer(clntSock.getInputStream());
      try {
        byte[] req;
        while ((req = framer.nextMsg()) != null) {
          System.out.println("Received message (" + req.length + " bytes)");
          VoteMsg responseMsg = service.handleRequest(coder.fromWire(req));
          framer.frameMsg(coder.toWire(responseMsg), clntSock.getOutputStream());
        }
      } catch (IOException ioe) {
        System.err.println("Error handling client: " + ioe.getMessage());
      } finally {
        System.out.println("Closing connection");
        clntSock.close();
      }
    }
  }
}
 

 

分享到:
评论

相关推荐

    对python 自定义协议的方法详解

    接下来,我们通过一个具体的例子来详细解释如何使用`struct`模块来实现一个简单的自定义协议。 ##### 3.1 定义异常类 首先,我们需要定义一个异常类`E`,用于在协议解析过程中遇到错误时抛出异常: ```python ...

    TCP自定义通讯协议参考

    在Netty协议栈中,TCP自定义通讯协议可以被实现为一个ChannelHandler,处理各种消息类型的解析和编码。字节传输过程中,由于采用UTF-8字符集和网络字节序(Big-Endian),确保了跨平台的兼容性。此外,心跳机制的...

    Mina自定义协议通信的示例

    总的来说,Mina自定义协议通信示例是一个很好的学习资源,它涵盖了网络编程的核心概念,如事件驱动模型、非阻塞I/O和自定义编解码。通过深入理解并实践这个示例,开发者能更好地掌握Mina框架,并有能力解决复杂网络...

    C实现基于Socket实现自定义协议通信

    在IT行业中,网络通信是至关重要的一个领域,而Socket编程是构建网络应用程序的基础。本教程主要聚焦于如何使用C语言实现基于Socket的自定义协议通信,涵盖了客户端和服务端的实现。我们将深入理解Socket编程的基本...

    自定义协议: 以0x5A 0xA5开头,第3个字节表示接下来要保存的字节个数;

    标题和描述中提到的自定义协议,其结构以特定的起始标识符0x5A 0xA5开始,然后紧接着一个字节指示后续数据的长度,这种设计具有明确的数据边界和简单的解析规则。 首先,0x5A和0xA5是十六进制数,它们组合在一起...

    [原创]IOS中自定义协议和回调示例附源代码

    首先,自定义协议是Objective-C和Swift中的一个重要特性,它允许我们定义一套方法和属性,然后让不同的类去遵循(Adopt)这些协议。这样,遵循协议的类就必须实现协议中定义的所有方法,从而实现特定的功能。创建...

    C#+自定义浏览器地址栏协议

    这通常涉及到在`HKEY_CLASSES_ROOT`下创建一个新的键,键名就是你的自定义协议,例如`myapp`。然后,在这个键下创建一个`Default`字符串值,指向你的应用程序路径,例如`"C:\Program Files\MyApp\MyApp.exe"`。 2. ...

    cordova ios 自定义插件例子

    这个"cordova ios 自定义插件例子"是一个适合新手入门的学习资源,它会引导你了解如何为 iOS 平台创建和使用自定义 Cordova 插件。 在 Cordova 中,插件是连接 JavaScript 世界与原生平台代码的桥梁。它们使你能...

    自定义协议解析demo

    在“自定义协议解析demo”中,我们可能会先定义一个结构体来存储协议的数据格式,比如: ```c typedef struct { char header[8]; // 协议头 int length; // 数据长度 char* data; // 数据体 } CustomProtocol; `...

    ns2自定义协议示例

    自定义协议意味着我们需要为ns2提供一个新的模块,该模块将处理特定的传输层操作,比如在这个例子中,是UDP(User Datagram Protocol)的定制版本。 1. **创建自定义协议类**:在ns2中,我们首先需要定义一个新的...

    C# SuperSocket 手把手教你入门 傻瓜教程-7(自定义SuperSocket内置的命令行协议)

    SuperSocket是一个轻量级且高度可扩展的网络通信框架,适用于开发各种类型的TCP服务,如聊天应用、游戏服务器或者数据传输服务。通过自定义命令行协议,我们可以更好地管理和解析客户端发送的数据,实现更高效、更...

    ios自定义table例子

    首先,自定义一个UITableViewCell类。在Swift中,你可以创建一个新的Swift文件,例如`CustomTableViewCell.swift`,并继承自`UITableViewCell`。在这个类中,你可以定义你需要的UI元素,比如UILabel、UIImageView或...

    FreeSwitch完整的自定义模块定义和改善自定义事件的例子

    要创建自定义事件,你需要编写一个ESL(Event Socket Library)事件处理器。通过这个库,你可以发送和接收事件,与FreeSwitch进行交互。事件的定义通常在C语言的源代码文件中完成,包括事件名称、数据结构和处理函数...

    CustomProtocolHandlerSample_Firefox:Firefox 插件处理自定义协议

    总的来说,“CustomProtocolHandlerSample_Firefox”提供了一个很好的起点,对于想要在Firefox中实现自定义协议处理的开发者来说,这是一个宝贵的参考资料。通过学习和实践,开发者不仅可以提升技能,还能为用户提供...

    jni协议+自定义控件简单例子

    在项目"JniText"中,我们可以期待看到一个结合了JNI本地函数调用和自定义开关控件的示例。这个项目可以帮助开发者了解如何将C/C++代码与Java代码相结合,以及如何设计和实现自定义UI元素。对于想要深入学习Android ...

    Java自定义协议进行socket连接的简单认证和消息.doc

    - 在这个例子中,自定义协议由一系列预定义的字符串组成,这些字符串代表了认证过程的不同阶段。这种方式简单但有限,实际应用中可能需要更复杂的数据结构和加密机制来确保安全性和可靠性。 6. **错误处理和重试...

    串口与以太网文件传送协议(或自定义控制协议)

    此解决方案包含4个工程 1)工程 yanzisoft.COMFTP 串口与以太网 文件传送协议(编译生成dll库文件:yanzisoft.COMFTP.dll) 2)工程 CECOMDebug 测试yanzisoft.COMFTP.dll在嵌入式wince系统测式使用的例子 3)工程 ...

    spring security 4 小例子带自定义过滤器

    例如,我们的自定义过滤器在验证成功后可以创建一个`Authentication`对象并将其设置到`SecurityContextHolder`中。 ```java public class CustomAuthenticationFilter extends GenericFilterBean { private final ...

Global site tag (gtag.js) - Google Analytics