Отправить двоичные данные и прочитать их значения с помощью асинхронного программирования сокетов
Я пытаюсь отправить данные на сервер с моего клиента. Клиент отправляет сообщения на сервер, каждое сообщение 36 bytes
и в этом сообщении каждый 4 байт является полем, а в серверной части я должен быть в состоянии обнаружить это поле из сообщения, которое отправляет клиент. Предположим, у меня есть эти данные:
A=21
B=32
c=43
D=55
E=75
F=73
G=12
H=14
M=12
В клиенте я должен отправить эти значения как одно сообщение. Как вы можете видеть, мое сообщение имеет 9 полей, и каждое поле 4 byte integer
и все сообщение 36 byte
,
Таким образом, в серверной части, когда сообщение получено, я должен иметь возможность отделить сообщение и найти значение полей.
В клиентском приложении я использую эту структуру для отправки сообщения на мой сервер:
m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Cet the remote IP address
IPAddress ip = IPAddress.Parse(GetIP());
int iPortNo = System.Convert.ToInt16("12345");
// Create the end point
IPEndPoint ipEnd = new IPEndPoint(ip, iPortNo);
// Connect to the remote host
m_clientSocket.Connect(ipEnd);
if (m_clientSocket.Connected)
{
Object objData = ?!!!!;//My message
byte[] byData = System.Text.Encoding.ASCII.GetBytes(objData.ToString());
if (m_clientSocket != null)
{
m_clientSocket.Send(byData);
}
Thread.Sleep(4000);
}
}
И в серверной части я использую этот код для получения данных:
public void OnDataReceived(IAsyncResult asyn)
{
try
{
SocketPacket socketData = (SocketPacket)asyn.AsyncState;
int iRx = 0;
// Complete the BeginReceive() asynchronous call by EndReceive() method
// which will return the number of characters written to the stream
// by the client
iRx = socketData.m_currentSocket.EndReceive(asyn);
char[] chars = new char[iRx + 1];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(socketData.dataBuffer,
0, iRx, chars, 0);
System.String szData = new System.String(chars);
MessageBox.Show(szData);
// Continue the waiting for data on the Socket
WaitForData(socketData.m_currentSocket);
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n");
}
catch (SocketException se)
{
}
}
Вот другая часть моего кода сервера:
public void OnClientConnect(IAsyncResult asyn)
{
try
{
// Here we complete/end the BeginAccept() asynchronous call
// by calling EndAccept() - which returns the reference to
// a new Socket object
m_workerSocket[m_clientCount] = m_mainSocket.EndAccept(asyn);
// Let the worker Socket do the further processing for the
// just connected client
WaitForData(m_workerSocket[m_clientCount]);
// Now increment the client count
++m_clientCount;
// Display this client connection as a status message on the GUI
String str = String.Format("Client # {0} connected", m_clientCount);
// Since the main Socket is now free, it can go back and wait for
// other clients who are attempting to connect
m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1", "\n OnClientConnection: Socket has been closed\n");
}
}
public class SocketPacket
{
public System.Net.Sockets.Socket m_currentSocket;
public byte[] dataBuffer = new byte[36];
}
В моем form_load
серверной части у меня есть этот код:
ipaddress = GetIP();
// Check the port value
string portStr = "12345";
int port = System.Convert.ToInt32(portStr);
// Create the listening socket...
m_mainSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port);
// Bind to local IP Address...
m_mainSocket.Bind(ipLocal);
// Start listening...
m_mainSocket.Listen(4);
// Create the call back for any client connections...
m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
на самом деле я пытаюсь реализовать эту ссылку:
Моя проблема в том, как я могу отправить свое сообщение в виде 36 байтов через клиента и получить и отделить его от сервера?
Каждые четыре байта - это поле, и я должен иметь возможность получить это значение.
2 ответа
Похоже, все, что вам нужно, это 2 метода для преобразования целых чисел в байтовый массив и наоборот:
byte[] packet = CreateMessage(21,32,43,55,75,73,12,14,12);
//send message
//recv message and get ints back
int[] ints = GetParameters(packet);
....
public byte[] CreateMessage(params int[] parameters)
{
var buf = new byte[parameters.Length * sizeof(int)];
for (int i = 0; i < parameters.Length; i++)
Array.Copy(BitConverter.GetBytes(parameters[i]), 0, buf, i * sizeof(int), sizeof(int));
return buf;
}
public int[] GetParameters(byte[] buf)
{
var ints = new int[buf.Length / sizeof(int)];
for (int i = 0; i < ints.Length; i++)
ints[i] = BitConverter.ToInt32(buf, i * sizeof(int));
return ints;
}
Ты делаешь это неправильно. Если вы хотите передать двоичные данные, не кодируйте их как строку. Есть несколько способов сделать это:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Msg
{
public int A, B, C, D, E, F, G, H, M;
}
Затем используйте Marshal
класс, чтобы получить байты. Тем не менее, вы получите прямой порядок передачи данных.
Другой метод заключается в использовании BinaryWriter
, То же самое, данные с прямым порядком байтов, если вы не конвертируете их самостоятельно или не используете альтернативную версию BinaryWriter
,
Тогда гораздо проще использовать NetworkStream
потому что этот класс будет обрабатывать фрагментацию пакетов для вас. Вот код отправки с использованием BinaryWriter
метод:
using (var stream = new NetworkStream(stocket))
{
var writer = new BinaryWriter(stream);
writer.Write(21);
writer.Write(32);
// etc
}
То же самое на стороне клиента, используя NetworkStream
и BinaryReader
,
NB. Вы можете использовать асинхронный ввод / вывод с NetworkStream
используя функцию async/await.