У меня есть небольшая программа, которая прослушивает порт 80 с помощью сокетов и отправляет Hello World. Я запускаю свою программу на Ubuntu 18 и отправляю запросы из браузера с другого хоста.
Первые несколько запросов, которые моя программа принимает успешно и успешно отвечает.
Но в конце концов после любого запросаможет произойти ошибка, при которой строка sock = this-> sock_backlog.front ();
устанавливает переменную sock 0. Строка 20, файл task.cpp.
И эта часть кода повторяется в цикле, даже если нет входящих соединений, пока не произойдет ошибка сегментации.
main.cpp
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <thread>
#include "taks.h"
using namespace std;
using namespace web;
int main()
{
task *tsk = new task;
thread task_mgr = thread(&task::manager, tsk);
int sock, newsock, sockaddrlen;
struct sockaddr_in sockaddr;
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
cout << "Socket create error: " << errno << endl;
return 0;
}
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = INADDR_ANY;
sockaddr.sin_port = htons(PORT);
sockaddrlen = sizeof(sockaddr);
if(bind(sock, (struct sockaddr *) &sockaddr, sockaddrlen) < 0) {
cout << "Socket bind error: " << errno << endl;
return 0;
}
if(listen(sock, 0) < 0) {
cout << "Socket listen error: " << errno << endl;
return 0;
}
cout << "Start listening" << endl;
while(true) {
newsock = accept(sock, (struct sockaddr *) &sockaddr, (socklen_t *) &sockaddrlen);
if(newsock < 0) {
cout << "Socket accept error: " << errno << endl;
continue;
}
cout << "Socket accepted: " << newsock << endl;
if(!tsk->push_backlog(newsock)) {
cout << "Reject connection: " << newsock << endl;
if(close(newsock) < 0) {
cout << "Socket close error: " << errno << endl;
}
}
}
//task_mgr.join();
return 0;
}
task.h
#include <thread>
#include <queue>
#include <atomic>
#include "defs.h"
using namespace std;
namespace web
{
class task
{
private:
thread tasks[MAXLCON];
queue<int> sock_backlog;
atomic_flag flag = ATOMIC_FLAG_INIT;
void accept_task(int);
public:
void manager();
bool push_backlog(int);
};
}
task.cpp
#include <iostream>
#include "taks.h"
#include <unistd.h>
#include <sys/socket.h>
using namespace std;
using namespace web;
void task::accept_task(int task_id)
{
int sock;
while(true) {
if(this->sock_backlog.empty()) {
this_thread::yield();
continue;
}
if(!this->flag.test_and_set()) {
sock = this->sock_backlog.front();
cout << "Thread #" << task_id << " got socket " << sock << " flag " << this->flag.test_and_set() << endl;
if(send(sock, "Hello World", 11, 0) < 0) {
cout << "Socket send error: " << errno << endl;
} else {
cout << "Sended to " << sock << endl;
}
if(close(sock) < 0) {
cout << "Socket close error: " << errno << endl;
}
this->sock_backlog.pop();
this->flag.clear();
}
}
}
void task::manager()
{
int f;
for(f = 0; f < MAXLCON; f++) {
this->tasks[f] = thread(&task::accept_task, this, f);
}
}
bool task::push_backlog(int sock)
{
if(this->sock_backlog.size() < MAXBLOG) {
this->sock_backlog.push(sock);
return true;
} else {
return false;
}
}
defs.h
#ifndef MAXLCON
#define MAXLCON 10
#endif
#ifndef MAXBLOG
#define MAXBLOG 20
#endif
#ifndef PORT
#define PORT 80
#endif
Как видите, в этой программе я запустил все потоки заранее. После запуска всех потоков, он просто добавляет подключенные сокеты в очередь. И любой свободный поток получает один из этих сокетов, используя атомарный.
Вывод из консоли
Start listening
Socket accepted: 5
Thread #2 got socket 5 flag 1
Sended to 5
Socket accepted: 4
Thread #6 got socket 4 flag 1
Sended to 4
Socket accepted: 5
Thread #7 got socket 5 flag 1
Sended to 5
Thread #5 got socket 0 flag 1
Socket send error: 88
Thread #5 got socket 0 flag 1
Socket send error: 9
Socket close error: 9
Thread #5 got socket 0 flag 1
Socket send error: 9
Socket close error: 9
Thread #5 got socket 0 flag 1
Socket send error: 9
Socket close error: 9
Thread #5 got socket 0 flag 1
Socket send error: 9
Socket close error: 9
Thread #5 got socket 0 flag 1
Socket send error: 9
Socket close error: 9
Thread #4 got socket 0 flag 1
Socket send error: 9
Socket close error: 9
Segmentation fault
Что не так в этом коде? Может быть неправильно использовать atomic
и функция test_and_set
возвращает false только для одного потока?