C# send image over HTTP - c#

I have a small HTTP-Server here written in C# and until now I only needed to send raw text back to the sender. But now I have to send a JPG-Image and I dont unterstand how.
this is what I have now:
// Read the HTTP Request
Byte[] bReceive = new Byte[MAXBUFFERSIZE];
int i = socket.Receive(bReceive, bReceive.Length, 0);
//Convert Byte to String
string sBuffer = Encoding.ASCII.GetString(bReceive);
// Look for HTTP request
iStartPos = sBuffer.IndexOf("HTTP", 1);
// Extract the Command without GET_/ at the beginning and _HTTP at the end
sRequest = sBuffer.Substring(5, iStartPos - 1 - 5);
String answer = handleRequest(sRequest);
// Send the response
socket.Send(Encoding.UTF8.GetBytes(answer));
I think I have to do some kind of filestream instead of a string but I really have no glue..

Do you want to send from file or Bitmap object?
MemoryStream myMemoryStream = new MemoryStream();
myImage.Save(myMemoryStream);
myMemoryStream.Position = 0;
EDIT
// Send the response
SendVarData(socket,memoryStream.ToArray());
for sending MemoryStream by socket you can use this method given here
private static int SendVarData(Socket s, byte[] data)
{
int total = 0;
int size = data.Length;
int dataleft = size;
int sent;
byte[] datasize = new byte[4];
datasize = BitConverter.GetBytes(size);
sent = s.Send(datasize);
while (total < size)
{
sent = s.Send(data, total, dataleft, SocketFlags.None);
total += sent;
dataleft -= sent;
}
return total;
}

Why did you create a httpserver by yourself? Why not use a open source one? For instance mine: http://webserver.codeplex.com
public class Example
{
private HttpListener _listener;
public void StartTutorial()
{
_listener = HttpListener.Create(IPAddress.Any, 8081);
_listener.RequestReceived += OnRequest;
_listener.Start(5);
}
private void OnRequest(object source, RequestEventArgs args)
{
IHttpClientContext context = (IHttpClientContext)source;
IHttpRequest request = args.Request;
IHttpResponse response = request.CreateResponse(context);
response.Body = new FileStream("Path\\to\\file.jpg");
response.ContentType = "image\jpeg";
response.Send();
}
}
Edit
If you really want do do it by yourself:
string responseHeaders = "HTTP/1.1 200 The file is coming right up!\r\n" +
"Server: MyOwnServer\r\n" +
"Content-Length: " + new FileInfo("C:\\image.jpg").Length + "\r\n" +
"Content-Type: image/jpeg\r\n" +
"Content-Disposition: inline;filename=\"image.jpg;\"\r\n" +
"\r\n";
//headers should ALWAYS be ascii. Never UTF8
var headers = Encoding.ASCII.GetBytes(responseHeaders);
socket.Send(headers, 0, headers.Length);
socket.SendFile("C:\\image.jpg");

Related

Basic HttpServer | How to read data from NetworkStream properly

I am trying to make a basic webserver for practice, however i think that i'm not reading from the NetworkStream properly... On the picture bellow you can see that request 2 and 3 do not have 2 newlines \r\n\r\n separating them , which means that i'm reading less data.
Also the requests hang for no obvious reason. I think that I am properly closing the stream and the client, by using networkStream.Close(); and tcpClient.Close();
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace HttpServer
{
class Program
{
static void Main(string[] args)
{
var webserver = new Program();
webserver.Listen(12345);
}
public static void ProcessTcpClientAsync(TcpClient tcpClient)
{
// Get a stream object for reading and writing
NetworkStream networkStream = tcpClient.GetStream();
byte[] buffer = readByteBufferFromNetworkStream(networkStream);
string request = Encoding.UTF8.GetString(buffer);
Console.WriteLine(request);
const string HTTPNewLine = "\r\n";
string html = "<h1>Hello from TestServer</h1>";
string response = "HTTP/1.1 200 OK" + HTTPNewLine;
response += "Server: TestServer 2020" + HTTPNewLine;
response += "Content-Type: text/html; charset=utf-8" + HTTPNewLine;
response += "Content-Length: " + html.Length + HTTPNewLine;
response += HTTPNewLine;
response += html;
response += HTTPNewLine;
byte[] responseBytes = Encoding.UTF8.GetBytes(response);
networkStream.Write(responseBytes);
networkStream.Close();
tcpClient.Close();
}
public void Listen(Int32 port)
{
//Int32 port = 12345;
IPAddress localaddr = IPAddress.Parse("127.0.0.1");
TcpListener tcpListener = new TcpListener(localaddr, port);
tcpListener.Start();
// Enter the listening loop (service/daemon)
while (true)
{
//# Sync Version for debugging
TcpClient tcpClient = tcpListener.AcceptTcpClient();
ProcessTcpClientAsync(tcpClient);
//tcpListener.AcceptTcpClientAsync().ContinueWith((res) => ProcessTcpClientAsync(res.GetAwaiter().GetResult()));
}
}
public static byte[] readByteBufferFromNetworkStream(NetworkStream networkStream)
{
byte[] buffer = new byte[256];
int bytesRead = 0;
while (true)
{
int currentBytesRead = networkStream.Read(buffer, bytesRead, buffer.Length - bytesRead);
bytesRead += currentBytesRead;
// If we've read less data than the size of our buffer -> it means there is no more information, so break.
if (currentBytesRead < buffer.Length) { break; }
// If we've reached the end of our buffer -> check to see if there's any more information, or break.
if (bytesRead == buffer.Length)
{
int nextByte = networkStream.ReadByte();
// End of stream? If so, we're done.
if (nextByte == -1)
{
break;
}
// Otherwise resize the buffer, put in the byte we've just read and continue.
else
{
byte[] newBuffer = new byte[buffer.Length * 2];
Array.Copy(buffer, newBuffer, buffer.Length);
newBuffer[bytesRead] = (byte)nextByte;
buffer = newBuffer;
bytesRead++;
}
}
}
// Buffer is now too big. Shrink it.
byte[] resultBuffer = new byte[bytesRead];
Array.Copy(buffer, resultBuffer, bytesRead);
return resultBuffer;
}
}
}
It's part of the standard for this type of response to seperate the header from the body content.
See https://www.rfc-editor.org/rfc/rfc7230#section-3
All HTTP/1.1 messages consist of a start-line followed by a sequence
of octets in a format similar to the Internet Message Format
[RFC5322]: zero or more header fields (collectively referred to as
the "headers" or the "header section"), an empty line indicating the
end of the header section, and an optional message body.
HTTP-message = start-line
*( header-field CRLF )
CRLF
[ message-body ]
(CRLF above will be treated like \r\n)

sending a message back to client using tcplistener server

i have this c# class
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
class Server
{
const string SERVER_IP = "127.0.0.1";
const int SERVER_PORT = 9998;
TcpListener server = new TcpListener(IPAddress.Parse(SERVER_IP), SERVER_PORT);
public void Start ()
{
server.Start();
Console.WriteLine("Server has started on "+SERVER_IP+":"+SERVER_PORT+".{0}Waiting for a connection...", Environment.NewLine);
TcpClient client;
while (true) // Add your exit flag here
{
client = server.AcceptTcpClient();
Socket Socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
ThreadPool.QueueUserWorkItem(ThreadProc, client);
}
}
private void ThreadProc(object obj)
{
Console.WriteLine("A client connected.");
TcpClient client = (TcpClient)obj;
NetworkStream stream = client.GetStream();
int UnactiveTimePeriod = int.Parse(TimeSpan.FromMinutes(1).TotalMilliseconds.ToString());
//enter to an infinite cycle to be able to handle every change in stream
while (true)
{
while (!stream.DataAvailable) ;
Byte[] bytes = new Byte[client.Available];
stream.Read(bytes, 0, bytes.Length);
// translate bytes of request to string
string data = Encoding.UTF8.GetString(bytes);
if (new Regex("^GET").IsMatch(data)) // Handshaking protocol
{
Byte[] response = Encoding.UTF8.GetBytes("HTTP/1.1 101 Switching Protocols" + Environment.NewLine
+ "Connection: Upgrade" + Environment.NewLine
+ "Upgrade: websocket" + Environment.NewLine
+ "Sec-WebSocket-Accept: " + Convert.ToBase64String(
SHA1.Create().ComputeHash(
Encoding.UTF8.GetBytes(
new Regex("Sec-WebSocket-Key: (.*)").Match(data).Groups[1].Value.Trim() + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
)
)
) + Environment.NewLine
+ Environment.NewLine);
stream.Write(response, 0, response.Length);
}
else
{
string msg = DecodeMessage(bytes);
Console.WriteLine(msg);
stream.Flush();
}
//Console.WriteLine(ByteToString(bytes));
}
}
private string DecodeMessage(byte[] bytes)
{
string incomingData = string.Empty;
byte secondByte = bytes[1];
int dataLength = secondByte & 127;
int indexFirstMask = 2;
if (dataLength == 126)
indexFirstMask = 4;
else if (dataLength == 127)
indexFirstMask = 10;
IEnumerable<byte> keys = bytes.Skip(indexFirstMask).Take(4);
int indexFirstDataByte = indexFirstMask + 4;
byte[] decoded = new byte[bytes.Length - indexFirstDataByte];
for (int i = indexFirstDataByte, j = 0; i < bytes.Length; i++, j++)
{
decoded[j] = (byte)(bytes[i] ^ keys.ElementAt(j % 4));
}
return incomingData = Encoding.UTF8.GetString(decoded, 0, decoded.Length);
}
}
I managed to receive the client messages using this :
string msg = DecodeMessage(bytes);
but how can i send a message from the server back to the client ?
i am trying to build a websocket server but i can't manage to send a msg back to my client
Any help ?
public void SendString(string str)
{
//ns is a NetworkStream class parameter
//logger.Output(">> sendind data to client ...", LogLevel.LL_INFO);
try
{
var buf = Encoding.UTF8.GetBytes(str);
int frameSize = 64;
var parts = buf.Select((b, i) => new { b, i })
.GroupBy(x => x.i / (frameSize - 1))
.Select(x => x.Select(y => y.b).ToArray())
.ToList();
for (int i = 0; i < parts.Count; i++)
{
byte cmd = 0;
if (i == 0) cmd |= 1;
if (i == parts.Count - 1) cmd |= 0x80;
ns.WriteByte(cmd);
ns.WriteByte((byte)parts[i].Length);
ns.Write(parts[i], 0, parts[i].Length);
}
ns.Flush();
}
catch (Exception ex)
{
_srv.LogError(">> " + ex.Message);
}
}
Sora, I think this link should help get you started:
TCP Server/Client Intro
It contains source code for a very simple TCP Server/Client. Notice that after the server Accepts the socket, it reads from the stream, outputs that message, then proceeds to write an Acknowledgement to that stream.
On the client-side: After sending it's message, the client reads bytes from the same stream to get the server's acknowledgement.

Simple async await tcp server design

I'm designing an asynchronous TCP server using async-await approach making use of event-driven .NET environment with the following features:
Request and response functionality.
Server side initiated events.
Does this server design is correct in terms of async-await approach?
Code below covers only basic functionality to judge whether I'm missing something or not.
The server uses COM Type library, which is where PRMasterAutomation class comes from.
public class AsyncServer {
private const int BufferSize = 4096;
private static bool ServerRunning = true;
private List<TcpClient> clients = new List<TcpClient>();
private PRMasterAutomation automation;
public AsyncServer()
{
InitializeComponent();
automation = new PRMasterAutomation();
automation.OnMonitoringEvent += automation_OnMonitoringEvent;
if (!automation.Login("ADMIN", ""))
return;
if (automation.GetPRMasterState() == PRMasterStateEnum.StateIdle)
automation.StartMonitoring();
var tcpServer = new TcpListener(IPAddress.Any, 9001);
tcpServer.Start();
ListenForClients(tcpServer);
}
private async void ListenForClients(TcpListener tcpServer)
{
while (ServerRunning)
{
TcpClient tcpClient = await tcpServer.AcceptTcpClientAsync();
clients.Add(tcpClient);
ProcessClient(tcpClient);
}
}
private async void ProcessClient(TcpClient tcpClient)
{
var stream = tcpClient.GetStream();
var buffer = new byte[BufferSize];
int amountRead;
string xml;
while (ServerRunning)
{
if ((amountRead = await stream.ReadAsync(buffer, 0, BufferSize)) > 0)
{
var message = Encoding.ASCII.GetString(buffer, 0, amountRead);
xml = "";
if (message.Equals("get_users"))
{
xml = automation.GetUsersXml();
}
else if (message.Equals("get_events"))
{
xml = automation.GetEventsHistoryXml("");
}
if (xml.Length > 0)
{
xml += "\r\n";
await stream.WriteAsync(Encoding.ASCII.GetBytes(xml), 0, xml.Length);
}
}
}
}
async void automation_OnMonitoringEvent(DateTime date, DateTime time, int networkID, int readerID, int userID, int groupID, int eventCode, int zoneID, int TandAID, string strEvent, string strAccessPoint, string strUserSource, string strGroup, string strNetwork, string strZone, string strTandAMode)
{
foreach (var c in clients)
{
if (c != null && c.Connected)
{
var stream = c.GetStream();
StringBuilder sb = new StringBuilder();
sb.Append(date.ToString() + ";" + time.ToString() + ";" + networkID.ToString() + ";" + userID.ToString() + "\r\n");
await stream.WriteAsync(Encoding.ASCII.GetBytes(sb.ToString()), 0, sb.Length);
}
}
}
}
Does this server design is correct in terms of async-await approach?
Yes, but not in terms of sockets. ReadAsync will return any number of bytes between 1 and infinite, it will not return messages.
Stream.ReadAsync(): The result value can be less than the number of bytes requested if the number of bytes currently available is less than the requested number, or it can be 0 (zero) if the end of the stream has been reached.
You have to create a buffer and keep reading and adding to that buffer until you know you have received a whole message. This means checking for \r\n in your case. If you encounter such a delimiter, you can extract the message from the buffer and start receiving a new message.

Downloading web site using sockets

I'm trying to download source of the site using sockets. Currently i can download headers and after that i just terminate connection because i don't know how long should I receive data. This is the code:
private void HandleConnect(SocketAsyncEventArgs e)
{
if (e.ConnectSocket != null)
{
// simply start sending
bool completesAsynchronously = e.ConnectSocket.SendAsync(e);
// check if the completed event will be raised.
// if not, invoke the handler manually.
if (!completesAsynchronously)
{
SocketAsyncEventArgs_Completed(e.ConnectSocket, e);
}
}
}
private void HandleReceive(SocketAsyncEventArgs e)
{
string responseL = Encoding.UTF8.GetString(e.Buffer, 0, e.Buffer.Length);
response += responseL;
temp += responseL;
string[] lines = Regex.Split(response, "\r\n\r\n");
if (lines.Length > 1 && header == "")
{
header = lines[0].ToString() + "\r\n";
lines[0] = "";
response = lines.ToString();
}
if (header == "")
{
bool completesAsynchronously = e.ConnectSocket.ReceiveAsync(e);
}
else
{
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
_callback(false, this);
});
}
}
I was trying to search for \r\n but it didn't help :/
Please help!
Thank you in advance :)
I use this code to send headers to the site and then read its content. I hope you find it useful.
ReadStateObject stateObject; //Info below
mytcpclient = new TcpClient();
mytcpclient.Connect(host, port);
mysocket = mytcpclient.Client;
SendHeader(mysocket);//Info below
ns = mytcpclient.GetStream();
if (ns.CanRead)
{
stateObject = new ReadStateObject(ns, 1024);
ns.BeginRead(stateObject.ReadBuffer, 0, stateObject.ReadBuffer.Length, new AsyncCallback(ReadCallBack), stateObject);
}
StateObject is small class used to represent the AsyncState object in BeginRead method:
class ReadStateObject
{
public NetworkStream Stream {get; set;}
public byte[] ReadBuffer;
public ReadStateObject(NetworkStream _stream, int bufferSize)
{
Stream = _stream;
ReadBuffer = new byte[bufferSize];
}
}
And this is a Callback Method used in BeginRead method.
private void ReadCallBack(IAsyncResult result)
{
ReadStateObject stateObject = (ReadStateObject)result.AsyncState;
NetworkStream myNetworkStream = stateObject.Stream;
int numberofbytesread = 0;
StringBuilder sb = new StringBuilder();
numberofbytesread = myNetworkStream.EndRead(result);
sb.Append(Encoding.ASCII.GetString(stateObject.ReadBuffer, 0, numberofbytesread));
/*It seems, if there is no delay, the DataAvailable may not be true even when there are still data to be received from the site, so I added this delay. Any suggestions, how to avoid this are welcome*/
Thread.Sleep(500);
while (myNetworkStream.DataAvailable)
{
byte[] mydata = new byte[1024];
numberofbytesread = myNetworkStream.Read(mydata, 0, mydata.Length);
sb.Append(Encoding.ASCII.GetString(mydata, 0, numberofbytesread));
}
Console.Writeln(sb.ToString());
mytcpclient.Close();
}
And this is where Headers are sent to the site
public void SendHeader(Socket mySocket)
{
String sBuffer = "";
sBuffer = sBuffer + "GET /"+pathquery+" HTTP/1.1" + "\r\n";
sBuffer = sBuffer + "Host: "+ hostname + "\r\n";
sBuffer = sBuffer + "Content-Type: text/html\r\n";
sBuffer = sBuffer + "\r\n";
Byte[] bSendData = Encoding.ASCII.GetBytes(sBuffer);
mySocket.Send(Encoding.ASCII.GetBytes(sBuffer), Encoding.ASCII.GetBytes(sBuffer).Length, 0);
}
Maybe, you should use WebClient or HttpWebRequest instead of sockets.
Using sockets and interpreting Http protocol can be painful.

send complex object using socket in C# but also simple text string

my goal is to send simple text data and also complex data to client application using socket..this is some of my code
private void button1_Click(object sender, EventArgs e)
{
byte[] data;
data = Encoding.ASCII.GetBytes("Welcome!!!");
ns.Write(data, 0, data.Length);
ns.Flush();
List<Person> list = new List<Person>();
for (int a = 0; a < 5; a++)
{
Person person = new Person();
person.Name = textBox1.Text;
person.Age = int.Parse(textBox2.Text);
list.Add(person);
}
BinaryFormatter formatter = new BinaryFormatter();
for (int a = 0; a < 5; a++)
{
formatter.Serialize(ns, list[a]);
}
ns.Flush();
}
and at the client i write this code to add all data to listview
private void AddToListView()
{
while (true && ns.DataAvailable)
{
byte[] data = new byte[1024];
int recv;
recv = ns.Read(data, 0, data.Length);
textFromServer = Encoding.ASCII.GetString(data, 0, recv);
ns.Flush();
listView1.Items.Add(textFromServer);
temp = formatter.Deserialize(ns) as Person;
ns.Flush();
listView1.Items.Add(temp.Name);
listView1.Items.Add(temp.Age);
}
}
but when i debug the application,,nothing happens...if i delete the networkstream read() process,,the application run nicely..the problem is,i not only need to send the user object,,but also simple text to client..can someone help me please?
is it possible using networkstream.read() and binaryformatter.deserialize() at the same time??or,we have to choose one of it?
i mean when just to send/receive simple text,,we use networkstream.read()/write(),,and for complex object we use serialize/deserialize..is it possible??
The problem with what you are doing is that the server and client need to have an agreed upon protocol by which to send and receive data. For instance your Client code will read everything that is available in the first ns.Read call you make. There is nothing that signals the end of one type of data and the beginning of the next, and thus you don't have an agreed upon method by which you are sending/reading data.
Easiest might be for you to encapsulate the data into another object that can contain both strings and objects.
[Serializable]
public class EncapsulatedData{
public EncapsulatedData(){}
public string Message{ get; set; }
public ISerializable Object{ get; set; }
}
And you will set Object to be a ISerializable type of yours, such as Person. Then you should be able to deserialize on the client and check Message or Object.
There are cleaner ways to do this, but the above trick should work for you.
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace ClientServer
{
class ClientServerProgram
{
public static void SendHeader(string sMIMEHeader, int iTotBytes, string sStatusCode, ref Socket mySocket)
{
String sBuffer = "";
// if Mime type is not provided set default to text/html
if (sMIMEHeader.Length == 0)
{
sMIMEHeader = "text/html"; // Default Mime Type is text/html
}
sBuffer = sBuffer + "HTTP/1.1" + sStatusCode + "\r\n";
sBuffer = sBuffer + "Server: cx1193719-b\r\n";
sBuffer = sBuffer + "Content-Type: " + sMIMEHeader + "\r\n";
sBuffer = sBuffer + "Accept-Ranges: bytes\r\n";
sBuffer = sBuffer + "Content-Length: " + iTotBytes + "\r\n\r\n";
Byte[] bSendData = Encoding.ASCII.GetBytes(sBuffer);
mySocket.Send(Encoding.ASCII.GetBytes(sBuffer),Encoding.ASCII.GetBytes(sBuffer).Length, 0);
Console.WriteLine("Total Bytes : " + iTotBytes.ToString());
}
public static void ReceiveCallback(IAsyncResult AsyncCall)
{
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
string messageString = "I am a little busy, come back later!";
Byte[] message = encoding.GetBytes(messageString);
Socket listener = (Socket)AsyncCall.AsyncState;
Socket client = listener.EndAccept(AsyncCall);
Console.WriteLine("Received Connection from {0}", client.RemoteEndPoint);
SendHeader("text/html", message.Length, "202 OK", ref client);
client.Send(message);
Console.WriteLine("Ending the connection");
client.Close();
listener.BeginAccept(new AsyncCallback(ReceiveCallback), listener);
}
public static void Main()
{
IPAddress localAddress = IPAddress.Parse("192.0.127.1");
Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipEndpoint = new IPEndPoint(localAddress, 8080);
listenSocket.Bind(ipEndpoint);
listenSocket.Listen(1);
listenSocket.BeginAccept(new AsyncCallback(ReceiveCallback), listenSocket);
while (true)
{
Console.WriteLine("Waiting....");
Thread.Sleep(1000);
}
}
}

Categories

Resources