网游开发之socket的简单设计

对于普通应用的网络模块一般使用http文本协议,在android开发中使用http协议比较简单,sdk已经做了很好的封装了,具体使用方法可以参考我的这篇博文。而在游戏开发中,可以结合使用http和socket,当然了http协议底层也是基于tcp协议的。http协议是无连接、无状态的,每次连接只能处理一个请求,然后就断了,而且发一个请求需要附加额外信息(请求行、请求头),每次请求都需要重新建立连接;使用socket的好处是更高效和省流量,建立一次连接后,只要不手动或者出现异常断开,就可以一直互相发送数据,而且是直接以字节的形式发送,不需要额外的附加信息,缺点就是难度加大了,需要服务端和客户端很好的配合,保证发送和读取时数据的顺序一致。本文通过一个简单的demo介绍开发android网游时socket的使用方法,主要包括:android客户端和一个简单的使用java实现的server端,实现客户端和服务端互相发送数据的功能。
1.客户端代码实现
首先创建一个Android应用,名称:AndroidSocketTest
然后分别创建4个文件:BytesUtil.java、BytesReader.java、BytesWriter.java、TCPCommunication.java,下面分别介绍这几个文件的用处和源码:
BytesUtil.java:包含了一些静态工具方法:基本类型和字节数组的相互转换,字符串和字节数组的相互转换,字节数组的赋值和大小重置,对输入流进行读取保存等。比较简单,下面直接看源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/**
author:alexzhou
date  :2012-11-7
  **/
 
public final class BytesUtil {
 
     /**
      *整型转换成字节数组
      * @param value 要转换的整型值
      * @return
      */
     public static byte [] shortToBytes( int value) {
         byte []write = new byte [ 2 ];
         write[ 0 ] = ( byte )( (value >>> 8 ) & 0xFF );
         write[ 1 ] = ( byte )( (value >>> 0 ) & 0xFF );
         return write;
     }
 
     public static byte [] intToBytes( int value) {
         byte []write = new byte [ 4 ];
         write[ 0 ] = ( byte )( (value >>> 24 ) & 0xFF );
         write[ 1 ] = ( byte )( (value >>> 16 ) & 0xFF );
         write[ 2 ] = ( byte )( (value >>> 8 ) & 0xFF );
         write[ 3 ] = ( byte )( (value >>> 0 ) & 0xFF );
         return write;
     }
 
     public static byte [] longToBytes( long value) {
         byte []write = new byte [ 8 ];
         write[ 0 ] = ( byte )( (value >>> 56 ) & 0xFF );
         write[ 1 ] = ( byte )( (value >>> 48 ) & 0xFF );
         write[ 2 ] = ( byte )( (value >>> 40 ) & 0xFF );
         write[ 3 ] = ( byte )( (value >>> 32 ) & 0xFF );
         write[ 4 ] = ( byte )( (value >>> 24 ) & 0xFF );
         write[ 5 ] = ( byte )( (value >>> 16 ) & 0xFF );
         write[ 6 ] = ( byte )( (value >>> 8 ) & 0xFF );
         write[ 7 ] = ( byte )( (value >>> 0 ) & 0xFF );
         return write;
     }
 
     /**
      * 字节数组转换成整型
      * @param value
      * @return
      */
     public static int bytesToInt( byte []value) {
         int i1 = (value[ 0 ] & 0xFF ) << 24 ;
         int i2 = (value[ 1 ] & 0xFF ) << 16 ;
         int i3 = (value[ 2 ] & 0xFF ) << 8 ;
         int i4 = (value[ 3 ] & 0xFF ) << 0 ;
         return (i1 | i2 | i3 | i4);
     }
 
     public static short bytesToShort( byte [] value) {
         int s1 = (value[ 0 ] & 0xFF ) << 8 ;
         int s2 = (value[ 1 ] & 0xFF ) << 0 ;
         return ( short )(s1 | s2);
     }
 
     public static long bytesToLong( byte [] value) {
         long L1 = (value[ 0 ] & 0xFF ) << 56 ;
         long L2 = (value[ 1 ] & 0xFF ) << 48 ;
         long L3 = (value[ 2 ] & 0xFF ) << 40 ;
         long L4 = (value[ 3 ] & 0xFF ) << 32 ;
         long L5 = (value[ 4 ] & 0xFF ) << 24 ;
         long L6 = (value[ 5 ] & 0xFF ) << 16 ;
         long L7 = (value[ 6 ] & 0xFF ) << 8 ;
         long L8 = (value[ 7 ] & 0xFF ) << 0 ;
         return (L1 | L2 | L3 | L4 | L5 | L6 | L7 | L8);
     }
 
     /**
      * 从指定字节数组中拷贝部分数据
      * @param origin
      * @param from
      * @param to
      * @return
      */
     public static byte [] copyBytes( byte [] origin, int from, int to) {
         int len = to - from;
         if (len < 0 || origin.length - from <= 0 ) {
             throw new IllegalArgumentException( "copyBytes->error arguments:to=" +to+ ",from=" +from);
         }
         byte [] ret = new byte [len];
         if (len == 0 ) return ret;
         System.arraycopy(origin, from, ret, 0 , Math.min(len, origin.length - from));
         return ret;
     }
 
     /**
      * 重置字节数组的大小,然后把原内容复制到新的字节数组中
      * @param origin
      * @param newSize
      * @return
      */
     public static byte [] resizeBytes( byte [] origin, int newSize) {
         if (newSize < 0 ) {
             throw new IllegalArgumentException( "resizeBytes->newSize must >= 0" );
         }
         byte [] ret = new byte [newSize];
         if (newSize == 0 ) return ret;
         System.arraycopy(origin, 0 ,ret, 0 ,Math.min(origin.length, newSize));
         return ret;
     }
 
     /**
      * 读取输入流中字节,并保存到指定的字节数组中
      * @param is
      * @param data
      * @param off
      * @param len
      */
     public static void readData(InputStream is, byte data[], int off, int len) {
         int hasRead = 0 ;
         final int BUFFER = 1024 ;
         while (hasRead < len) {
             try {
                 int remain = len - hasRead;
                 int count = is.read(data, off + hasRead, remain > BUFFER ? BUFFER : remain);
                 if (count < 0 ) throw new IOException( "readData->read data error" );
                 hasRead += count;
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
}

BytesReader.java:从服务端接收数据时使用,定义了一个字节数组类型的成员变量,用来保存从输入流中读取的数据。封装了一些从该字节数组中读取相应数据类型的函数,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
  * 接受服务端数据时,读取字节并转换到相应类型
author:alexzhou
date  :2012-11-7
  **/
 
public final class BytesReader {
 
     private final byte []data;
     //字节数组的大小
     private final int size;
     //当前读取的位置
     private int position;
 
     public BytesReader( byte []data) {
         this .data = data;
         this .size = data.length;
         this .position = 0 ;
     }
 
     public byte [] read( int len) {
         if (len < 0 ) return null ;
         byte [] value = BytesUtil.copyBytes(data, position, position + len);
         this .position += len;
         return value;
     }
 
     public int getSize() {
         return size;
     }
 
     public boolean isAvailable() {
         return size - position > 0 ;
     }
 
     public short readShort() {
         byte [] value = read( 2 );
         return BytesUtil.bytesToShort(value);
     }
 
     public int readInt() {
         byte [] value = read( 4 );
         return BytesUtil.bytesToInt(value);
     }
 
     public long readLong() {
         byte [] value = read( 8 );
         return BytesUtil.bytesToLong(value);
     }
 
     public byte readByte() {
         int value = this .isAvailable() ? ( 0xFF & data[position++]) : - 1 ;
         return ( byte )value;
     }
 
     public byte [] readBytes() {
         int len = readShort();
         //读取大数据
         if (len >= 0xFFFF ) {
             len = this .readInt();
         }
         return len == 0 ? null : read(len);
     }
 
     public String readUTF() {
         byte [] bytes = readBytes();
         if ( null != bytes) {
             try {
                 return new String(bytes, "UTF-8" );
             } catch (UnsupportedEncodingException e) {
                 e.printStackTrace();
             }
         }
         return null ;
     }
}

BytesWriter.java:向服务端发送数据时使用,跟BytesReader.java对应,也是把需要发送的数据保存到字节数组中,然后一次性发送给服务器。源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
author:alexzhou
date  :2012-11-9
  **/
 
public final class BytesWriter {
 
     private byte [] data;
     private int count;
 
     public BytesWriter() {
         this ( 64 );
     }
 
     public BytesWriter( int size) {
         this .data = new byte [size];
     }
 
     public byte [] getBytes() {
         return this .data.length == count ? data : count == 0 ? null : BytesUtil.resizeBytes( this .data, count);
     }
 
     public void write( byte [] value) {
         this .write(value, 0 , value == null ? 0 : value.length);
     }
 
     public void write( byte [] d, int offset, int len) {
         if (d == null || len == 0 ) return ;
         int newCount = count + len;
         if (newCount > this .data.length) {
             int newSize = Math.max( this .data.length << 1 , newCount);
             this .data = BytesUtil.resizeBytes( this .data, newSize);
         }
         System.arraycopy(d, offset, this .data, this .count, len);
         this .count = newCount;
     }
 
     public void writeInt( int value) {
         this .write(BytesUtil.intToBytes(value));
     }
 
     public void writeShort( int value) {
         this .write(BytesUtil.shortToBytes(value));
     }
 
     public void writeLong( long value) {
         this .write(BytesUtil.longToBytes(value));
     }
 
     public void writeByte( byte value) {
         int newCount = count + 1 ;
         if (newCount > this .data.length) {
             int newSize = Math.max( this .data.length << 1 , newCount);
             this .data = BytesUtil.resizeBytes( this .data, newSize);
         }
         this .data[count] = value;
         this .count = newCount;
     }
 
     public void writeBytes( byte [] value) {
         int length = (value == null ? 0 : value.length);
         //发送大数据时
         if (length >= 0xFFFF ) {
             this .writeShort( 0xFFFF );
             this .writeInt(length);
         } else {
             //告诉服务端发送的数据的大小
             this .writeShort(length);
         }
         this .write(value);
     }
 
     public void writeUTF(String value) {
         if (value == null || value.length() == 0 ) {
             this .writeShort( 0 );
         }
         byte [] bytes = null ;
         try {
             bytes = value.getBytes( "UTF-8" );
         } catch (UnsupportedEncodingException e) {
             e.printStackTrace();
         }
         this

你可能感兴趣的:(Android开发,网游)