Для колледжа я разрабатываю локальный релейный чат. Я должен программировать сервер чата и клиент, который будет только работать над отправкой сообщений на различных окнах терминала на том же компьютере с потоками и FIFOs.
Первая часть FIFOs не испытывает затруднений, часть потоков является той, которая дает мне некоторые головные боли.
Сервер имеет один поток для получения команд от FIFO (используемый всеми клиентами) и другой поток для каждого клиента, который соединен.
Для каждого клиента, который соединен, я должен знать определенную информацию. Во-первых, я использовал глобальные переменные, которые работали longs, поскольку был только один соединенный клиент, который является большой частью чата, для обсуждения один.
Так, идеально у меня были бы некоторые данные как:
- псевдоним
имя:
- электронная почта
- и т.д...
на клиент, который соединен. Однако я не знаю, как сделать это.
Я мог создать client_data [MAX_NUMBER_OF_THREADS], где client_data был структурой со всем, к чему у меня должен был быть доступ, но это потребует к в каждой коммуникации между сервером и клиентом просить идентификатор клиента в массиве client_data, и это не кажется очень практичным
Я мог также сразу инстанцировать client_data после создания потока, но это только будет доступно в том блоке, и это не очень практично также.
Поскольку Вы видите, что я нуждаюсь в небольшом руководстве здесь. Любой комментарий, часть кода или ссылки на любую релевантную информацию значительно ценится.
Вот некоторые псевдо-код, который на самом деле может почти запуститься. Обратите внимание: я не освобождаю то, что выделяю, я не проверяю ошибки, я просто пытаюсь продемонстрировать, как передать структуру потоку и использовать простой мьютекс.
Есть одна вещь, о которой следует позаботиться. Указатель функции для потоков указывает аргумент void *
, который может быть буквально любым типом. В потоке мы предполагаем , что безопасно привести аргумент потока к типу, который мы определили для использования. Если вы передаете несколько возможных типов, вы должны быть осторожны :)
Я не совсем уверен в структуре вашей программы или в том, как вы обрабатываете потоки, но вот короткий независимый от подхода пример того, как передавать данные им:
typedef struct client {
char *firstname;
char *lastname;
char *email;
char *nickname
} client_t;
pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
void *client_thread(void *threadarg)
{
client_t *client = (client_t *) threadarg;
pthread_mutex_lock(&client_lock);
/* read and write to client structure */
pthread_mutex_unlock(&client_lock);
/* do some other stuff */
pthread_exit(NULL);
}
int main(void)
{
client_t *client;
pthread_t cthread;
client = (client_t *)malloc(sizeof(struct client));
if (client == NULL)
return 1;
client->firstname = strdup("Joe Public");
/* set the rest of the data */
pthread_create(&cthread, NULL, (void *)client_thread, (void *)client);
/* join / detach / etc */
/* Free all the structure members and the structure itself */
return 0;
}
Я почти уверен, что это то, о чем вы спрашивали?
Я не знаю, какой язык вы используете, но вот несколько основных идей:
Простой цикл сокет-сервера выглядит следующим образом (в Java):
while(true){
ClientWorker w;
try{
//server.accept returns a client connection
w = new ClientWorker(server.accept(), textArea);
Thread t = new Thread(w);
t.start();
} catch (IOException e) {
// log the exception or something...
}
}
Если вам интересно, что он делает - ClientWorker
доступен здесь . В C #, если вы создаете новый поток
, не забудьте установить для его свойства IsBackground
значение true
, чтобы поток закрывался при завершении работы вашего приложения, т. Е. никаких свисающих ниток.
Помните: принятие соединения с сокетом или получение данных от сокета обычно является вызовом блокировки , что означает, что ваш поток будет блокироваться до тех пор, пока кто-нибудь не подключится к сокету или данные не поступят через сокет.
В C #:
В Java:
В C ++
Вместо того, чтобы делать глобальные переменные, просто определите struct
для учетной записи клиента и объявить переменную учетной записи для каждого пользователя ... вот как вы можете определить информацию учетной записи:
struct account {
char nickname[32];
char first_name[32];
char last_name[32];
char e_mail[32];
char password[32];
};
Когда клиент отправляет сообщение, оно должно иметь стандартный формат: FROM | TO | CONTENT
struct message{
char nickname_from[32];
char nickname_to[32]; // optional
char msg_content[256];
};
Поместите каждый сообщение
в FIFO [очереди], и у вас будет вся необходимая информация, чтобы определить, кто его отправил.