Здесь работает реализация CookieJar для OkHttp3. Если у вас есть несколько экземпляров OkHttp3 (обычно вы должны иметь только один экземпляр и использовать его как синглетон), вы должны установить один и тот же экземпляр cookiejar для всех клиентов http, чтобы они могли делиться файлами cookie !!! В этой реализации не сохраняются файлы cookie (все они будут удалены при перезапуске приложения), но должно быть легко реализовать постоянство SharedPreferences вместо списка файлов cookie в cookie (cookieStore).
CookieJar cookieJar = new CookieJar() {
private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookieStore.put(url.host(), cookies);
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies = cookieStore.get(url.host());
return cookies != null ? cookies : new ArrayList<Cookie>();
}
};
OkHttpClient httpClient = new OkHttpClient.Builder()
.cookieJar(cookieJar)
.build();
Предлагаю взглянуть на: http://www.kegel.com/c10k.html , во-вторых, взгляните на существующие библиотеки, такие как libevent, Boost.Asio которые уже выполняют свою работу и смотрят, как они работают.
Дело в том, что подход может быть разным для каждого типа системного вызова:
Предложение: используйте хорошую существующую библиотеку, такую как Boost.Asio для C ++ или libevent для C.
РЕДАКТИРОВАТЬ: Вот как ASIO справляется с этим
class connection {
boost::asio:ip::tcp::socket socket_;
public:
void run()
{
// for variable length chunks
async_read_until(socket_,resizable_buffer,'\n',
boost::bind(&run::on_line_recieved,this,errorplacehplder);
// or constant length chunks
async_read(socket_,buffer(some_buf,buf_size),
boost::bind(&run::on_line_recieved,this,errorplacehplder);
}
void on_line_recieved(error e)
{
// handle it
run();
}
};
Потому что ASIO работает как проактор он уведомляет вас, когда операция завершена и обрабатывает EWOULDBLOCK внутренне.
Если вы используете слово «реактор», вы можете смоделировать это поведение:
class conn {
// Application logic
void run() {
read_chunk(&conn::on_chunk_read,size);
}
void on_chunk_read() {
/* do something;*/
}
// Proactor wrappers
void read_chunk(void (conn::*callback),int size, int start_point=0) {
read(socket,buffer+start,size)
if( complete )
(this->*callback()
else {
this -> tmp_size-=size-read;
this -> tmp_start=start+read;
this -> tmp_callback=callback
your_event_library_register_op_on_readable(callback,socket,this);
}
}
void callback()
{
read_chunk(tmp_callback,tmp_size,tmp_start);
}
}
Что-то вроде этого.
You want to decouple "io" from processing, at which point the code you read will become very readable. Basically you have:
int read_io_event(...) { /* triggers when we get a read event from epoll/poll/whatever */
/* read data from "fd" into a vstr/buffer/whatever */
if (/* read failed */) /* return failure code to event callback */ ;
if (/* "message" received */) return process_io_event();
if (/* we've read "too much" */) /* return failure code to event callback */ ;
return /* keep going code for event callback */ ;
}
int process_io_event(...) {
/* this is where you process the HTTP request/whatever */
}
...then the real code is in process event, and even if you have multiple requests responses it's pretty readable, you just do "return read_io_event()" after setting a state or whatever.
Конечные автоматы - хороший подход. Это немного сложностей, которые избавят вас от головной боли в будущем, когда будущее начинается очень-очень скоро. ; -)
Другой метод - использовать потоки и выполнять блокирующий ввод-вывод для одного fd в каждом потоке. Компромисс здесь заключается в том, что вы делаете ввод / вывод простым, но может усложнять синхронизацию.