Essentially what I'm trying to do is have a client (with an unknown IP) connect via socket to a server, and have the server send a BufferedImage back to the client every x seconds.
I understand how this can be done with a known client IP, but not with an unknown. A simple example would be awesome, thanks.
Example with known IP:
BufferedImage scr = getImage();
Socket sock = new Socket(ip, 123456); //unknown IP
byte[] mybytearray = new byte[1024];
InputStream is = sock.getInputStream();
FileOutputStream fos = new FileOutputStream(scr);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int bytesRead = is.read(mybytearray, 0, mybytearray.length);
bos.write(mybytearray, 0, bytesRead);
bos.close();
sock.close();
Also, if anyone could show me where I could loop it to keep sending the file, that would be awesome.
I whipped up some sample server client connection. Basically you just define your server to use your localhost then port forward or open the server port depending on what your network configuration is. Thers lots of tutorials about that online. On your client you need to either know your external or internal IP address depending on where your connecting from.
This sample just uses your localhost and sends a file from your harddrive, but I wrote it specifically with the intended use of adding in any InputStream or OutputStream so you can adapt this to reading or writing an image. Most servers you will just bind your ip address to 127.0.0.1. When connection to the server outside your local network you will need to find your external IP address. You can find this on websites like whatsmyip.org.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
public class SendFileExample {
public static void main(String [] args) throws IOException {
System.out.print("Type 's' for server or 'c' for client: ");
char c = (char) System.in.read();
if(Character.toLowerCase(c) == 's') {
createServer();
} else if(Character.toLowerCase(c) == 'c') {
createClient();
}
}
public static void createServer() throws IOException {
// create a server to listen on port 12345
ServerSocket socket = new ServerSocket(12345, 0, InetAddress.getByName("127.0.0.1"));
System.out.println("Server started on " + socket.getInetAddress().getHostAddress() + ":" + socket.getLocalPort() + ",\nWaiting for client to connect.");
Socket clientConnection = socket.accept();
System.out.println("Client accepted from "+clientConnection.getInetAddress().getHostAddress()+", sending file");
pipeStreams(new FileInputStream(new File("c:\\from.txt")), clientConnection.getOutputStream(), 1024);
System.out.println("File sent, closing out connection");
clientConnection.close();
socket.close();
}
public static void createClient() throws IOException {
System.out.println("Connecting to server.");
Socket socket = new Socket();
// connect to an address, this is the server address (which you have to know)
socket.connect(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 12345));
// read all bytes from the socket
System.out.println("Success, retreiving file.");
pipeStreams(socket.getInputStream(), new FileOutputStream(new File("c:\\to.txt")), 1024);
System.out.println("Done, file sent. Closing connection");
socket.close();
}
/**
* writes all bytes from inputStream to outputStream
* #param source
* #param out
* #throws IOException
*/
public static void pipeStreams(java.io.InputStream source, java.io.OutputStream destination, int bufferSize) throws IOException {
// 16kb buffer
byte [] buffer = new byte[bufferSize];
int read = 0;
while((read=source.read(buffer)) != -1) {
destination.write(buffer, 0, read);
}
destination.flush();
destination.close();
source.close();
}
}
I think the cleanest way to achieve this would be to have the client connect every x seconds to the server, and pull the image from the socket's stream.
If you want the server to have the initiative, have the client connect to the server, and then keep the socket open to send images every x second. This means that the client must be ready to read the images as they come. It also means that the length of an image must be sent before it's content, since the image is not terminated by the end of the stream.
Related
Good day, I'm a beginner developer and I'm trying to write a client-server application. My application should work like this, it checks if the file has been modified and if so, it is sent to the server, the question is how to organize this?
I did it through the socket, but if I get a deadlock, I will be grateful for any comments, I attach the code further
Server
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocket serverSocket;
Socket client;
BufferedInputStream clientInputStream;
ByteArrayOutputStream byteArrayOutputStream;
StringBuilder fileName;
byte[] mass;
{
fileName = new StringBuilder("this.txt"); // give name for file
serverSocket = new ServerSocket(5000); // open server in 5000 port
mass = new byte[1]; // create new buffer array
client = serverSocket.accept(); // waiting connect
clientInputStream = new BufferedInputStream(client.getInputStream()); // to accept byte's array
byteArrayOutputStream = new ByteArrayOutputStream(); // to write byte array in file
}
{
BufferedOutputStream bufferedOutputStream;
int bytesRead;
// FIXME: 02.07.2022 первый файл
do {
byteArrayOutputStream.write(mass, 0, mass.length); // write by one byte in array
bytesRead = clientInputStream.read(mass, 0, mass.length);
} while (bytesRead != -1);
FileOutputStream writer = new FileOutputStream(fileName.toString()); // stream for file write
bufferedOutputStream = new BufferedOutputStream(writer);
bufferedOutputStream.write(byteArrayOutputStream.toByteArray(), 0, byteArrayOutputStream.toByteArray().length); // write file
System.out.println("first f");
bufferedOutputStream.flush();
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
bufferedWriter.write("success"); // if file send success then write string in socket and send to client
}
{
serverSocket.close();
client.close();
clientInputStream.close();
byteArrayOutputStream.close();
}
}
}
And Client
import java.io.*;
import java.net.Socket;
public class Client {
private final static String serverIP = "192.168.0.47"; // server ip address
private final static int serverPort = 5000; // server port address
private final static String fileOutput = "first.txt"; // first file to send
private final static String fileOutput1 = "second.txt"; // second file to send
public static void main(String[] args) throws IOException, InterruptedException{
Socket client = new Socket(serverIP,serverPort); // make new connect with my server
BufferedInputStream inputFile = new BufferedInputStream(new FileInputStream(fileOutput)); // convert file to array bytes
// FIXME: 02.07.2022 first file
BufferedOutputStream clientSocketOutputWriter = new BufferedOutputStream(client.getOutputStream()); // this is the stream to send to the socket
byte[] massByte = inputFile.readAllBytes(); // this going on convert file to array bytes
clientSocketOutputWriter.write(massByte,0, massByte.length); // send array bytes to server
System.out.println("first file send");
//----------------if i remove this two string, program begin work successfully but i can't send several file because this beging deadlock
// BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream()));
// String s = bufferedReader.readLine();
//----------------
System.out.println("the end");
clientSocketOutputWriter.close();
client.close();
inputFile.close();
}
}
If the TCP connection isn't closed,The variable bytesRead will never be -1 because the code clientInputStream.read(mass, 0, mass.length); keeps executing and waiting for data.If you want to send a file,you should send its length first then you can call byte[] data=clientInputStream.readNBytes(its length),or put its data into a object and use ObjectInputStream and ObjectOutputStream to send the object.
The variable bytesRead isn't -1 means the server cannot leave the loop,so the server cannot put the data which it has just received into a file.Just use writer.write(mass); instead of byteArrayOutputStream.write(mass, 0, mass.length);,write the data to the file directly then stop running the program can solve the problem too.
I am trying to create a client/server program that allows a server and client to send files to each other. I created the sockets, and connected the client to the server. I am doing this one the same computer for now. if it is perfected, i will take it to another computer and try it.
My problem is that the file is transferred successfully but it is corrupt. the file received is corrupt, but the original is okay. I've had problems with socket exception where the socket keeps resetting after sending the file, but I've managed to solve that problem. Now the file is sent, but it is not complete.
The size of the file received is smaller than the size of the file sent, and this causes the received file not work. I sent a pdf file over the network. the original was about 695kb, but the received file was 688kb, and this caused the document to be corrupt. I also tried sending a video, and had the same result. the received file is smaller than the sent file.
I have checked the program, but I can't see where the problem is coming from.
The sending method i try to implement is the zero-copy method, where the data from the file is sent directly to the socket, from where it is read directly to the file. i did not use the other method where it is stored in a buffer before it is sent to the output stream. This is because I want to be able to use the program to send large files. Large files will fill up the java heap memory, and besides this method is faster.
buffer method:
....
File file = new File("path to file);
BufferedInputStream = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
byte[] buf = new byte[length];
in.read(buf, 0, buf.length);
out.write(buf, 0, buf.length);
....
I did not use this buffer method. Here is my code. This is the file server
import java.io.*;
import java.net.*;
import javax.swing.JFileChooser;
public class ShareServer {
public static void main(String args[]) {
int port = 4991;
ServerSocket server;
Socket socket = null;
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
server = new ServerSocket(port);
System.out.println("Waiting for connection request..");
socket = server.accept();
System.out.println("Connected to " + socket.getInetAddress().getHostName());
JFileChooser fc = new JFileChooser();
File file = null;
if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION)
file = fc.getSelectedFile();
// send out the reference of the file using writeObject() method
new ObjectOutputStream(socket.getOutputStream()).writeObject(file);
in = new BufferedInputStream(new FileInputStream(file));
out = new BufferedOutputStream(socket.getOutputStream());
// send file
int b = 1;
while (b != -1){
b = in.read();
out.write(b);
}
System.out.println(file.getName() + " has been sent successfully!");
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
try {
in.close();
out.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Here is the Client class:
import java.io.*;
import java.net.*;
import javax.swing.JOptionPane;
public class ShareClient {
public ShareClient() {
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
String host = InetAddress.getLocalHost().getHostAddress();
Socket socket = new Socket(host, 4991);
System.out.println("Connected to " + host);
// receive the file object. this does not contain the file data
File refFile = (File) new ObjectInputStream(socket.getInputStream()).readObject();
System.out.println("File to receive " + refFile.getName());
// create a new file based on the refFile
File newFile = new File(System.getProperty("user.home") + "/desktop/ReceivedFiles", refFile.getName());
BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(newFile));
System.out.println("Receiving file now...");
int b;
while ((b = in.read()) != -1)
out.write(b);
System.out.println("File has been received successfully!");
socket.close();
}
}
The server and the client classes run successfully without any exceptions, and the file is sent, but it is corrupt. it is incomplete.
Take note that the file sent through the ObjectInput and ObjectOutput streams is not the real file, but just a file object that has all the information of the filie i want to send, but not the binary data of the file.
Please can anybody help me? Why is the file corrupt or incomplete? It is read to the end (when -1) is returned, and all the bytes are sent, but for some reason i can't explain, it ends up being less than the size of the original file.
Currently, you write -1 at the end of the file (that's when you should stop). Something like,
int b = 1;
while (b != -1){
b = in.read();
if (b != -1) {
out.write(b);
}
}
or
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
I have finally got the answer to the problem! It was something so simple! the buffer. I just added a small line of code to flush the socket outputstream buffer in the server, and flush the fileoutputstream buffer in the client program, and that was it! It seems some bytes of data was left in the buffer and that was making the file to be incomplete. this is one of the problems of buffered input and output. if you forget to flush the buffer, you start running into problems.
here's the code:
int b = 1;
while(b != -1){
out.write(b);
}
out.flush(); //this solved my problem. I also did it in the client class
Thank you so much for your answer #Elliot Frisch :)
I am trying to send pdf files over to clients using tcp connections. Somehow, the file is corrupted. I wonder if tcp support non-txt files?
this is a simple server program
import java.io.*;
import java.net.*;
class server{
public static void main (String args[]) throws Exception
{
Socket s = null;
int firsttime=1;
while(true) {
ServerSocket s_sock = new ServerSocket(3248);
Socket socket = s_sock.accept();
BufferedReader bf = new BufferedReader(new InputStreamReader(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
File myFile = new File ("/Users/wchen48/Desktop/123.pdf");
byte [] mybyte = new byte[(int)myFile.length()];
OutputStream os = socket.getOutputStream();
System.out.println("sending......" + mybyte.length + "Byte");
os.write(mybyte,0,mybyte.length);
os.flush();
socket.close();
s_sock.close();
}
} //end of main
This is the client:
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.Socket;
public class client{
public static void main(String[] argv) throws Exception {
Socket sock = new Socket("127.0.0.1", 3248);
byte[] mybytearray = new byte[11536];
InputStream is = sock.getInputStream();
FileOutputStream fos = new
FileOutputStream("/Users/wchen48/Documents/123.pdf");
bos = new BufferedOutputStream(fos);
int bytesRead = is.read(mybytearray, 0, mybytearray.length);
bos.write(mybytearray, 0, mybytearray.length);
bos.close();
sock.close();
}
}
Thank you in advance.
What Zielu and Juned said, plus:
You need loops at both server and client.
At the server, choose a reasonable-size buffer and loop around. Try to read [buffer length] bytes into it from the file. Then USE THE RETURNED VALUE FROM THE READ CALL to both check for errors, (result<0), or end-of-file, (result 0), or use the value in the network write call as the number of bytes to write. DO NOT use the buffer length - it may not be full. When you get 0, close the file and socket, else loop back and read some more.
At the client, choose a reasonable-size buffer and loop around. Try to read [buffer length] bytes into it from the socket. Then USE THE RETURNED VALUE FROM THE READ CALL to both check for errors, (result<0), or socket close, (result 0), or use the value in the file write call as the number of bytes to write. DO NOT use the buffer length - it may not be full. When you get 0, exit the loop and close the file, else loop back and read some more.
you dont read content of your file into the buffer mybyte you later send through socket, so you send 00000....
The most likely reason for getting the corrupted file is the fixed number of bytes(11536) that you are trying to read. You should try to read all the bytes received on the socket rather than fixed number of bytes.
I have a client and a server, a classic example where trying to simulate in very simple way the http protocol. Firstly the client sends data while server prints the data and then the opposite. In the following code, either the server or client blocks, for an unknown reason.
The client sends to the server data, the server receive the data and prints it. But it just blocks after prints the data. If i close the outputstream of the client (out.close()) the client should get the server's data but instead throws IOException with the message: Socket closed.
My question is why is it blocking? Do i have to trigger the output with EOS?
CLIENT
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
import java.io.IOException;
import java.io.*;
public class block_client_webclient
{
public static void main(String [] args)
{
try{
Socket s = new Socket("localhost", 8080);
OutputStreamWriter out = new OutputStreamWriter(s.getOutputStream());
InputStreamReader in = new InputStreamReader(s.getInputStream());
//WRITE
out.write("GET / HTTP/1.1\r\nUser-agent: Agent 2.0 Browser\r\nAccept: */*\r\nAccept-Language: en-US,en;q=0.5\r\nConnection: keep-alive\r\n\r\n");
out.flush();
out.close();
if (s.isConnected()==true && s.isClosed()==false) System.out.println("OPEN");
else System.out.println("CLOSED");
char[] bin = new char[400];
int r=0;
//READ
while((r=in.read(bin))!=-1) { System.out.println("Input data: "+r+" bytes"); System.out.print(bin); bin= new char[400]; };
System.out.println(r);
s.close();
}
catch (IOException ex) {System.out.println(ex.getMessage());}
}
}
SERVER
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
import java.io.IOException;
import java.io.*;
public class block_server_webserver
{
public static void main(String [] args)
{
while(true)
{
try{
ServerSocket server = new ServerSocket(8080);
Socket connection = server.accept();
try{
OutputStreamWriter out = new OutputStreamWriter( connection.getOutputStream());
InputStreamReader in = new InputStreamReader( connection.getInputStream());
char[] bin = new char[400];
int r=0;
int readsofar=0;
//READ
while((r=in.read(bin))!=-1) { System.out.print(bin); bin= new char[400]; };
System.out.println("END");
//WRITE
out.write("Server: BlockServer 1.0\r\nHost: 192.168.1.1\r\n\r\n");
out.flush();
System.out.println("Just written data to "+connection.getRemoteSocketAddress());
connection.close();
} catch (IOException ex) { connection.close();}
}catch (IOException ex) {}
}
}
}
Doing out.close() results in a socket closed exception because when you close an input/output stream, the socket related to it also gets closed. That means that in your client program, you won't be able to read in after closing the socket.
First, take out out.close() Instead of checking for -1, try creating an "exit"string. If the server reads the exit string, recognize that it needs to stop being in the while loop and just break; (can be done with a simple if statement). Also, usually I would use a buffered reader, String x and readLine() instead of a char array + read().
while (true) {
in.read(bin);
if (bin[0] == '^') { // or something like that
break;
}
System.out.print(bin);
bin = new char[400];
}
Only close() streams when you are finished with the socket entirely.
The server should not wait for the socket to get closed (because, once that happens, it won't be able to write back).
Instead of reading until it returns -1 (closed), it should read to the end of request (two new lines in case of http headers), and start sending output as soon as that happens, then, possibly, close the connection to inform the client that there is no more data (in http, the connection usually stays open for a while, the client knows to stop reading after enough bytes have been received according to Content-length header).
I am trying to create a p2p connection. This is a just test app that i have check but seems like it does not work over internet.
This is the java code that I am using on my pc to send a datagram to my friend:
'
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.*;
import javax.net.ssl.SSLServerSocket;
public class j2{
public static void main(String[] args) throws Exception {
InetAddress IPAddress = InetAddress.getByName("my friend's public IP");
DatagramSocket clientSocket = new DatagramSocket(3456);
System.out.println("Sending data");
String datamsg = "hello ";
byte[] sendData = datamsg.getBytes("UTF-8");
byte [] receiveData = new byte[10];
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, 7890);
int i = 500;
//incase if some packets are lost
while(i-->1)
{
clientSocket.send(sendPacket);
}
System.out.println("Data sent");
System.out.println(clientSocket.isClosed());
clientSocket.close();
}
}
'
//My friend uses this app to receive a data gram:
// port 7890 is used to send data gram and create a hole. The same is used to receice data.
'
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.*;
import javax.net.ssl.SSLServerSocket;
public class j1{
public static void main(String[] args) throws Exception {
InetAddress IPAddress = InetAddress.getByName("any ip"); //does not matter as it is used to open a hole
DatagramSocket clientSocket = new DatagramSocket(7890);
System.out.println("Sending data");
String datamsg = "hello ";
byte[] sendData = datamsg.getBytes("UTF-8");
byte [] receiveData = new byte[10];
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, 5000);
int i = 500;
while(i-->1)
{
clientSocket.send(sendPacket);
}
System.out.println("Data sent");
System.out.println(clientSocket.isClosed());
DatagramPacket receivePacket = new DatagramPacket(sendData, sendData.length);
clientSocket.receive(receivePacket);
System.out.println("Packet received");
String msg = new String(receivePacket.getData());
clientSocket.close();
}
}'
// I am not using a stun server as i already know my friends public ip address. We both have disabled our firewall as well.
Your approach is not the most reliable way to do NAT hole punching. At best it will "sometimes work".
Here's some suggestions:
Don't hardcode port numbers. Let your UDP socket code pick a random port number (i.e. port=0) and use a STUN server (or equivalent) to ascertain your public IP address and public port mapping for this local socket.
Use a reliable service to exchange the IP/port. Since you are just trying to get a single packet to go through, start with using a phone to verbally exchange this information could suffice.
You don't need to send 500 packets all at once. The firewall code on
the remote NAT might see this as a DOS attack and block everything.
Try sending like 1 a second.
You should be simultaneously listening and sending periodic packets at the same time
when trying to do the connectivity check that your code is doing.
(e.g. two seperate threads or periodic polling).
Don't close the socket until after connectivity is confirmed by both endpoints. As you have it now, your first program closes the socket immediately after it sends the packet burst.
Read my full answer here: https://stackoverflow.com/a/8524609/104458