I am currently working on a homework assignment where I have to create a server that accepts connections using Java Selector and communicates over socket channels. Clients connect to the Server using their own socket channel, the server accepts the connection, sets the new socket channel to OP_READ and waits for information to read. Below is the run method for the server and the accept and read methods for the server.
//TODO: Figure out why the data is being read over and over again
public void run()
{
while(true)
{
try{
// Process potential change requests
processChangeRequests();
//select all the things! returns the amount of channels that are ready
//PPP: Select is stuck after accepted socket is changed to OP_READ -- data is not read
this.selector.select();
//DEBUG
System.out.println("Selector Selected Something!");
//get the set of keys of the channels that are ready
Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
while(keyIterator.hasNext()) {
//get the key itself
SelectionKey key = keyIterator.next();
keyIterator.remove();
System.out.println("key: "+key.interestOps());
if(!key.isValid())
{
continue;
}
//conditions
if(key.isAcceptable()) {
// a connection was accepted by a ServerSocketChannel.
this.accept(key);
}
else if (key.isReadable()) {
// a channel is ready for reading
this.readData(key);
}
else if (key.isWritable()) {
// a channel is ready for writing
//this.fPool.submitTask(new MessageDigestProcessor(key,this.buffer.array()));
}
}
//fPool.submitTask(new MessageDigestProcessor(socket));
}catch (Exception e) { e.printStackTrace(); }
}
}
private void accept(SelectionKey key) throws IOException
{
System.out.println("Accepted Connection!");
ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
SocketChannel clientSocketChannel = serverSocketChannel.accept();
clientSocketChannel.configureBlocking(false);
clientSocketChannel.register(this.selector, SelectionKey.OP_READ);
}
/**
* Reads a message digest from the input stream from the socket
* #return the message digest read from the input stream or "" if there was a problem reading
* TODO read until there is no more data to be read?
* TODO do not close the socket channels unless there is an exception from reading
*/
private void readData(SelectionKey key) throws IOException
{
SocketChannel clientSocketChannel = (SocketChannel) key.channel();
//DEBUG
System.out.println("Message received from: "+clientSocketChannel.socket().getInetAddress());
//clear the buffer before reading
this.buffer.clear();
int numRead;
try
{
numRead = clientSocketChannel.read(this.buffer);
//DEBUG
System.out.println("num read: "+numRead);
} catch (IOException e)
{
key.cancel();
clientSocketChannel.close();
return;
}
if(numRead==-1)
{
key.cancel();
key.channel().close();
return;
}
//DEBUG
try {
System.out.println(Utility.SHA1FromBytes(this.buffer.array()));
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//time for writing!
key.interestOps(SelectionKey.OP_WRITE);
}
The accept works just fine after I connect to the server with the client, but after the client writes data the server is blocked on the this.selector.select() so it never calls readData(). Is there something I'm missing? I followed the code through the debugger in eclipse and that was where it stopped.
EDIT: Here is the client code
package cs455.scaling;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
public class Client implements Runnable {
//stores all the hash codes for all messages that are sent to the server
private final LinkedList<String> hashCodes = new LinkedList<String>();
private final long messageRate;
private final ByteBuffer buffer = ByteBuffer.allocate(8192);
private final Selector selector;
private final InetSocketAddress serverSocketAddress;//maintain an open connection at all times, bro. Why should I shut it?
private final List<ChangeRequest> changeRequests = new LinkedList<ChangeRequest>();
//private final Thread senderThread;
public Client(String ipAddress, int port, long messageRate) throws UnknownHostException, IOException
{
this.serverSocketAddress = new InetSocketAddress(InetAddress.getByName(ipAddress),port);
this.messageRate = messageRate;
this.selector = Selector.open();
// senderThread = new Thread(new MessageDigestSender(this));
// senderThread.start();
}
public long getMessageRate()
{
return messageRate;
}
/**
* Generates a message digest and sends it over the connected socket
* TODO open new connection each time a send needs to occur?
* #throws IOException
*/
public void sendMessageDigest() throws IOException
{
initConnection();
//generate the message
byte [] data = generateMessageDigest();
//prepare the data
buffer.clear();
buffer.put(data);
this.selector.wakeup();
}
/**
* Does the actual writing of the message
* #param SelectionKey key that represents the channel that is being written to
*/
private void write(SelectionKey key) throws IOException
{
SocketChannel socketChannel = (SocketChannel) key.channel();
socketChannel.write(buffer);
System.out.println("Wrote data to Server...");
key.interestOps(SelectionKey.OP_READ);//not interested in writing for right now -- wait for message from the server
}
public void run()
{
while(true)
{
try{
//process the socket channel op changes
processChangeRequests();
this.selector.select();
//get the set of keys of the channels that are ready
Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
while(keyIterator.hasNext()) {
//get the key itself
SelectionKey key = keyIterator.next();
keyIterator.remove();
//System.out.println(key.interestOps());
//avoid invalid keys
if(!key.isValid())
{
continue;
}
if (key.isConnectable()) {
this.finishConnection(key);
} else if (key.isReadable()) {
this.readData(key);
} else if (key.isWritable()) {
this.write(key);
}
}
} catch (Exception e) { e.printStackTrace(); }
}
}
/**
* Method that queues up changes that need to be made to selection keys before they are processed
* this is useful for when multiple threads need to make changes to selection keys
* XXX: Used when the caller is NOT the selecting thread
*/
private void processChangeRequests() throws IOException
{
synchronized(this.changeRequests)
{
Iterator<ChangeRequest> changes = this.changeRequests.iterator();
while(changes.hasNext())
{
ChangeRequest changeRequest = changes.next();
switch(changeRequest.type)
{
case(ChangeRequest.CHANGE_OP):
changeRequest.channel.keyFor(this.selector).interestOps(changeRequest.ops);
break;
case ChangeRequest.REGISTER:
changeRequest.channel.register(this.selector, changeRequest.ops);
break;
}
}
this.changeRequests.clear();
}
}
/**
* Initialize the socket channel on the specified ip address and port
* configure it for non-blocking
* #param ipAddress
* #param port
* #throws IOException
*/
private void initConnection() throws IOException
{
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(this.serverSocketAddress);
//a request is made because the selecting thread is not the caller of this function
synchronized(this.changeRequests){
this.changeRequests.add(new ChangeRequest(socketChannel, ChangeRequest.REGISTER,SelectionKey.OP_CONNECT));
}
}
/**
* Finish the connection by calling finishConnect() on the channel and then setting it to OP_WRITE
* #param key
*/
private void finishConnection(SelectionKey key)
{
// a connection was established with a remote server.
SocketChannel socketChannel = (SocketChannel) key.channel();
// Finish the connection. If the connection operation failed
// this will raise an IOException.
try {
socketChannel.finishConnect();
System.out.println("Finished connecting to server");
} catch (IOException e) {
// Cancel the channel's registration with our selector
e.printStackTrace();
key.cancel();
return;
}
// Register an interest in writing on this channel
key.interestOps(SelectionKey.OP_WRITE);
}
/**
* Serves as a wrapper around the SHA1FromBytes method
* It generates a byte array using the util.Random class and then generates a message digest from those bytes
* If the algorithm doesn't exist then a blank string is returned
*/
private byte [] generateMessageDigest()
{
Random random = new Random();
byte [] data = new byte[8192];
random.nextBytes(data);
String digest = "";
try {
digest = Utility.SHA1FromBytes(data);
//add it to the hashCodes linkedList
hashCodes.add(digest);
System.out.println("Generated Digest: "+digest);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return data;
}
/**
* Reads a message digest from the input stream from the socket
* #return the message digest read from the input stream or "" if there was a problem reading
* TODO read until there is no more data to be read?
* TODO do not close the socket channels unless there is an exception from reading
*/
private void readData(SelectionKey key) throws IOException
{
SocketChannel clientSocketChannel = (SocketChannel) key.channel();
//DEBUG
System.out.println("Message received from: "+clientSocketChannel.socket().getInetAddress());
//clear the buffer before reading
this.buffer.clear();
int numRead;
try
{
numRead = clientSocketChannel.read(this.buffer);
//DEBUG
System.out.println("num read: "+numRead);
} catch (IOException e)
{
//problem reading from a socket channel
e.printStackTrace();
key.cancel();
clientSocketChannel.close();
return;
}
this.buffer.flip();
System.out.println("Received Message Digest: "+new String(this.buffer.array()));
//done! open business later on!
key.channel().close();
key.channel().keyFor(this.selector).cancel();
}
public static void main(String [] args)
{
String serverHost = "";
int serverPort = 0;
long messageRate = 0;
if(args.length<3)
{
System.err.println("Format: java cs455.scaling.Client [server-host] [server-port] [message-rate]");
System.exit(0);
}
//parse the arguments out
try{
serverHost = args[0];
serverPort = Integer.parseInt(args[1]);
messageRate = Long.parseLong(args[2]);
} catch(NumberFormatException ne) {
ne.printStackTrace();
} catch (ArrayIndexOutOfBoundsException ae)
{
ae.printStackTrace();
}
//start the client
try
{
Client client = new Client(serverHost,serverPort,messageRate);
Thread t = new Thread(client);
t.start();
client.sendMessageDigest();
} catch(IOException i)
{
i.printStackTrace();
}
}
}
You are ignoring the return values of both read() and write(), so you aren't able to detect short or zero length writes, or EOS conditions when reading.
Related
I'm trying to create a TCP client/server that sends and receives messages. My problem right now is that I can't do them both simultaneously. I'm sending a message to the server and reading it just fine, but after each received message to the server, I also want to send back a response to the client acknowledging it.
I'm sending several messages per second to the server, but when I try to re-send a message to the client, it just gets stuck, no message on either end. I can however send in either direction, client to server and server to client, but only one way.
This is the client handler for the server:
public TCPClientHandler(Socket client, int bufferSize) throws IOException {
this.client = client;
messageToClient = new PrintWriter(client.getOutputStream(),true);
recvFromClient = new BufferedReader(new InputStreamReader(client.getInputStream()));
buffer = new byte[bufferSize];
}
#Override
public void run() {
try {
/* Read the data from the client.
* If the message is larger than the server buffer we close the connection.
* If the client closes connection we get exception and we catch it
* at the end.
*/
while (true) {
if (recvFromClient.readLine().getBytes().length > buffer.length){
break;
}
/* Receiving and printing message */
buffer = Arrays.copyOf(recvFromClient.readLine().getBytes(),
recvFromClient.readLine().getBytes().length);
String messageFromClient = new String(buffer,StandardCharsets.UTF_8);
System.out.println("Client message: " + messageFromClient);
messageToClient.println();
/* Sending message to client */
}
} catch (IOException e) {
System.out.println("Connection to client lost...");
} finally {
System.out.println("Connection closed on thread " + Thread.currentThread().getName());
messageToClient.close();
try {
recvFromClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
This is the client:
public class TCPEchoClient {
/*
Args input
1. Server Address
2. Server Port
3. Socket Buff Size
4. Transfer Rate
5. Message
*/
public void run(String args[]){
try {
// Dummy values
InetAddress host = InetAddress.getByName("localhost");
int serverPort = 4950;
int buffSize = 100;
int transferRate = 5;
String echoMessage = "112312451265214126531456234321";
String receive;
System.out.println("Connection to server on port " + serverPort);
Socket socket = new Socket(host,serverPort);
socket.setReceiveBufferSize(buffSize);
System.out.println("Just connected to " + ocket.getRemoteSocketAddress());
// Writer and Reader to write and read to/from the socket.
PrintWriter writeToServer = new PrintWriter(socket.getOutputStream(),true);
BufferedReader recvFromServer = new BufferedReader(new `InputStreamReader(socket.getInputStream()));`
if (transferRate < 1){
writeToServer.println(echoMessage);
}else {
// Continuously send messages with 1 second between x amount of
// messages, until client is aborted.
while (true) {
for (int i = 0; i < transferRate; i++) {
writeToServer.println(echoMessage);
receive = new String(recvFromServer.readLine().getBytes(), StandardCharsets.UTF_8);
System.out.println(receive);
}
Thread.sleep(1000);
}
}
// Close reader/writer and socket
writeToServer.close();
recvFromServer.close();
socket.close();
} catch (SocketException e){
System.out.println("Socket exception...");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TCPEchoClient client = new TCPEchoClient();
client.run(args);
}
}
I would like to read the messages that are transmitted as bytes, store them in the buffer, and THEN read the message, but so far no success.
Fixed the issue. I moved the receive part inside the client to AFTER the thread sleep.
I am running into CPU usage problem when I am using a
java.nio.channel.Selector.
when the server thread started, it initially consume 200% cpu resource and dramatically drop down to 0.1%. but if it is connected by a client. this number rapidly increases to 97% - 100% and keep that number even after the client disconnected.
here is the code I wrote for server.
package com.cs.gang.test;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public final class TCPConnectionServer implements Runnable {
private RandomAccessFile logFile;
private Selector selector;
public TCPConnectionServer() throws IOException {
final File logFile = new File("server_log.txt");
if (logFile.exists()) {
logFile.delete();
}
this.logFile = new RandomAccessFile(logFile.getCanonicalPath(), "rw");
System.out.println(logFile.getCanonicalPath());
selector = Selector.open();
final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(8888));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
#Override
public void run() {
while (true) {
try {
if (selector.select() > 0) {
final Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
final SelectionKey key = keys.next();
keys.remove();
if (key.channel() instanceof SocketChannel) {
if (!((SocketChannel) key.channel()).isConnected()) {
logFile.writeChars(((SocketChannel) key.channel()).toString() + " is off line");
}
}
if (key.isAcceptable()) {
final ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
final SocketChannel clientChannel = serverSocketChannel.accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
logFile.writeChars(clientChannel.toString() + "is now connected");
} else if (key.isReadable()) {
final SocketChannel client = (SocketChannel) key.channel();
if (client.isConnected()) {
final ByteBuffer buffer = ByteBuffer.allocate(1024);
int byteRead = -1;
final StringBuilder sb = new StringBuilder(client.toString()).append(" : ");
while ((byteRead = client.read(buffer)) > 0) {
sb.append(new String(buffer.array()), 0, byteRead);
buffer.clear();
}
logFile.writeChars(sb.toString());
System.out.println(sb.toString());
} else {
System.out.println("Closed Connection detected");
}
}
}
} else {
System.out.println("Sleep for 100ms");
Thread.sleep(100);
}
} catch (final IOException e) {
e.printStackTrace();
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
new Thread(new TCPConnectionServer()).start();
}
}
can any one help me out? I am new to NIO and I am having absolutely no idea about this problem now.
Thanks
clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
The problem is here. OP_WRITE is almost always ready, so your selector will rarely block and usually spin. This is a misuse of OP_WRITE. The correct way to use it is as follows:
Write whenever you have something to write.
If write() returns zero, register the socket for OP_WRITE and return to the selection loop. Of course you'll also have to save the ByteBuffer you were writing from, in association with the channel: this is normally done via the SelectionKey's attachment, either directly or indirectly. Ideally you will have both a read and a write ByteBuffer per channel, saved in a channel context object which in turn is saved as the key attachment.
When OP_WRITE fires, continue writing from that ByteBuffer. If this completes, i.e. write() does't return zero or a short write count, de-register the channel for OP_WRITE.
I am using SocketChannel to communicate with remote server. I send data using socketChannel.write() with no errors and exceptions, however, the server log indicates no data was received; client tcp traffic monitor also shows that the string message in the ByteBuffer was not sent.
Could anyone give me a hint why this is the case? Thank you!
public class Client implements Runnable {
SocketChannel socketChannel;
Selector selector;
SelectionKey key;
ByteBuffer inbuf, outbuf;
int id;
#Override
public void run() {
try {
// prepare inbuf and outbuf
inbuf = ByteBuffer.allocate(10000);
outbuf = ByteBuffer.allocate(10000);
// prepare a socket channel for communication
socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("<remote server ip>", ));
selector = Selector.open();
socketChannel.configureBlocking(false);
key = socketChannel.register(selector, SelectionKey.OP_READ
| SelectionKey.OP_WRITE);
while (selector.select() > 0) {
if (key.isReadable()) {
// read from channel when server sends data
read();
}
if (key.isWritable()) {
// write
Random r = new Random(500);
write("b", r.nextInt(), r.nextInt());
for (int i = 0; i < 10; i++) {
// write a message to server after 1 second
Thread.sleep(1000);
write("m", r.nextInt(), r.nextInt());
}
write("e", r.nextInt(), r.nextInt());
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void write(String action, int x, int y) throws IOException {
String msg = String.format("%s:%d:%d:%d", action, id, x, y);
int r=outbuf.remaining();
outbuf.put(msg.getBytes());
int rBytes = outbuf.remaining();
boolean connected = socketChannel.isConnected();
Socket sock = socketChannel.socket();
if (connected && sock.isConnected() && !sock.isOutputShutdown())
>>>>>>>>>> socketChannel.write(outbuf);
else
System.out.println("Connection broken!");
System.out.printf("Client %d told server:%s\n", id, msg);
//outbuf.clear();
}
... //read omitted here
After putting stuff into a Buffer, or reading stuff into it you have to flip the buffer to write or get the data from it. Check the flip() method in the Buffer class. The docs say
Flips this buffer. The limit is set to the current position and then the position is set to zero. If the mark is defined then it is discarded.
After a sequence of channel-read or put operations, invoke this method to prepare for a sequence of channel-write or relative get operations.
So adding a buffer.flip() after the put should do the trick :)
Good morning! I have these VideoChatRTP class I got here (I just added the main) and an EncodingUtilClass class from here.
VideoChatRTP.java
import javax.media.rtp.*;
import javax.media.*;
import javax.media.protocol.*;
import java.awt.Image;
import java.io.*;
import java.net.*;
import com.sun.media.ui.*;
/**
* A RTP over UDP player which will receive RTP UDP packets and stream
* them to the JMF RTP Player or (RTPSM) which is not aware of the
* underlying network/transport protocol. This sample uses the
* interfaces defined in javax.media.rtp.RTPSocket and its related classes.
*/
public class VideoChatRTP implements ControllerListener{
/**
* RTP Session address, multicast, unicast or broadcast address
*/
String address = "";
/**
* RTP Session port
*/
int port = 49200;
/**en
* Media Type i.e. one of audio or video
*/
String media = "video";
/////////////DO NOT CHANGE ANYTHING BELOW THIS LINE//////////////////////////
// our main rtpsocket abstraction to which we will create and send
// to the Manager for appropriate handler creation
RTPSocket rtpsocket = null;
// the control RTPIODataSource of the RTPSocket above
RTPPushDataSource rtcpsource = null;
// The GUI to handle our player
PlayerWindow playerWindow;
// The handler created for our RTP session, as returned by the Manager
Player player = null;
// maximum size of buffer for UDP receive from the sockets
private int maxsize = 2000;
UDPHandler rtp = null;
UDPHandler rtcp = null;
public VideoChatRTP(String address, int port){
this.address = address;
this.port = port;
try {
// create the RTPSocket
rtpsocket = new RTPSocket();
// set its content type : rtpraw/video for a video session and
// rtpraw/audio for an audio session
String content = "rtpraw/" + media;
rtpsocket.setContentType(content);
// set the RTP Session address and port of the RTP data
rtp = new UDPHandler(address, port);
// set the above UDP Handler to be the sourcestream of the rtpsocket
rtpsocket.setOutputStream(rtp);
// set the RTP Session address and port of the RTCP data
rtcp = new UDPHandler(address, port +1);
// get a handle over the RTCP Datasource so that we can set
// the sourcestream and deststream of this source to the rtcp
// udp handler we created above.
rtcpsource = rtpsocket.getControlChannel();
// Since we intend to send RTCP packets from the network to
// RTPSM and vice-versa, we need to set the RTCP UDP handler
// as both the input and output stream of the rtcpsource.
rtcpsource.setOutputStream(rtcp);
rtcpsource.setInputStream(rtcp);
// set the dynamic info on the RTPSocket, so that we can
// playback DVI as well
if (media.equals("audio"))
EncodingUtil.Init(rtpsocket);
// start & connect the RTP socket data source before creating
// the player
try{
rtpsocket.connect();
player = Manager.createPlayer(rtpsocket);
//rtpsocket.start();
} catch (NoPlayerException e){
System.err.println(e.getMessage());
// e.printStackTrace();
return;
}
catch (IOException e){
System.err.println(e.getMessage());
// e.printStackTrace();
return;
}
if (player != null){
player.addControllerListener(this);
// send this player to out playerwindow
playerWindow = new PlayerWindow(player);
}
}
catch (Exception e) { System.out.println("could not connect to RTP stream."); }
}
public synchronized void controllerUpdate(ControllerEvent ce) {
if ((ce instanceof DeallocateEvent) ||
(ce instanceof ControllerErrorEvent)){
// stop udp handlers
if (rtp != null)
rtp.close();
if (rtcp != null)
rtcp.close();
}
}
// method used by inner class UDPHandler to open a datagram or
// multicast socket as the case maybe
private DatagramSocket InitSocket(String sockaddress, int sockport){
InetAddress addr = null;
DatagramSocket sock = null;
try{
addr = InetAddress.getByName(sockaddress);
if (addr.isMulticastAddress()){
MulticastSocket msock = new MulticastSocket(sockport);
msock.joinGroup(addr);
sock = (DatagramSocket)msock;
}else{
sock = new
DatagramSocket(sockport,addr);
}
return sock;
}
catch (SocketException e){
e.printStackTrace();
return null;
}
catch (UnknownHostException e){
e.printStackTrace();
return null;
}
catch (IOException e){
e.printStackTrace();
return null;
}
}// end of InitSocket
//INNER CLASS UDPHandler which will receive UDP RTP Packets and
//stream them to the handler of the sources stream. IN case of
//RTCP , it will also accept RTCP packets and send them on the
//underlying network.
public class UDPHandler extends Thread implements PushSourceStream, OutputDataStream{
DatagramSocket mysock = null;
DatagramPacket dp = null;
SourceTransferHandler outputhandler = null;
String myaddress = null;
int myport;
boolean closed = false;
// in the constructor we open the socket and create the main
// UDPHandler thread.
public UDPHandler(String haddress, int hport){
myaddress = haddress;
myport = hport;
mysock = InitSocket(myaddress,myport);
setDaemon(true);
start();
}
// the main thread simply receives RTP data packets from the
// network and transfer's this data to the output handler of
// this stream.
public void run(){
int len;
while(true){
if (closed){
cleanup();
return;
}
try{
do{
dp = new DatagramPacket(new byte[maxsize],maxsize);
mysock.receive(dp);
if (closed){
cleanup();
return;
}
len = dp.getLength();
if (len > (maxsize >> 1))
maxsize = len << 1;
}
while (len >= dp.getData().length);
}catch (Exception e){
cleanup();
return;
}
if (outputhandler != null)
outputhandler.transferData(this);
}
}
public void close(){
closed = true;
}
private void cleanup(){
mysock.close();
stop();
}
// methods of PushSourceStream
public Object[] getControls() {
return new Object[0];
}
public Object getControl(String controlName) {
return null;
}
public ContentDescriptor getContentDescriptor(){
return null;
}
public long getContentLength(){
return SourceStream.LENGTH_UNKNOWN;
}
public boolean endOfStream(){
return false;
}
// method by which data is transferred from the underlying
// network to the RTPSM.
public int read(byte buffer[], int offset, int length) {
System.arraycopy(dp.getData(),
0,
buffer,
offset,
dp.getLength());
return dp.getData().length;
}
public int getMinimumTransferSize(){
return dp.getLength();
}
public void setTransferHandler(SourceTransferHandler transferHandler){
this.outputhandler = transferHandler;
}
// methods of PushDestStream used by teh RTPSM to transfer
// data to the underlying network.
public int write(byte[] buffer, int offset, int length) {
InetAddress addr = null;
try{
addr = InetAddress.getByName(myaddress);
}catch (UnknownHostException e){
}
DatagramPacket dp = new DatagramPacket(buffer,length,addr,myport);
try{
mysock.send(dp);
}catch (IOException e){}
return dp.getLength();
}
}
public static void main(String[] args)
{
try
{
new VideoChatRTP("239.1.2.3", 1234);
}
catch (Exception ex)
{
ex.printStackTrace();
System.exit(0);
}
}
}
EncodingUtilClass.java
import javax.media.rtp.RTPPushDataSource;
import javax.media.rtp.RTPControl;
import javax.media.rtp.RTPSocket;
import javax.media.rtp.SessionManager;
import javax.media.Format;
import javax.media.format.AudioFormat;
public class EncodingUtil {
private static final String rtpcontrol = "javax.media.rtp.RTPControl";
public static void Init(RTPPushDataSource ds){
RTPControl control = (RTPControl)ds.getControl(rtpcontrol);
if (control == null){
//System.out.println("The datasource " + ds +
// "\n does not support the RTPControl interface..." +
// "\nexiting from EncodingUtil.Init()");
return;
}
Init(control);
}
public static void Init(SessionManager mgr){
Format format = null;
if (mgr == null)
return;
format = new AudioFormat(AudioFormat.DVI, 44100, 8, 1);
mgr.addFormat(format, 18);
}
public static void Init(RTPControl control){
Format format = null;
if (control == null)
return;
format = new AudioFormat(AudioFormat.DVI, 44100, 8, 1);
control.addFormat(format, 18);
}
}
When I run the program, it catches the NoPlayerException and prints Cannot find a Player for: javax.media.rtp.RTPSocket#1eb0.
Whole error message if printStackTrace:
Cannot find a Player for: javax.media.rtp.RTPSocket#1dee400
javax.media.NoPlayerException: Cannot find a Player for: javax.media.rtp.RTPSocket#1dee400
at javax.media.Manager.createPlayerForSource(Manager.java:1512)
at javax.media.Manager.createPlayer(Manager.java:500)
at VideoChatRTP.<init>(VideoChatRTP.java:80)
at VideoChatRTP.main(VideoChatRTP.java:280)
I have taken into consideration the JAR files and put the jmf.properties in the same folder where the JAR files are. Please help me fix this. I'm still learning Java. Thank you in advance.
For the purpose of writing an instant messenger program, I am trying to make up a simple server class which will run in its own thread.
What the server should do
accept connections from / connect to other instances of the server and associate the selection keys for the connections in Map<Integer, SelectionKey> keys wit an ID so the messenger thread can access the connections by ID
read from / write to connections
store incoming messages in a queue
messenger thread can
fetch incoming messages
queue messages to be sent : send_message(int id, String msg)
My current approach is based mainly on this example: A simple non-blocking Echo server with Java nio.
I also used Using a Selector to Manage Non-Blocking Sockets and the realted pages to learn about non-blocking sockets and selectors.
Current code
Suggestions by EJP implemented
small changes
package snserver;
/* imports */
//class SNServer (Simple non-blocking Server)
public class SNServer extends Thread {
private int port;
private Selector selector;
private ConcurrentMap<Integer, SelectionKey> keys; // ID -> associated key
private ConcurrentMap<SocketChannel,List<byte[]>> dataMap_out;
ConcurrentLinkedQueue<String> in_msg; //incoming messages to be fetched by messenger thread
public SNServer(int port) {
this.port = port;
dataMap_out = new ConcurrentHashMap<SocketChannel, List<byte[]>>();
keys = new ConcurrentHashMap<Integer, SelectionKey>();
}
public void start_server() throws IOException {
// create selector and channel
this.selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
// bind to port
InetSocketAddress listenAddr = new InetSocketAddress((InetAddress)null, this.port);
serverChannel.socket().bind(listenAddr);
serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
log("Echo server ready. Ctrl-C to stop.");
// processing
while (true) {
// wait for events
this.selector.select();
// wakeup to work on selected keys
Iterator keys = this.selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey) keys.next();
// this is necessary to prevent the same key from coming up
// again the next time around.
keys.remove();
if (! key.isValid()) {
continue;
}
if (key.isAcceptable()) {
this.accept(key);
}
else if (key.isReadable()) {
this.read(key);
}
else if (key.isWritable()) {
this.write(key);
}
else if(key.isConnectable()) {
this.connect(key);
}
}
}
}
private void accept(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
channel.configureBlocking(false);
send_message(key, "Welcome."); //DEBUG
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
log("Connected to: " + remoteAddr);
// register channel with selector for further IO
dataMap_out.put(channel, new ArrayList<byte[]>());
channel.register(this.selector, SelectionKey.OP_READ);
//store key in 'keys' to be accessable by ID from messenger thread //TODO first get ID
keys.put(0, key);
}
//TODO verify, test
public void init_connect(String addr, int port){
try {
SocketChannel channel = createSocketChannel(addr, port);
channel.register(this.selector, channel.validOps()/*, SelectionKey.OP_?*/);
}
catch (IOException e) {
//TODO handle
}
}
//TODO verify, test
private void connect(SelectionKey key) {
SocketChannel channel = (SocketChannel) key.channel();
try {
channel.finishConnect(); //try to finish connection - if 'false' is returned keep 'OP_CONNECT' registered
//store key in 'keys' to be accessable by ID from messenger thread //TODO first get ID
keys.put(0, key);
}
catch (IOException e0) {
try {
//TODO handle ok?
channel.close();
}
catch (IOException e1) {
//TODO handle
}
}
}
private void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(8192);
int numRead = -1;
try {
numRead = channel.read(buffer);
}
catch (IOException e) {
e.printStackTrace();
}
if (numRead == -1) {
this.dataMap_out.remove(channel);
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
log("Connection closed by client: " + remoteAddr); //TODO handle
channel.close();
return;
}
byte[] data = new byte[numRead];
System.arraycopy(buffer.array(), 0, data, 0, numRead);
in_msg.add(new String(data, "utf-8"));
}
private void write(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
List<byte[]> pendingData = this.dataMap_out.get(channel);
Iterator<byte[]> items = pendingData.iterator();
while (items.hasNext()) {
byte[] item = items.next();
items.remove();
//TODO is this correct? -> re-doing write in loop with same buffer object
ByteBuffer buffer = ByteBuffer.wrap(item);
int bytes_to_write = buffer.capacity();
while (bytes_to_write > 0) {
bytes_to_write -= channel.write(buffer);
}
}
key.interestOps(SelectionKey.OP_READ);
}
public void queue_data(SelectionKey key, byte[] data) {
SocketChannel channel = (SocketChannel) key.channel();
List<byte[]> pendingData = this.dataMap_out.get(channel);
key.interestOps(SelectionKey.OP_WRITE);
pendingData.add(data);
}
public void send_message(int id, String msg) {
SelectionKey key = keys.get(id);
if (key != null)
send_message(key, msg);
//else
//TODO handle
}
public void send_message(SelectionKey key, String msg) {
try {
queue_data(key, msg.getBytes("utf-8"));
}
catch (UnsupportedEncodingException ex) {
//is not thrown: utf-8 is always defined
}
}
public String get_message() {
return in_msg.poll();
}
private static void log(String s) {
System.out.println(s);
}
#Override
public void run() {
try {
start_server();
}
catch (IOException e) {
System.out.println("IOException: " + e);
//TODO handle exception
}
}
// Creates a non-blocking socket channel for the specified host name and port.
// connect() is called on the new channel before it is returned.
public static SocketChannel createSocketChannel(String hostName, int port) throws IOException {
// Create a non-blocking socket channel
SocketChannel sChannel = SocketChannel.open();
sChannel.configureBlocking(false);
// Send a connection request to the server; this method is non-blocking
sChannel.connect(new InetSocketAddress(hostName, port));
return sChannel;
}
}
My question: Is the above code correct and good or what should I change? How do I implement the requirements I mentioned above correctly? Also note my "TODO"s.
Thank you for any help!
There are several problems here.
You aren't checking the result of write(). It can return anything from zero up. You may have to re-do it more than once.
If finishConnect() returns false it isn't an error, it just hasn't finished yet, so just leave OP_CONNECT registered and wait for it to fire (again). The only validOps() for a SocketChannel you have just created via SocketChannel.open() is OP_CONNECT. If finishConnect() throws an Exception, that's an error, and you should close the channel.
Closing a channel cancels the key, you don't have to cancel it yourself.
Generally you should use null as the local InetAddress when binding.