struct
{
int X;
int Y;
int Type;
}
ServerSocket
1) Как лучше передавать данные через интернет , через создания сокетов с помощью WINAPI или через ServerSocket самого билдера ? Какие у кого преимущества ?
2) Если мне нужно передавать разные данные. Например координату и тип объекта с этой координатой, как это реализовать? Спасибо.
Цитата: Frio
Доброго времени суток. Есть 2 вопроса.
1) Как лучше передавать данные через интернет , через создания сокетов с помощью WINAPI или через ServerSocket самого билдера ? Какие у кого преимущества ?
2) Если мне нужно передавать разные данные. Например координату и тип объекта с этой координатой, как это реализовать? Спасибо.
1) Как лучше передавать данные через интернет , через создания сокетов с помощью WINAPI или через ServerSocket самого билдера ? Какие у кого преимущества ?
2) Если мне нужно передавать разные данные. Например координату и тип объекта с этой координатой, как это реализовать? Спасибо.
1. Монопенисуально
2. Структурой типа
Код:
ну и соответственно на сервере и на клиенте Type должен быть одинаково описан.
Теперь касательно второго вопроса, как передавать данные. По большому счету, вам нужно разработать собственный протокол передачи данных (либо воспользоваться одним из существующим, но если вы спрашиваете о реализации - то я называю наиболее общий вариант). Посмотрите структуру некоторых существующих протоколов уровня приложения. Обычно передача данных осуществляется на уровне пакетов, который содержит тип пакета, длину передаваемых данных и собственно данные (это называется TLV-структурой - "Type, Length, Value").
Alexander92 - у меня уже была одна программа но там передавались только координаты. Передавал весь массив координат целиком, потомучто при передаче через цикл пакеты склеивались и получалась неразбериха. Я так понял чтобы клиент различал данные нужно вводить в покет с данными какието "разделители" ? Но как их потом прочесть , ведь весь массив в строку не пихнёшь для нахождения того "разделителя" . И ещё, есть ли разница в скорости передачи между написанным сокетом от руки и билдеровским ServerSocket'ом ?
1. Сервер видит, что сокет не пустой.
2. Сервер читает заранее предопределенное количество байт заголовка, извлекает из него длину тела пакета.
3. Сервер читает тело пакета в соответствии с той длиной, которую он извлек на прыдыдущем шаге.
Само тело может быть тоже "многоступенчатым", т.е. в него могут быть вложены подпакеты. Почитайте, например, про структуру PCM WAVE-файла, про структуру TCP-фрейма, про передачу данных в HTTP POST-запросе, ну или про FLAP'ы в протоколе ICQ, на худой конец. :)
Цитата:
И ещё, есть ли разница в скорости передачи между написанным сокетом от руки и билдеровским ServerSocket'ом
Затрудняюсь ответить, очень мало работал с билдером. Скажу одно: безусловно, любая обертка всегда работает немного медленнее, чем оригинал. Но разница по скорости между "родным" сокетом и компонентами играет значительную роль только если этот вопрос ОЧЕНЬ принципиальный. В бытовых приложениях скорости компонентов обычно вполне достаточно.
К примеру такой случай. Каждые 40мс передаются какието координаты независимо ни от чего. И мне вдруг понадобилось отправить ещё какуюто переменную. Как это осуществиться ? Извините я с сокетами и интернетом очень мало работал.
Цитата: Frio
Каждые 40мс передаются какието координаты независимо ни от чего.
Такой точности добиться в условиях Windows и TCP/IP сетей невозможно.
Код:
#define PACKET_COORDINATS 1
#define PACKET_SOME_VAR 2
#define PACKET_SOME_VAR 2
Далее, вы определяете структуру самого пакета, например:
Код:
typedef struct tagPacket {
BYTE Type;
UINT Length;
BYTE Data[32767];
} PACKET, *LPPACKET;
BYTE Type;
UINT Length;
BYTE Data[32767];
} PACKET, *LPPACKET;
Теперь примеры отправки данных. Для отправки координат вы строго оговариваете, что каждая координата занимает, например, два байта. Тогда имеете:
Код:
int SendPacket(SOCKET *s, PACKET *p) {
int ret = 0;
// преобразовать p->Type к строковой переменной TypeStr
// преобразовать p->Length к строковой переменной LengthStr
ret = send(s, TypeStr, sizeof(p->Type), 0);
ret += send(s, LengthStr, sizeof(p->Length), 0);
ret += send(s, (CHAR *)&(p->Value), p->Length, 0);
return ret;
}
SOCKET s;
// здесь создаете сокет, открываете соединение
PACKET packet;
BYTE X[2], Y[2]; // предполагается, что вы уже преобразовали свои координаты в массив байт
// обнулить пакет
ZeroMemory((LPVOID)&packet, sizeof(PACKET));
packet.Type = PACKET_COORDINATES;
packet.Length = 4; // 2 координаты
CopyMemory((LPVOID)&Value[0], (LPVOID)X, 2);
CopyMemory((LPVOID)&Value[2], (LPVOID)Y, 2);
// отправка
SendPacket(&S, &packet);
// а теперь вам нужно отправить еще какую-то переменную Z:
DWORD Z; // например, она типа DWORD
ZeroMemory((LPVOID)&packet, sizeof(PACKET));
packet.Type = PACKET_SOME_VAR;
packet.Length = sizeof(DWORD); // размер вашей переменной
CopyMemory((LPVOID)&Value[0], (LPVOID)&Z, sizeof(DWORD));
// отправляете
SendPacket(&S, &packet);
// закрываете сокет
int ret = 0;
// преобразовать p->Type к строковой переменной TypeStr
// преобразовать p->Length к строковой переменной LengthStr
ret = send(s, TypeStr, sizeof(p->Type), 0);
ret += send(s, LengthStr, sizeof(p->Length), 0);
ret += send(s, (CHAR *)&(p->Value), p->Length, 0);
return ret;
}
SOCKET s;
// здесь создаете сокет, открываете соединение
PACKET packet;
BYTE X[2], Y[2]; // предполагается, что вы уже преобразовали свои координаты в массив байт
// обнулить пакет
ZeroMemory((LPVOID)&packet, sizeof(PACKET));
packet.Type = PACKET_COORDINATES;
packet.Length = 4; // 2 координаты
CopyMemory((LPVOID)&Value[0], (LPVOID)X, 2);
CopyMemory((LPVOID)&Value[2], (LPVOID)Y, 2);
// отправка
SendPacket(&S, &packet);
// а теперь вам нужно отправить еще какую-то переменную Z:
DWORD Z; // например, она типа DWORD
ZeroMemory((LPVOID)&packet, sizeof(PACKET));
packet.Type = PACKET_SOME_VAR;
packet.Length = sizeof(DWORD); // размер вашей переменной
CopyMemory((LPVOID)&Value[0], (LPVOID)&Z, sizeof(DWORD));
// отправляете
SendPacket(&S, &packet);
// закрываете сокет
Соответственно, на стороне сервера обработка следующая:
Код:
SOCKET S;
// открываете сокет, принимаете соединение
// прочесть один байт - тип пакета
SHORT type;
recv(S, (CHAR *)&type, 1, 0);
// прочесть четыре байта - длина пакета
CHAR len[5] = {'\0'};
recv(S, len, 4, 0);
BYTE X[2], Y[2];
// определить дальнейшие операции в соответствии с тем, какой пакет вы получили:
switch(type) {
case PACKET_COORDINATES:
// получить X
recv(S, (CHAR *)X, 2, 0);
// получить Y
recv(S, (CHAR *)Y, 2, 0);
// ...
case PACKET_SOME_VAR:
// обработать вашу переменную
}
// открываете сокет, принимаете соединение
// прочесть один байт - тип пакета
SHORT type;
recv(S, (CHAR *)&type, 1, 0);
// прочесть четыре байта - длина пакета
CHAR len[5] = {'\0'};
recv(S, len, 4, 0);
BYTE X[2], Y[2];
// определить дальнейшие операции в соответствии с тем, какой пакет вы получили:
switch(type) {
case PACKET_COORDINATES:
// получить X
recv(S, (CHAR *)X, 2, 0);
// получить Y
recv(S, (CHAR *)Y, 2, 0);
// ...
case PACKET_SOME_VAR:
// обработать вашу переменную
}
Плюс, разумеется, обработка ошибок и т.п. Заранее прошу прощения, если где-то ошибся, писал сходу, но идею, думаю, поняли.
Да, спасибо огромное..Чтото подобное я себе представлял просто не мог самомстоятельно написать...Спасибо
Почитайте еще про структуру TLV-пакетов, у вас отпадут многие вопросы.
При компиляции серверной и клиентской части на разных компиляторах (скажем клиент на линуксе а сервер на виндовс) линкеры могут по разному выравнивать структуры на клиентской и серверной стороне. То есть, если у вас в сумме набирается размер 15 байт, он может выравнять до 16 в одном случае и до 64 на другом, или оставить как есть.
Можно использовать следующий прием:
Код:
#pragma pack (push)
#pragma pack(1) // здесь выравнивается до байта
struct info {
// ваши данные
// ...
};
#pragma pack (pop)
#pragma pack(1) // здесь выравнивается до байта
struct info {
// ваши данные
// ...
};
#pragma pack (pop)