Converting data to byte

Hello guys, i want to send some data over tcp,
can u do the conversion via static_cast<unsigned char> ?
or can u give me an simple example so i can understand it.

btw does this work?

1
2
3
4
	unsigned char msg[] = "Hello world";

	cout << msg[0] << endl;
	cout << static_cast<int>(static_cast<unsigned>(msg[0])) << endl;


this shoes me a value, but im not sure if its the right one, after using some online converting "String" to "Byte" it shows me some other results.

msg[0] is an unsigned char, and assuming normal ASCII encoding, its 8-bit value is 72 (0x48). I'm not sure I understand the specific problem here?

chars pretty much are bytes, so msg is already in a "byte array" format that you can send over the wire.
https://stackoverflow.com/questions/18670807/sending-and-receiving-stdstring-over-socket
Last edited on
What exacly does this part do? does it convert it to byte array?

1
2
3
4
5
6
7
8
9
10
11
12
unsigned char[] RCON_Command(std::string Command, int ServerData)
{
	unsigned char Packet[static_cast<unsigned char>((13 + Command.length())) + 1];
	Packet[0] = Command.length() + 9; //Packet Size (Integer)
	Packet[4] = 0; //Request Id (Integer)
	Packet[8] = ServerData; //SERVERDATA_EXECCOMMAND / SERVERDATA_AUTH (Integer)
	for (int X = 0; X < Command.length(); X++)
	{
		Packet[12 + X] = System::Text::Encoding::Default->GetBytes(Command[X])[0];
	}
	return Packet;
}


this part exacly:
1
2
3
4
5
for (int X = 0; X < Command.length(); X++)
	{
		Packet[12 + X] = System::Text::Encoding::Default->GetBytes(Command[X])[0];
	}
	return Packet;

or does it fill all free places with zeros?! ;l i just dont get it.

thx
Last edited on
Well, first, a note: That is not standard C++. You can't return an array like that in C++. Looks like you're using the .NET managed C++, which is fine, it's just you should let people know this up front so they aren't surprised.

The Packet array is the array of "bytes" (unsigned chars) that you are filling with data. Did you write this function yourself? You seem to skipping over indices, like Packet[1], Packet[2], etc. Each index is a byte, not a bit, FYI.

The specific part you're talking about fills Packet[12] to Packet[12 + Command.length() - 1], inclusive, with data.

GetBytes is a Microsoft library function that you can look up:
https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding.getbytes?view=net-6.0

Command[X] is an individual char, and actually I don't see an overload in the documentation that accepts a char.

Look at the examples in the link above (switch the view to C++ if it defaults to C#).
You might want something like this:
1
2
3
4
5
array<Byte>^ bytes = System::Text::Encoding::Default->GetBytes(Command.c_str());
for (int i = 0; i < (int)Command.length(); i++)
{
    Packet[12 + i] = bytes[i];
}

(not tested, and note the other issue I already mentioned)

Edit: Also, on line 3: You don't want to be convert the size of the array into an unsigned char. The array itself might be greater than 255 in size.

You just want unsigned char Packet[13 + Command.length()]; (possibly with the +1 there? I forget the specifics of the header format.)
Last edited on
I want to communicate via TCP Socket with an CSGO Server
its a gaming server for "Counter-strike Global Offensive"

here is a short documentation on how to send packets,

https://developer.valvesoftware.com/wiki/Source_RCON_Protocol

thanks for the fast replay, to be honest that is not my personal code, its an example, but i didnt know u can include .Net in C++ too.

https://i.ibb.co/W26h3B0/array.png

and why do they want command[1]2 and 3 to be empty ?
Last edited on
and why do they want command[1]2 and 3 to be empty ?

They don't. The image is the memory layout of a byte sequence used in the exchange, showing the size and order of the fields. It's not specified, but it's assumed to be little endian, so you'll need to be aware of this if you work on any ARM hardware.

I want to communicate via TCP Socket with an CSGO Server

Well, according to the documentation you posted, the first thing you need to do is login. You'll need the password.

We can work thru that if you need help, but someone else will have to chip in if you insist on using C#,

EDIT:
From the docs:
SERVERDATA_AUTH

Typically, the first packet sent by the client will be a SERVERDATA_AUTH packet, which is used to authenticate the connection with the server. The value of the packet's fields are as follows:
Field Contains
ID any positive integer, chosen by the client (will be mirrored back in the server's response)
Type 3
Body the RCON password of the server (if this matches the server's rcon_password cvar, the auth will succeed)

So, you'll need a structure to fill in:
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
#include <cstddef>
#include <cstring>
#include <string>
#include <string_view>

// type codes
enum class PacketType : std::int32_t {
    SERVERDATA_AUTH  = 3,
    SERVERDATA_AUTH_RESPONSE = 2,
    SERVERDATA_EXECCOMMAND = 2,
    SERVERDATA_RESPONSE_VALUE = 0
};

using PacketSize = std::int32_t;
using ClientId = std::int32_t;

#pragma pack(push) // save old alignment
#pragma pack(1) // need byte alignment
class Packet {
    // binary layout of message to send/recv
    PacketSize size_; // packet size
    ClientId id_; // client id
    PacketType type_; // message type
    char payload_[];  // variable length buffer

public:
    Packet(const unsigned char* buffer, ssize_t buffer_size);  // construct from byte stream
    Packet(ClientId id, PacketType type, const std::string& body); // construct from args
    ~Packet(); // we'll need a custom delete, plus copy should be disabled

    PacketSize size() const { return size_; } 
    ClientId id() const { return id_; }
    std::string_view body() const { return {payload_, std::strlen(payload)}; }
};
#pragma pack(pop)  // restore alignment 


That's enough typing ... The constructors are an exercise for the reader. Come back if you're still stuck, I'm just out of time right now.
Last edited on
wow this looks rly professional, can it be that this is for linux.
on windows it seems string_view is not part of the std;

i did it a bit different, would u jsut explain to me how can we put the size of a string
to a constant char array?

those CSGO commands are something like this:
"sv_restart 1"
so I want a way to do something like this:
1
2
3
4
5
6
string cmd = "sv_restart 1";

const char buffer[cmd.length()+1]; // but this is not working it says i need a constant value
//tried even this
const char buffer[static_cast<const int>(cmd.length()+1)];


and I bump into this problem quite alot.

I would appriciate ur help with this problem :S
thanks alot
Please accept my apologies, it was somewhat remiss to post incomplete code.

If you send me the address of a server I can connect to, I'll post a complete working version; otherwise, I'll post a best effort from spec untested version.
here is a list of online csgo servers, but the thing is to send commands to the server
u need to know the "RCON password"

https://www.gametracker.com/search/csgo/
but maybe when u try to access it, ull get some replay with some info.

thx
EDIT:
As promised, an implementation. I haven't connected it to a real RCON server, so ...

It's written with Windows support in mind, and builds on Windows and Linux/BSD.

It's written in C++14, to support various VS versions.

Comments welcome.
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// Windows/POSIX support
// C++14 for VS compatibility
// older includes for VS compatibility
#include <iomanip>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>

#include <stdlib.h>
#include <stdint.h>
#include <string.h>

// socket stuff
#if defined(_WIN32) || defined(_WIN64)
#define WIN32_LEAN_AND_MEAN 1
#include <WinSock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "AdvApi32.lib")
class WsaWrapper {
public:
	WsaWrapper() {
		auto rc = WSAStartup(MAKEWORD(2, 2), &data_);
		if (rc != 0)
			throw std::runtime_error("Cannot initialize WinSock");
	}
	~WsaWrapper() { WSACleanup(); };
	WsaWrapper(const WsaWrapper& n) = delete;
	WsaWrapper& operator=(const WsaWrapper& n) = delete;
private:
	WSADATA data_;
};
using socket_t = SOCKET;
using ssize_t = int;
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
using socket_t = int;
#endif

namespace simple_socket {
	socket_t connectTo(const char* host, short port);
	void closeSocket(socket_t s);

	class ClientSocket {
	public:
		ClientSocket(int argc, char* argv[]) : s_(static_cast<socket_t>(-1)) {
			const char* host = "127.0.0.1";
			short port = 27015;
			if (argc > 1)
				host = argv[1];
			if (argc > 2)
				port = static_cast<short>(atoi(argv[2]));
			s_ = connectTo(host, port);
		}
		~ClientSocket() {
			closeSocket(s_);
		}
		ClientSocket() = delete;
		ClientSocket(const ClientSocket& n) = delete;
		ClientSocket& operator=(const ClientSocket& n) = delete;

		socket_t socket() { return s_; }
	private:
		socket_t s_;
	};

	socket_t connectTo(const char* host, short port) {
		socket_t s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (s < 0)
			throw std::runtime_error("Cannot create socket");

		sockaddr_in addr;
		memset(&addr, 0, sizeof(addr));
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
		int rc = inet_pton(AF_INET, host, &addr.sin_addr);
		if (rc != 1)
			throw std::runtime_error(std::string("Cannot accept address: ") + host);

		rc = connect(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
		if (rc != 0)
			throw std::runtime_error(std::string("Cannot connect to: ") + host + std::string(":") + std::to_string(port));

		return s;
	}

	void closeSocket(socket_t s) {
#if defined(_WIN32) || defined(_WIN64)
		closesocket(s);
#else
		close(s);
#endif
	}
} // simple_socket

// RCON stuff
namespace rcon {
	using id_t = int32_t;

	enum class PacketType : int32_t {
		SERVERDATA_AUTH = 3,
		SERVERDATA_AUTH_RESPONSE = 2,
		SERVERDATA_EXECCOMMAND = 2,
		SERVERDATA_RESPONSE_VALUE = 0
	};

#pragma pack(push) // save old alignment
#pragma pack(1) // need byte alignment
	struct Packet {
		id_t id;
		PacketType type;
		char body[];
	};
#pragma pack(pop)  // restore alignment

	inline std::pair<std::unique_ptr<char[]>, size_t> createPacket(id_t id, PacketType type, const std::string& body) {
		size_t size = sizeof(id) + sizeof(type) + body.size() + 2; // two trailing nulls
		auto raw_buffer = std::make_unique<char[]>(size);
		Packet* packet = reinterpret_cast<Packet*>(raw_buffer.get());
		memset(packet, 0, size);
		packet->id = id;
		packet->type = type;
		memcpy(packet->body, body.data(), body.size());
		return { std::move(raw_buffer), size };
	}

	std::pair<std::unique_ptr<char[]>, size_t> createServerDataAuth(id_t id, const std::string& password) {
		return createPacket(id, PacketType::SERVERDATA_AUTH, password);
	}

	std::pair<std::unique_ptr<char[]>, size_t> createServerExecCmd(id_t id, const std::string& cmd) {
		return createPacket(id, PacketType::SERVERDATA_EXECCOMMAND, cmd);
	}
} // namespace rcon

std::pair<bool, std::string> serverDataAuth(simple_socket::ClientSocket& s, rcon::id_t id, const std::string& password) {
	auto msg = rcon::createServerDataAuth(id, password);
	ssize_t nbytes = ::send(s.socket(), msg.first.get(), msg.second, 0);
	if (rcon::id_t(nbytes) != msg.second)
		return { false, "Failed to send ServerDataAuth: id=" + std::to_string(id) + " password=" + password };

	const size_t bufsz = 64;
	char buf[bufsz];
	memset(buf, 0, bufsz);
	nbytes = ::recv(s.socket(), buf, bufsz, 0);
	if (nbytes == -1)
		return { false, "Failed to receive ServerDataAuth response" };

	const rcon::Packet* packet = reinterpret_cast<const rcon::Packet*>(buf);
	if (packet->id == -1)
		return { false, "id is -1" };
	if (packet->type != rcon::PacketType::SERVERDATA_AUTH_RESPONSE)
		return { false, "Unexpected reply message type: " + std::to_string(static_cast<int>(packet->type)) };
	return { true, "ok" };
}

std::pair<bool, std::string> serverExecCmd(simple_socket::ClientSocket& s, rcon::id_t id, const std::string& cmd, std::string& reply) {
	auto msg = rcon::createServerExecCmd(id, cmd);
	ssize_t nbytes = ::send(s.socket(), msg.first.get(), msg.second, 0);
	if (rcon::id_t(nbytes) != msg.second)
		return { false, "Failed to send ServerExecCmd: id=" + std::to_string(id) + " command=" + cmd };

	const size_t bufsz = 1024;
	char buf[bufsz];
	memset(buf, 0, bufsz);
	nbytes = ::recv(s.socket(), buf, bufsz, 0);
	if (nbytes == -1)
		return { false, "Failed to receive ServerExecCmd response" };

	const rcon::Packet* packet = reinterpret_cast<const rcon::Packet*>(buf);
	if (packet->id == -1)
		return { false, "id is -1" };
	if (packet->type != rcon::PacketType::SERVERDATA_RESPONSE_VALUE)
		return { false, "Unexpected reply message type: " + std::to_string(static_cast<int>(packet->type)) };
	reply = packet->body;
	return { true, "ok" };
}

int main(int argc, char* argv[])
try {
#if defined(_WIN32) || defined(_WIN64)
	WsaWrapper wsa;
#endif
	simple_socket::ClientSocket s(argc, argv);

	rcon::id_t id{ 7 }; // arbitrary number
	auto rc = serverDataAuth(s, id, "password");
	std::cout << "serverDataAuth returned state="
		<< std::boolalpha << rc.first
		<< " message=" << rc.second
		<< std::endl;

	if (rc.first) {
		// send random command, now that we're connected
		std::string reply;
		rc = serverExecCmd(s, id, "sleep", reply);
		std::cout << "serverExecCmd returned state="
			<< std::boolalpha << rc.first
			<< " message=" << rc.second
			<< " reply=" << reply
			<< std::endl;
	}
}
catch (const std::exception& e) {
	std::clog << "fatal: " << e.what() << std::endl;
}
Last edited on
Wow, i heard in programming there are thousand ways to solve a problem,
But this must be the absolute perfect one!!,
Thanks Keith
🤣
Ill check it line by line
Thanks alot 👏👏👏
Topic archived. No new replies allowed.