Поскольку я не знаю, какую информацию вы ищете, я попытаюсь описать, как настроить программу сокетов и с какими подводными камнями я столкнулся.
Для начала прочитайте учебник Winsock из MSDN. Это базовая программа для подключения, отправки сообщения и отключения. Отлично подходит для программирования сокетов.
Итак, давайте начнем:
Вопросы:
блокировка или неблокировка
Прежде всего вам необходимо определить, хотите ли вы блокирующую или неблокирующую программу. Большая разница в том, что если у вас есть графический интерфейс, вам нужно будет использовать неблокирование или многопоточность, чтобы не заморозить программу. Я сделал это, используя блокирующие вызовы, но всегда вызывая select
перед вызовом блокирующих функций (подробнее о выборе позже). Таким образом, я избегаю многопоточности, мьютекса и прочего, но все же использую базовые вызовы accept
, send
и receive
.
Вы не можете полагаться на то, что ваши посылки будут доставлены так, как вы их отправили!
Вы тоже не оказывает на это никакого влияния. Это была самая большая проблема, с которой я столкнулся, в основном потому, что сетевая карта может решать, какую информацию отправлять и когда отправлять. Я решил, что нужно создать networkPackageStruct
, содержащий size
и data
, где size - это общее количество данных в этом пакете. Обратите внимание, что отправленное вами сообщение может быть разбито на 2 или более сообщений, а также может быть объединено с другим отправленным сообщением.
Учтите следующее:
Вы отправляете два сообщения
"Hello"
"World!"
Когда вы отправляете эти два сообщения с помощью функции send
, ваша функция recv
может не получить их таким образом. Это может выглядеть так:
"Hel"
"loWorld!"
или, возможно,
"HelloWorld!"
какой бы ни была базовая сеть ..
Журнал (почти) всего!
Отладка сетевой программы трудна, потому что у вас нет полного контроля над ней (так как она на двух компьютерах). Если вы столкнетесь с блокирующей операцией, вы тоже ее не увидите. Это также можно назвать «Знай свой код блокировки». Когда одна сторона отправляет что-то, чего ты не знаешь, поступит ли оно на другую, следи за тем, что отправлено и что получено.
Обратите внимание на ошибки сокета
Функции winsock возвращают много информации. Знай свою функцию WSAGetLastError()
. Я не буду держать это в примерах ниже, но учтите, что они имеют тенденцию возвращать много информации. Каждый раз, когда вы получаете SOCKET_ERROR
или INVALID_SOCKET
, проверяйте сообщения об ошибках Winsock , чтобы найти его
Настройка соединения:
Поскольку вам не нужен сервер, всем клиентам понадобится прослушивающий сокет для приема новых соединений. Самый простой:
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in localAddress;
localAddress.sinfamily = AF_INET;
localAddress.sin_port = htons(10000); // or whatever port you'd like to listen to
localAddress.sin_addr.s_addr = INADDR_ANY;
INADDR_ANY великолепен - он действительно заставляет ваш сокет прослушивать все ваши сети, а не только один ipaddress.
bind(s, (SOCKADDR*)&localAddress, sizeof(localAddress));
listen(s, SOMAXCONN);
здесь начинается интересная часть. bind
и listen
не будут блокироваться, но accept
будут. Хитрость заключается в том, чтобы использовать select
для проверки наличия входящего соединения. Таким образом, приведенный выше код просто для установки сокета. в цикле программы вы проверяете наличие новых данных в сокете.
Обмен данными
Способ, который я решил, это использовать select
много. По сути, вы видите, есть ли что-то, на что вам нужно ответить, в любом из ваших сокетов. Это делается с помощью функций FD_xxx
.
// receiving data
fd_set mySet;
FD_ZERO(&mySet);
FD_SET(s, &mySet);
// loop all your sockets and add to the mySet like the call above
timeval zero = { 0, 0 };
int sel = select(0, &mySet, NULL, NULL, &zero);
if (FD_ISSET(s, &mySet)){
// you have a new caller
sockaddr_in remote;
SOCKET newSocket = accept(s, (SOCKADDR*)&remote, sizeof(remote));
}
// loop through your sockets and check if they have the FD_ISSET() set
в newSocket
теперь у вас есть новый пир. Так что это было для получения данных. Но учтите! send
тоже блокирует! Одна из «ошибок почесывания головы», которую я получил, заключалась в том, что send
заблокировал меня. Это было также решено с помощью select
.
// sending data
// in: SOCKET sender
fd_set mySet;
FD_ZERO(&mySet);
FD_SET(sender, &mySet);
timeval zero = { 0, 0 };
int sel = select(0, NULL, mySet, NULL, &zero);
if (FD_ISSET(sender, &mySet)){
// ok to send data
}
Отключение
Наконец, есть два способа выключения. Вы либо просто отключаетесь, закрывая свою программу, либо вызываете функцию shutdown
.
- Вызов выключения сделает ваш равноправный
select
триггер. Однако recv
не получит никаких данных, но вместо этого вернет 0. Я не заметил ни одного другого случая, когда recv
возвращает 0, поэтому (несколько) можно с уверенностью сказать, что это можно считать кодом завершения работы. звонить shutdown
- это самое приятное занятие ..
- Завершение соединения без вызова отключения просто хладнокровно, но, конечно, работает. Вам все еще нужно обработать ошибку, даже если вы используете
shutdown
, так как это может быть не ваша программа, которая закрывает соединение. Хороший код ошибки, который нужно запомнить, это 10054, который WSAECONNRESET: сброс соединения по пиру. .