Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (492)
Games in Android Showcase (112)
games submitted by our members
Games in WIP (556)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  [SOLVED] What's a Packet in Java?  (Read 4255 times)
0 Members and 1 Guest are viewing this topic.
Offline jonjava
« Posted 2011-10-30 23:44:17 »

Hi,

With Java Sockets you send and receive messages through the InputStream and OutputStream classes. The most basic tutorial reads lines of strings from these streams (as in a chat application or something).

What if you would like to send a bunch of primitive data types? One would think that wrapping the Streams into a DataInputStream and DataOutputStream would work - but here's a problem. Each time you write to these streams the message is sent directly! You can't bunch up a, say, 1 Byte and 2 Integers and a String ( in that order ) and then send the 'package' as whole through the Sockets Streams.

What I've been accustomed to from another language, is that you build a 'package' first, and then send the package - like this f.ex:
1  
2  
3  
4  
5  
6  
clearbuffer();
writeByte(1); // Used to identify what sort of message this is
writeInt(x);
writeInt(y);
writeString("Teddybears");
sendMessage(socket.outputStream);


This package would be extremely light as it would only be the size of the bytes that is sent.

In Java, I've noticed people sending whole Objects through Serialization. Which, to me, seems highly unnecessary - and a lot more heavy in size f.ex:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
class PositionMessage() implements Serializable {
int x;
int y;
String msg;
}
...
PositionMessage msg = new PositionMessage();
msg.x = x;
msg.y = y;
msg.msg = "Teddybears";
// code to serialize the msg ( turn it into bytes )
sendMessage(socket.outputStream);


and to identify what kind of message is sent you use the 'instanceof' keyword, and for each different type of message you create a new class.

I would think that this Java solution, while it might work, creates a lot more unnecessary weight in my packets and that there should exist a more controlled way, like the former solution, in Java. Is there?

Offline Riven
« League of Dukes »

JGO Overlord


Medals: 784
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #1 - Posted 2011-10-30 23:46:43 »

Regarding not sending data immediately:
1. use BufferedOutputStream and .flush() when you're done.
2. use ByteArrayOutputStream and .toByteArray() when you're done.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline jonjava
« Reply #2 - Posted 2011-10-31 01:02:44 »

Regarding not sending data immediately:
1. use BufferedOutputStream and .flush() when you're done.

o.O --> re-reads about Buffered Streams.

Looking closer at BufferedOutputStream and BufferedInputStream, they both write and read bytes of data.. which is good. How would you convert these into primitive data types?

I'm assuming you would use a Scanner for this, but how would you know in what order the bytes are supposed to be read if you can't figure out the message ID that... that we put in front of every single message as a byte value... kind of answered my own question there Tongue

So to lay it all out, would this be a suitable design in Java, using Scanners

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
Scanner s = null;
try {
s = new Scanner (
               new BufferedInputStream ( socket.getInputStream() );
byte b = s.nextByte();
     /* we know the first value is going to be a byte that is going to function as the ID
         to identify what kind of message it is and therefore we will know what values
         to read from it further down*/

if (b == 1) readPosition(s);
} finally {
if (s != null) s.close(); // close the scanner
}

// Methods
readPosition(Scanner s) {
int x = s.readNextInt();
int y = s.readNextInt();
String str = s.readNextString();

// do stuff with the information
}


I haven't done any tests, but to me this looks like it might work. Would you agree?

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 784
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #3 - Posted 2011-10-31 01:04:19 »

Looking closer at BufferedOutputStream and BufferedInputStream, they both write and read bytes of data.. which is good. How would you convert these into primitive data types?


Mix 'n match...
1  
2  
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline ra4king

JGO Kernel


Medals: 345
Projects: 3
Exp: 5 years


I'm the King!


« Reply #4 - Posted 2011-10-31 01:05:31 »

Scanner is slow, use BufferedReader.
As for writing, you can wrap an ObjectOutputStream around the output stream since it can write primitives, strings, and objects.

EDIT: You can also take a look at my networking library, which uses a Packet system and handles the sending for you.

Offline jonjava
« Reply #5 - Posted 2011-10-31 01:14:22 »

Scanner is slow, use BufferedReader.
As for writing, you can wrap an ObjectOutputStream around the output stream since it can write primitives, strings, and objects.

EDIT: You can also take a look at my networking library, which uses a Packet system and handles the sending for you.

The BufferedReader can't read primitives though. The DataInputStream ( and Scanner ) does however... so changing the Scanner into DataInputStream...

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
try {
DataInputStream din = new DataInputStream (
               new BufferedInputStream ( socket.getInputStream() );
byte message_id = din.readByte();
     /* we know the first value is going to be a byte that is going to function as the ID
         to identify what kind of message it is and therefore we will know what values
         to read from it further down*/

if (message_id == 1) readPosition(din);
} catch (IOExceptione ){ }
 
// Methods
readPosition(DataInputStream din) {
int x = din.readInt();
int y = din.readInt();
String str = din.readUTF(): // I assume this reads a string?

// do stuff with the information
}

Offline counterp

Senior Member


Medals: 11



« Reply #6 - Posted 2011-10-31 02:41:41 »

Part of my buffer class:

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  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
import java.nio.ByteBuffer;
import java.util.Arrays;

public class Buffer {

   public Buffer() {
      this(128);
   }

   public Buffer(int size) {
      buffer = new byte[size];
   }

   private byte[] buffer;
   private int readPos = 0, writePos = 0, sizePos = 0;

   public byte[] array() {
      return Arrays.copyOf(buffer, writePos);
   }

   public int get() {
      return buffer[readPos++] & 0xFF;
   }

   public int getSigned() {
      return buffer[readPos++];
   }

   public int getUShort() {
      return buffer[readPos++] << 8 | (buffer[readPos++] & 0xFF);
   }

   public int getShort() {
      return (buffer[readPos++] & 0xFF) << 8 | (buffer[readPos++] & 0xFF);
   }

   public int getInt() {
      return (buffer[readPos++] & 0xFF) << 24 | (buffer[readPos++] & 0xFF) << 16 | (buffer[readPos++] & 0xFF) << 8 | (buffer[readPos++] & 0xFF);
   }

   public long getLong() {
      return (buffer[readPos++] & 0xFFL) << 56 | (buffer[readPos++] & 0xFFL) << 48 | (buffer[readPos++] & 0xFFL) << 40 | (buffer[readPos++] & 0xFFL) << 32 | (buffer[readPos++] & 0xFFL) << 24 | (buffer[readPos++] & 0xFFL) << 16 | (buffer[readPos++] & 0xFFL) << 8 | buffer[readPos++] & 0xFFL;
   }

   public double getDouble() {
      return Double.longBitsToDouble(getLong());
   }

   public int getLEShort() {
      return (buffer[readPos++] & 0xFF) | (buffer[readPos++] & 0xFF) << 8;
   }

   public int getLEInt() {
      return (buffer[readPos++] & 0xFF) | (buffer[readPos++] & 0xFF) << 8 | (buffer[readPos++] & 0xFF) << 16 | (buffer[readPos++] & 0xFF) << 24;
   }

   public String getString() {
      StringBuilder bldr = new StringBuilder();
      char c;
      while ((c = (char) buffer[readPos++]) != 0)
         bldr.append(c);
      return bldr.toString();
   }

   public int readPosition() {
      return readPos;
   }

   public int remaining() {
      return writePos - readPos;
   }

   public int writePosition() {
      return writePos;
   }

   public Buffer put(ByteBuffer buffer) {
      int len = buffer.position();
      ensure(len);
      System.arraycopy(buffer.array(), 0, this.buffer, writePos, len);
      writePos += len;
      return this;
   }

   public Buffer put(byte[] buf) {
      int len = buf.length;
      ensure(len);
      System.arraycopy(buf, 0, buffer, writePos, len);
      writePos += len;
      return this;
   }

   public Buffer put(byte[] buf, int off, int len) {
      ensure(len - off);
      System.arraycopy(buf, off, buffer, writePos, len - off);
      writePos += len - off;
      return this;
   }

   public Buffer put(int b) {
      ensure(1);
      buffer[writePos++] = (byte) b;
      return this;
   }

   public Buffer putShort(int s) {
      ensure(2);
      buffer[writePos++] = (byte) (s >> 8);
      buffer[writePos++] = (byte) s;
      return this;
   }

   public Buffer putInt(int i) {
      ensure(4);
      buffer[writePos++] = (byte) (i >> 24);
      buffer[writePos++] = (byte) (i >> 16);
      buffer[writePos++] = (byte) (i >> 8);
      buffer[writePos++] = (byte) i;
      return this;
   }

   public Buffer putLong(long l) {
      ensure(8);
      buffer[writePos++] = (byte) (l >> 56);
      buffer[writePos++] = (byte) (l >> 48);
      buffer[writePos++] = (byte) (l >> 40);
      buffer[writePos++] = (byte) (l >> 32);
      buffer[writePos++] = (byte) (l >> 24);
      buffer[writePos++] = (byte) (l >> 16);
      buffer[writePos++] = (byte) (l >> 8);
      buffer[writePos++] = (byte) l;
      return this;
   }

   public Buffer putDouble(double d) {
      return putLong(Double.doubleToRawLongBits(d));
   }

   public Buffer putLEInt(int i) {
      ensure(4);
      buffer[writePos++] = (byte) i;
      buffer[writePos++] = (byte) (i >> 8);
      buffer[writePos++] = (byte) (i >> 16);
      buffer[writePos++] = (byte) (i >> 24);
      return this;
   }

   public Buffer putLEShort(int s) {
      ensure(2);
      buffer[writePos++] = (byte) s;
      buffer[writePos++] = (byte) (s >> 8);
      return this;
   }

   public Buffer putString(String s) { // cannot use character 0
     ensure(s.length() + 1);
      for (int i = 0; i < s.length(); i++)
         buffer[writePos++] = (byte) s.charAt(i);
      buffer[writePos++] = 0;
      return this;
   }

   public Buffer end() {
      buffer[sizePos] = (byte) (writePos - sizePos - 1);
      return this;
   }

   public Buffer start(int opcode) {
      ensure(2);
      buffer[writePos++] = (byte) opcode;
      sizePos = writePos++;
      return this;
   }

   public void clear() {
      readPos = writePos = sizePos = 0;
      Arrays.fill(buffer, (byte) 0);
   }

   private void ensure(int size) {
      int required = writePos + size;
      if (buffer.length <= required) {
         byte[] temp = new byte[required * 2];
         System.arraycopy(buffer, 0, temp, 0, buffer.length);
         buffer = temp;
      }
   }

   public void rewind() {
      readPos = 0;
   }

   public void skip(int n) {
      readPos += n;
   }

}


writing objects sucks, and sending single packets also sucks, you should buffer them and send them after X period of time.
Offline theagentd
« Reply #7 - Posted 2011-10-31 11:42:53 »

Is no one going to recommend NIO? The psuedo-code that JonJava wrote as an example can be neatly implemented with a ByteBuffer. Why stick to the old IO library when ByteBuffers are so convenient?

Myomyomyo.
Offline ra4king

JGO Kernel


Medals: 345
Projects: 3
Exp: 5 years


I'm the King!


« Reply #8 - Posted 2011-10-31 16:14:01 »

Haha, I was about to say that counterp's Buffer class looks like java.nio.ByteBuffer.

@OP
At this stage, it is best for you to use java.nio.channels.SocketChannel as you can just use the ByteBuffer class to read and write. However, ByteBuffer does not have a way to write Strings so you will have to get the bytes from a string:
1  
byte[] data = "this is my string".getBytes("UTF-8");

Offline counterp

Senior Member


Medals: 11



« Reply #9 - Posted 2011-10-31 23:57:54 »

Yeah, I guess you can compare it to NIO byte buffer, except mine is faster and no annoying flip method, it also doesn't have some of the same methods, but it has support for signed and unsigned types (or as much support as you can get in java). You can also specify the size of a packet with mine using the start and end methods (if you have a packet with varying size and it is important that you know how big it is).
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline jonjava
« Reply #10 - Posted 2011-11-01 01:39:13 »

EDIT: You can also take a look at my networking library, which uses a Packet system and handles the sending for you.

I had a moderately (didn't understand eveything) thorough (took me 2 hours) look through your library (the networking bits) - it looks very clean. Since I'm just now, as a result of this thread, getting to know the java.nio package it still is a bit over my head. But I found other outlets for tutorials and explanations, this blog post in particular was very informative for me since I was already accustomed to the java.io package and how the java.net sockets work.
http://blogs.oracle.com/slc/entry/javanio_vs_javaio

Is no one going to recommend NIO? The psuedo-code that JonJava wrote as an example can be neatly implemented with a ByteBuffer. Why stick to the old IO library when ByteBuffers are so convenient?

Thanks for pointing this out.

@OP
At this stage, it is best for you to use java.nio.channels.SocketChannel as you can just use the ByteBuffer class to read and write. However, ByteBuffer does not have a way to write Strings so you will have to get the bytes from a string:
1  
byte[] data = "this is my string".getBytes("UTF-8");


I was slightly unsure on how to read Strings, thanks for clearing that up. Of course the byte size of a string will vary but since I can identify what type of message is incoming I'll be able to add the strings size in front of the string in the buffer as a byte value (for example) and that way know how many bytes to read.

The biggest difference between the old Socket from java.net and the new SocketChannel* from java.nio is that SocketChannel implements non-blocking mode. Which means that the program won't "lock up" when trying to read for input like it would on the old java.net Socket (you would have to make a new Thread to listen for input in order to circumvent our program from locking up).
[size=6pt]
*The java.nio SocketChannels are built from the old java.net Sockets.[/size]

Here's a brief revealing outtake from the blog post linked above:
Quote
SocketChannel

SocketChannel is different to FileChannel: The new socket channels can operate in nonblocking mode and are selectable. It's no longer necessary to dedicate a thread to each socket connection, Using the new NIO classes, one or a few threads can manage hundreds or even thousands of active socket connections with little or no performance loss. It's possible to perform readiness selection of socket channels using a Selector object.

Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Nickropheliac (15 views)
2014-08-31 22:59:12

TehJavaDev (23 views)
2014-08-28 18:26:30

CopyableCougar4 (33 views)
2014-08-22 19:31:30

atombrot (41 views)
2014-08-19 09:29:53

Tekkerue (40 views)
2014-08-16 06:45:27

Tekkerue (35 views)
2014-08-16 06:22:17

Tekkerue (25 views)
2014-08-16 06:20:21

Tekkerue (37 views)
2014-08-16 06:12:11

Rayexar (72 views)
2014-08-11 02:49:23

BurntPizza (49 views)
2014-08-09 21:09:32
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!