Оглавление
Сокет (сокет) — метод сетевого программирования. Через сокеты можно обеспечить не только межпроцессное взаимодействие, но и межхостовое сетевое взаимодействие. Используя эту технологию, можно обеспечить связь по всей стране. Например: компьютер в Шэньчжэне получает информацию от компьютера в Пекине. В этой статье не рассматриваются основные принципы работы сети, а только объясняется основное использование сокетов. В основном обратитесь к разделу «Сетевое программирование Linux». Как получить исходный код этой статьи можно узнать мелким шрифтом внизу текста.
Сокеты — это метод взаимодействия с другими программами через стандартные файловые дескрипторы UNIX. Он может осуществлять связь между различными процессами на одном хосте; он также может осуществлять связь между разными хостами;
Существует три типа сокетов: потоковые сокеты (SOCK_STREAM), дейтаграммные сокеты (SOCK_DGRAM) и необработанные сокеты.
Потоковый сокет (SOCK_STREAM) Потоковые сокеты обеспечивают надежный поток связи, ориентированный на соединение. Если вы отправляете последовательные данные через потоковый сокет: «1», «2». Тогда порядок поступления данных в удаленное место также будет «1», «2».
Рабочий процесс Socket, ориентированный на соединение
Датаграммный сокет (SOCK_DGRAM) Сокеты дейтаграмм определяют службу без установления соединения. Данные передаются через независимые сообщения, которые не в порядке и не гарантируют надежность и отсутствие ошибок.
Необработанный сокет (SOCK_RAM) Необработанные сокеты в основном используются для разработки некоторых протоколов и могут выполнять операции относительно низкого уровня. Он мощный, но не так удобен в использовании, как два представленных выше сокета, а обычные программы не используют оригинальные сокеты.
connect()
。struct sockaddr {
unsigned short sa_family; /* адрес семьи, AF_xxx */
char sa_data[14]; /* 14 Протокольный адрес байтов */
};
sa_family
Для указанного семейства протоколов,Обычно используютсяAF_INET、AF_UNIXждать。
sa_data
для Связь между различными семействами протоколовчаснеобходимые данные。Например,sa_family
дляAF_INETчас,sa_data
Хочу пройтиIPадрес иномер порта。
sockaddr_in
используется для храненияAF_INETАдрес сокета,вinЭто означаетInet。
представлятьsockaddr
час,Говоря об использованииAF_INETнуждаться Хочу пройтиIPиномер порта,Но я не знаю, какIPиномер порта Заполнятьsockaddr
где в。затем,Разработанныйsockaddr_in
,определятьадрес иномер портачлен。в использованиичас Тольконужно заполнитьsockaddr_in
,Передача параметровчас Принудительный переводдляsockaddr
Вот и все(Обе конструкции имеют одинаковый размер.)。struct sockaddr_in {
short int sin_family; /* Семейство интернет-адресов */
unsigned short int sin_port; /* номер порта */
struct in_addr sin_addr; /* Интернет-адрес */
unsigned char sin_zero[8]; /* добавьте 0 (и структура sockaddr того же размера) */
};
in_addr
дляsockaddr_in
член,Используется для хранения 4 байтов IP-адресов. Необходимость Примечание означает,Это значение необходимо заполнить в соответствии с сетевыми байтами.,Это можно сделать с помощью некоторых функций преобразования.struct in_addr {
unsigned long s_addr;
};
sockaddr_un
используется для храненияAF_UNIXАдрес сокета,Предполагается, что un представляет UNIX (без проверки).
˜Аналогично предыдущему,При использовании AF_UNIX,нужно заполнитьAF_UNIXСтруктура адресаsockaddr_un
,然后Передача параметровчас Принудительный переводдляsockaddr
。struct sockaddr_un
{
uint8_t sun_len;
sa_family_t sun_family; /* AF_LOCAL */
char sun_path[104]; /* null-terminated pathname */
};
Преобразование порядка байтов в сети Как описано выше, обратите внимание на порядок сетевых байтов при заполнении IP-адреса и порта, иначе вы не сможете подключиться к указанному устройству. В системе предусмотрены следующие функции для облегчения конвертации:
трансляция IP-адреса
Примечание:inet_ntoa()
Возвращает указатель символа,Он определяется как указатель на функцию inet_ntoa() в статической входной строке. Итак, каждый раз, когда вы вызываете inet_ntoa(), наконец, изменится после вызова inet_ntoa() Результат, полученный функцией.
Linux Linux поддерживает сокеты как с установлением соединения, так и без него. При обмене данными с установлением соединения сервер и клиент должны установить соединение перед обменом данными. При обмене данными без установления соединения данные обмениваются как часть сообщения. В любом случае сервер всегда запускается первым, привязывается к сокету, а затем прослушивает сообщения.
socket
В основном используются следующие функции:
Под TCP-сокетом можно понимать Inet с использованием потоковых сокетов, а протокол TCP используется для обеспечения стабильной связи. Его преимущества – надежность и стабильность.
TCP-сокет-сервер
void DealClientMsgThread(int fd)
{
while(1)
{
char buf[1024] = {0};
int ret = read(fd, buf, sizeof(buf));
if (ret > 0) {
TSVR_LOG("# RECEIVE: %s.\n", buf);
// response dstAddr msg
char ack[20] = {0};
snprintf(ack, sizeof(ack), "Ack [%ld]", strlen(buf));
write(fd, ack, strlen(ack));
} else {
break;
}
}
TSVR_LOG("Disconnect.\n");
}
int main(int argc, char *argv[])
{
int i = 0;
std::thread threads[MAX_NUM_THREAD];
if (argc != 2)
{
TSVR_LOG("usage ./tcp_server 8080.\n");
return -1;
}
string port = argv[1];
int sockFd = socket(AF_INET, SOCK_STREAM, 0);
if (sockFd == -1)
{
TSVR_LOGE("socket failed. (%s)\n", strerror(errno));
return -1;
}
int op = 1;
if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &op, sizeof(op)) < 0) {
TSVR_LOGE("setsockopt failed. (%s)\n", strerror(errno));
goto exit;
}
struct sockaddr_in myAddr;
bzero(&myAddr, sizeof(myAddr));
myAddr.sin_family = AF_INET;
myAddr.sin_addr.s_addr = htonl(INADDR_ANY);
myAddr.sin_port = htons(atoi(port.c_str()));
if (bind(sockFd, (struct sockaddr *)&myAddr, sizeof(myAddr)) == -1) {
TSVR_LOGE("bind failed. (%s)", strerror(errno));
goto exit;
}
if (listen(sockFd, MAX_SIZE_BACKLOG) == -1) {
TSVR_LOGE("listen failed. (%s)\n", strerror(errno));
goto exit;
}
while(1)
{
// accept
struct sockaddr_in dstAddr;
socklen_t addrSize = (socklen_t)sizeof(dstAddr);
int conFd = accept(sockFd, (struct sockaddr *)&dstAddr, &addrSize);
if (conFd < 0) {
TSVR_LOGE("accept failed. (%s)\n", strerror(errno));
continue;
}
if (i < MAX_NUM_THREAD)
{
threads[i] = std::thread(DealClientMsgThread, conFd);
threads[i].detach();
i++;
} else {
TSVR_LOG("The number of threads reaches max(%d).\n", MAX_NUM_THREAD);
}
}
exit:
close(sockFd);
return 0;
}
клиент TCP-сокета
int main(int argc, char *argv[])
{
int op = 1024;
if (argc != 3)
{
TCLT_LOG("usage ./tcp_client <ip> <port>.\n");
return -1;
}
string ipAddr = argv[1];
string port = argv[2];
int sockFd = socket(AF_INET, SOCK_STREAM, 0);
if (sockFd == -1)
{
TCLT_LOGE("socket failed. (%s)\n", strerror(errno));
return -1;
}
if (setsockopt(sockFd, SOL_SOCKET, SO_RCVBUF, &op, sizeof(op)) < 0) {
TCLT_LOGE("setsockopt failed. (%s)\n", strerror(errno));
close(sockFd);
return -1;
}
if (setsockopt(sockFd, SOL_SOCKET, SO_SNDBUF, &op, sizeof(op)) < 0) {
TCLT_LOGE("setsockopt failed. (%s)\n", strerror(errno));
close(sockFd);
return -1;
}
struct sockaddr_in dstAddr;
dstAddr.sin_family = AF_INET;
dstAddr.sin_addr.s_addr = inet_addr(ipAddr.c_str());
dstAddr.sin_port = htons(atoi(port.c_str()));
// Linux TCP repeat connect directly return EISCONN.
if ( connect(sockFd, (struct sockaddr*)&dstAddr, sizeof(struct sockaddr_in)) < 0
&& errno != EISCONN)
{
TCLT_LOGE("connect %s:%d failed. (%s)\n", ipAddr.c_str(), atoi(port.c_str()), strerror(errno));
close(sockFd);
return -1;
}
thread rTh([&]() {
char recvBuf[1024] = {0};
TCLT_LOG("Start write thread.\n");
while(1)
{
int ret = read(sockFd, recvBuf, sizeof(recvBuf));
if (ret > 0) {
TCLT_LOG("# RECEIVE: %s.\n", recvBuf);
} else {
break;
}
}
});
thread wTh([&]() {
TCLT_LOG("Start read thread.\n");
while(1)
{
char sndBuf[] = "Hello World";
int ret = write(sockFd, sndBuf, strlen(sndBuf));
if (ret > 0)
{
TCLT_LOG("# SEND > %s.\n", sndBuf);
} else {
break;
}
sleep(2);
}
});
rTh.join();
wTh.join();
return 0;
}
Код представляет собой очень простую обработку TCP-связи. Для облегчения понимания не используется чрезмерная инкапсуляция.
Сокет UDP можно понимать как сокет дейтаграмм, используемый Inet. Для быстрой связи клиент и сервер соглашаются использовать соединение через сокет UDP.
UDP-сокет-сервер
int main(int argc, char *argv[])
{
struct sockaddr_in dstAddr;
socklen_t addrSize = sizeof(struct sockaddr_in);
if (argc != 2)
{
USVR_LOG("usage ./udp_server 8080.\n");
return -1;
}
string port = argv[1];
int sockFd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockFd == -1)
{
USVR_LOGE("socket failed. (%s)\n", strerror(errno));
return -1;
}
int op = 1;
if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &op, sizeof(op)) < 0) {
USVR_LOGE("setsockopt failed. (%s)\n", strerror(errno));
goto exit;
}
struct sockaddr_in myAddr;
bzero(&myAddr, sizeof(struct sockaddr_in));
myAddr.sin_family = AF_INET;
myAddr.sin_addr.s_addr = htonl(INADDR_ANY);
myAddr.sin_port = htons(atoi(port.c_str()));
if (bind(sockFd, (struct sockaddr *)&myAddr, sizeof(struct sockaddr_in)) < 0) {
USVR_LOGE("bind failed. (%s)\n", strerror(errno));
goto exit;
}
while(1)
{
char buf[1024] = {0};
int ret = recvfrom(sockFd, buf, sizeof(buf), 0,
(struct sockaddr *)&dstAddr, &addrSize);
if (ret > 0) {
char ack[20] = {0};
snprintf(ack, sizeof(ack), "Ack [%ld]", strlen(buf));
USVR_LOG("# RECEIVE: %s.\n", buf);
sendto(sockFd, ack, strlen(ack), 0,
(struct sockaddr *)&dstAddr, addrSize);
} else {
USVR_LOG("recvfrom failed. (%s)\n", strerror(errno));
break;
}
}
exit:
close(sockFd);
return 0;
}
Клиент сокета UDP
int main(int argc, char *argv[])
{
if (argc != 3)
{
UCLT_LOG("usage ./udp_client <ip> <port>.\n");
return -1;
}
string ipAddr = argv[1];
string port = argv[2];
int sockFd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockFd == -1)
{
UCLT_LOGE("socket failed. (%s)\n", strerror(errno));
return -1;
}
int op = 1024;
if (setsockopt(sockFd, SOL_SOCKET, SO_RCVBUF, &op, sizeof(op)) < 0) {
UCLT_LOGE("setsockopt failed. (%s)\n", strerror(errno));
close(sockFd);
return -1;
}
struct sockaddr_in dstAddr;
bzero(&dstAddr, sizeof(struct sockaddr_in));
dstAddr.sin_family = AF_INET;
dstAddr.sin_addr.s_addr = inet_addr(ipAddr.c_str());
dstAddr.sin_port = htons(atoi(port.c_str()));
thread wTh([&]() {
UCLT_LOG("Start write thread.\n");
while(1)
{
char sndBuf[] = "Hello World";
int ret = sendto(sockFd, sndBuf, strlen(sndBuf), 0,
(struct sockaddr *)&dstAddr, sizeof(struct sockaddr_in));
if (ret > 0) {
UCLT_LOG("# SEND: %s.\n", sndBuf);
} else {
break;
}
sleep(2);
}
});
thread rTh([&]() {
struct sockaddr_in dstAddr;
UCLT_LOG("Start read thread.\n");
while(1)
{
char recvBuf[1024] = {0};
socklen_t addrSize = sizeof(struct sockaddr_in);
bzero(&dstAddr, addrSize);
int ret = recvfrom(sockFd, recvBuf, sizeof(recvBuf), 0,
(struct sockaddr *)&dstAddr, &addrSize);
if (ret > 0)
{
UCLT_LOG("# RECEIVE: %s.\n", recvBuf);
} else {
break;
}
}
});
wTh.join();
rTh.join();
return 0;
}
Чувствуйте сердцем, записывайте внимательно, хорошо пишите каждую статью и делитесь каждой коробочкой полезной информации.