有关 Windows 中错误严重性代码有何不同的问题。我知道错误代码中的最高 2 位负责错误严重性代码。二进制形式:00 - 成功,01 - 信息,10 - 警告,11 - 错误。这些代码之间有什么区别,每个代码的生成必须发生什么?我很乐意阅读文献或文章链接,否则我无法在任何地方找到有关此的信息。
kuskas112's questions
挑战是编写一个简单的网络聊天。语言没有根本区别,但我决定用 C 编写。服务器必须能够同时处理多个用户,而用户又必须能够请求所有活动用户的列表并向他们写入消息。如果用户退出网络,他/她将相应地与服务器断开连接并从活动用户列表中删除。
当我添加注册和发布活跃用户列表的功能时,我遇到了一个问题,在新创建的线程中访问服务器不会出现问题,但是如果您从以前创建的线程访问,服务器会卡住。
我认为重点是同时使用内存,尽管这不太可能,因为我是从一台机器上单独输入所有内容的,但为了以防万一我添加了互斥体,它没有帮助。
我还认为它可能是输入缓冲区堵塞,但在某些地方清除它的功能要么什么都不做,要么只会让情况变得更糟。
我最后的猜测是我的实现根本不适合 pthread 线程,我需要使用共享内存和 fork() 重写所有内容,但我想避免这种情况。
这是服务器代码
:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#define PORT 8080
#define BUFFER_SIZE 1024
#define MAX_CONNECTIONS 5
#define REGISTRATION '1'
#define CHECKINFO '2'
#define SENDMESSAGE '3'
#define EXIT '4'
struct Node {
char name[30];
struct Node* next;
};
struct Node* createNode(char* name) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
for(int i = 0; i < 30; i++)
{
newNode->name[i] = name[i];
}
newNode->next = NULL;
return newNode;
}
void CleanStdin()
{
char c;
while ((c = getchar()) != '\n' && c != EOF) {}
}
void pushBack(struct Node** headRef, char* name) {
struct Node* newNode = createNode(name);
if (*headRef == NULL) {
*headRef = newNode;
return;
}
struct Node* temp = *headRef;
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = newNode;
}
void Delete(struct Node** headRef, char* name)
{
struct Node* temp = *headRef;
struct Node* prev = temp;
if (strcmp(temp->name, name) == 0)
{
*headRef = NULL;
return;
}
while (temp != NULL)
{
if (strcmp(temp->name, name) == 0)
{
prev->next = temp->next;
free(temp);
return;
}
prev = temp;
temp = temp->next;
}
}
int Find(struct Node** headRef, char* name)
{
struct Node* temp = *headRef;
while (temp != NULL)
{
if (strcmp(temp->name, name) == 0)
{
return 1;
}
temp = temp->next;
}
return 0;
}
void PrintUsers(struct Node** headRef, int sock, int usersVal)
{
struct Node* temp = *headRef;
for (int i = 0; i < usersVal; i++)
{
send(sock, temp->name, 30, 0);
temp = temp->next;
}
}
pthread_t thread_id;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
struct Node* head = NULL;
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE];
int usersVal = 0;
void* mainFunc()
{
char name[30];
// Чтение сообщения от клиента
while(1)
{
//CleanStdin();
memset(buffer, (char)0, BUFFER_SIZE);
char operation;
if ( operation == REGISTRATION)
{
read(new_socket, name, 30);
int RegCode;
if (Find(&head, name) == 1)
{
printf("User already registered: %s", name);
RegCode = 1;
}
else
{
printf("User successfully registered: %s", name);
pushBack(&head, name);
pthread_mutex_lock(&mutex);
usersVal++;
pthread_mutex_unlock(&mutex);
RegCode = 0;
}
send(new_socket, &RegCode, 4, 0);
}
else if (operation == CHECKINFO)
{
pthread_mutex_lock(&mutex);
printf("\nUserval = %d\n", usersVal);
send(new_socket, &usersVal, 4, 0);
pthread_mutex_unlock(&mutex);
//PrintUsers(&head, new_socket, usersVal);
}
else if (operation == EXIT)
{
close(new_socket);
printf("\nExited!\n");
//Delete(&head, name);
pthread_mutex_lock(&mutex);
usersVal--;
pthread_mutex_unlock(&mutex);
CleanStdin();
break;
}
read(new_socket, &operation, 1);
}
return NULL;
}
int main(int argc, char const *argv[]) {
// Создание сокета
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// Присваивание опции сокету (можно повторно использовать адрес)
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
// Привязка сокета к адресу и порту
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// Ожидание входящих соединений
if (listen(server_fd, MAX_CONNECTIONS) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
// Принятие входящего соединения
while(1)
{
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
printf("\nConnected!\n");
pthread_create(&thread_id, NULL, mainFunc, NULL);
}
return 0;
}
客户:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SERVER_ADDRESS "127.0.0.1"
#define SERVER_PORT 8080
#define BUFFER_SIZE 1024
#define REGISTRATION '1'
#define CHECKINFO '2'
#define SENDMESSAGE '3'
#define EXIT '4'
int Socket(int domain, int type, int protocol)
{
int sock = socket(domain, type, protocol);
if (sock < 0) {
perror("Failed to create socket");
exit(EXIT_FAILURE);
}
return sock;
}
void Inet_pton(int af, const char *src, void *dst)
{
if (inet_pton(af, src, dst) <= 0) {
perror("Invalid server address");
exit(EXIT_FAILURE);
}
}
void Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
if (connect(sockfd, addr, addrlen) < 0) {
perror("Failed to connect to server");
exit(EXIT_FAILURE);
}
}
void Send(int sockfd, const void *buf, size_t len, int flags)
{
if (send(sockfd, buf, len, flags) < 0)
{
perror("Failed to send message to server");
exit(EXIT_FAILURE);
}
}
void Recv(int sockfd, void *buf, size_t len, int flags)
{
if (recv(sockfd, buf, len, flags) < 0) {
perror("Failed to receive message from server");
exit(EXIT_FAILURE);
}
}
// Создание сокета и подключение к серверу
int Conn()
{
int sock = Socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(SERVER_PORT);
Inet_pton(AF_INET, SERVER_ADDRESS, &server_address.sin_addr);
Connect(sock, (struct sockaddr*) &server_address, sizeof(server_address));
return sock;
}
void CleanStdin()
{
char c;
while ((c = getchar()) != '\n' && c != EOF) {}
}
int main() {
char buffer[BUFFER_SIZE];
char symb, operation;
int sock = Conn();
// Первоначальная регистрация
operation = REGISTRATION;
Send(sock, &operation, 1, 0);
printf("\nEnter Name: ");
fgets(buffer, 30, stdin);
Send(sock, buffer, 30, 0);
int RegCode;
Recv(sock, &RegCode, 4, 0);
if (RegCode == 1)
{
printf("You was registered before");
}
else if (RegCode == 0)
{
printf("You were successfully registered");
}
while(1)
{
CleanStdin();
memset(buffer, (char)0, BUFFER_SIZE);
printf("\n1. Get users info");
printf("\n3. Exit\n");
symb = getchar();
switch (symb)
{
// Регистрация пользователя
case '1':
{
operation = CHECKINFO;
Send(sock, &operation, 1, 0);
int usersVal;
Recv(sock, &usersVal, 4, 0);
printf("\nAmount of users = %d\n", usersVal);
/*for(int i = 0; i < usersVal; i++)
{
char name[30];
Recv(sock, name, 30, 0);
printf("\nUser: %s\n", name);
}*/
break;
}
case '3':
{
operation = EXIT;
printf("Exiting...\n");
Send(sock, &operation, 1, 0);
exit(0);
}
}
}
return 0;
}
不严格判断,我自己知道这一切的样子,我写了一天不停,尽我所能集体耕种,只是为了完成。
我评论了我认为可能包含错误的行,但没有区别。如果您按顺序执行以下操作,则会发生错误:
- 启动服务器
- 在另一个终端窗口中运行客户端应用程序(我正在运行 Ubuntu)
- 输入名字
- 在第三个终端窗口中启动一个新的客户端应用程序而不关闭旧的
- 输入名称并退出
- 转到第二个窗口并尝试获取活跃用户列表(现在有一个存根只显示活跃用户的数量)
是否可以修复此问题,或重做已经存在的内容以使其正常工作?如果有任何建议,我将不胜感激。下面是对该问题的更详细描述。
有必要实施 - “CHAT”。
Необходимо реализовать с использованием сетевых сокетов две программы: Клиент и Сервер. Сервер должен быть один. Клиентов может быть много.
Сервер.
После запуска работает в бесконечном цикле слушая закрепленный за ним сокет, пока не поступит команда на его завершения.
К серверу через сокет, могут поступать следующие виды запросов от клиента:
Запрос на регистрацию (сервер должен вести список зарегистрированных клиентов). При поступлении данного запроса проверяется данный список, если клиент там не зарегистрирован, то происходит его регистрация. Клиенту отправляется ответ с подтверждением регистрации или сообщением о том, что он уже зарегистрирован.
Запрос на получение списка зарегистрированных клиентов. В ответ сервер отправляет Имена клиентов, зарегистрированных в данный момент.
Запрос на передачу сообщения клиенту. (в запросе указывается имя клиента получателя и текст сообщения). Если клиент получатель не зарегистрирован в чате, то отправителю сообщения отправляется об этом информация, иначе сообщение пересылается клиенту получателю.
Запрос на отключение от чата. В этом случае клиент удаляется из чата.
Клиент.
После запуска запрашивает параметры сервера (адрес и порт), а также имя клиента и пытается подключиться к серверу – отправляет запрос на регистрацию. Если подключение невозможно (не получен ответ), то завершает свою работу.
Иначе либо ждет команду от пользователя, либо сообщение от сервера.
При поступлении сообщения, выводит имя отправителя и сам текст сообщения.
Пользователь может ввести следующие команды:
Получить список зарегистрированных в чате. В этом случае клиент запрашивает этот список на сервере и выводит его на экране.
Отправить сообщение. Указывается имя клиента получателя и текст сообщения.
Команда на завершение работы. Отправляется соответствующий запрос на сервер и программа завершает свою работу.
При выполнении лабораторной работы обязательно необходимо для взаимодействия использовать сокеты операционной системы.