Socket 构建和解析协议消息-- "投票"示例程序

本文详细介绍了投票系统中客户端与服务器之间的Socket通信过程,包括消息类型、编码与解码实现。通过定义VoteMsg类表示消息,并实现VoteMsgCoder接口进行序列化与反序列化,确保了投票数据的正确传输。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍socket 的一个简单例子:  "投票" 示例程序

网站代码: http://booksite.elsevier.com/9780123742551/?ISBN=9780123742551


这里用一个类来表示客户端和服务器端的两种消息. VoteMsg.java

package com.test;

public class VoteMsg {
   private boolean isInquiry; // true 表示该消息是查询清,false 是投票请求
   private boolean isResponse;// true 是响应,false 为请求
   private int candidateID;   // in [0,1000]  候选人的ID
   private long voteCount;    //  所查询的候选人获得的总选票数

   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;
   }
 }

还需要根据一定的协议来对其进行编码和解码。 VoteMsgCoder 接口提供了对投票消息进行序列化和反序列化的方法.

import java.io.IOException;

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

基于文本方式编码实现VoteMsgCoder接口 的类.

package com.test;

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);
  }
}


UDP 版本的投票客户端与TCP 版本非常相似.  需要注意的是, 在UDP  客户端中不需要使用 成帧器,因为 UDP  协议为我们维护了消息的边界信息. 对于UDP 协议,我们使用基于文本的编码方式对消息进行编码。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值