【发布时间】:2021-03-28 18:44:18
【问题描述】:
我在客户端和服务器之间有一个 TCP 多线程通信。我不知道为什么第三次使用 read 函数时它只给出 0。
- 客户端代码
while(1)
{
/* citirea raspunsului dat de server
(apel blocant pina cind serverul raspunde) */
if (read(sd, msg, 1024) < 0) {
perror("[client]Eroare la read() de la server.\n");
return errno;
}
/* afisam mesajul primit */
printf("[client]Mesajul primit este: %s\n", msg);
if(strcmp(msg, "Conexiune incheiata") == 0)
break;
memset(mesaj,0,256);
cin>>mesaj;
/* trimiterea mesajului la server */
if (write(sd, mesaj, 1024) <= 0) {
perror("[client]Eroare la write() spre server.\n");
return errno;
}
}
/* inchidem conexiunea, am terminat */
close (sd);
- 服务器主代码和我发现问题的函数
#include "ConectareServer.h"
using namespace std;
/* portul folosit */
#define PORT 2825
/* codul de eroare returnat de anumite apeluri */
extern int errno;
typedef struct thData{
int idThread; //id-ul thread-ului tinut in evidenta de acest program
int cl; //descriptorul intors de accept
}thData;
static void *treat(void *); /* functia executata de fiecare thread ce realizeaza comunicarea cu clientii */
void raspunde(void *);
#pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop"
int main ()
{
struct sockaddr_in server; // structura folosita de server
struct sockaddr_in from;
int nr; //mesajul primit de trimis la client
int sd; //descriptorul de socket
int pid;
pthread_t th[100]; //Identificatorii thread-urilor care se vor crea
int i=0;
/* crearea unui socket */
if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("[server]Eroare la socket().\n");
return errno;
}
/* utilizarea optiunii SO_REUSEADDR */
int on=1;
setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
/* pregatirea structurilor de date */
bzero (&server, sizeof (server));
bzero (&from, sizeof (from));
/* umplem structura folosita de server */
/* stabilirea familiei de socket-uri */
server.sin_family = AF_INET;
/* acceptam orice adresa */
server.sin_addr.s_addr = htonl (INADDR_ANY);
/* utilizam un port utilizator */
server.sin_port = htons (PORT);
/* atasam socketul */
if (bind (sd, (struct sockaddr *) &server, sizeof (struct sockaddr)) == -1)
{
perror ("[server]Eroare la bind().\n");
return errno;
}
/* punem serverul sa asculte daca vin clienti sa se conecteze */
if (listen (sd, 2) == -1)
{
perror ("[server]Eroare la listen().\n");
return errno;
}
/* servim in mod concurent clientii...folosind thread-uri */
while (1)
{
int client;
thData * td; //parametru functia executata de thread
int length = sizeof (from);
printf ("[server]Asteptam la portul %d...\n",PORT);
fflush (stdout);
// client= malloc(sizeof(int));
/* acceptam un client (stare blocanta pina la realizarea conexiunii) */
if ( (client = accept (sd, (struct sockaddr *) &from, reinterpret_cast<socklen_t *>(&length))) < 0)
{
perror ("[server]Eroare la accept().\n");
continue;
}
/* s-a realizat conexiunea, se astepta mesajul */
// int idThread; //id-ul threadului
// int cl; //descriptorul intors de accept
td=(struct thData*)malloc(sizeof(struct thData));
td->idThread=i++;
td->cl=client;
pthread_create(&th[i], NULL, &treat, td);
}//while
};
#pragma clang diagnostic pop
static void *treat(void * arg)
{
struct thData tdL;
tdL= *((struct thData*)arg);
printf ("[thread]- %d - Asteptam mesajul...\n", tdL.idThread);
fflush (stdout);
pthread_detach(pthread_self());
raspunde((struct thData*)arg);
/* am terminat cu acest client, inchidem conexiunea */
printf ("[Server]Inchidem conexiunea cu threadul %d\n",tdL.idThread);
close ((intptr_t)arg);
return(NULL);
}
void raspunde(void *arg)
{
struct thData tdL;
tdL= *((struct thData*)arg);
ConectareServer client;
cout<<tdL.cl;
client.Conectare(tdL.cl);
}
void ConectareServer::Conectare(int arg) {
fd=arg;
cout<<fd;
memset(mesaj_catre_client, 0, 1024);
sprintf(mesaj_catre_client,
"Conectare reusita, pentru a continua, trebuie sa te loghezi. Sintaxa comenzilor este urmatoarea: \n<login>, pentru a realiza logarea la server\n"
"<quit>, pentru a inchide programul");
write(arg, mesaj_catre_client, sizeof(mesaj_catre_client));
VerifComanda(arg);
}
void ConectareServer::VerifComanda(int fd) {
memset(mesaj_catre_client, 0, 1024);
memset(mesaj_de_la_client, 0, 1024);
read(fd, mesaj_de_la_client, sizeof(mesaj_de_la_client));
printf("[server]Mesajul a fost receptionat...%s\n", mesaj_de_la_client);
if(strcmp(mesaj_de_la_client, "login")==0){
Logare client(fd);
client.Login();
cout<<"Intra in Logare";
}
if (strcmp(mesaj_de_la_client, "quit") == 0) {
sprintf(mesaj_catre_client, "Conexiune incheiata");
write(fd, mesaj_catre_client, sizeof(mesaj_catre_client));
}
if(strcmp(mesaj_de_la_client, "login") != 0 && strcmp(mesaj_de_la_client, "quit") != 0)
{
cout<<"Crash";
}
}
和
void Logare::Login() {
memset(mesaj_catre_client, 0, 256);
char user[10];
char parola[8];
sprintf(mesaj_catre_client,"Introduceti user-ul: ");
write (fd, mesaj_catre_client, sizeof(mesaj_catre_client));
read (fd, user, sizeof (user));
printf ("[server]Mesajul a fost receptionat...%s\n", user);
memset(mesaj_catre_client, 0, 256);
sprintf(mesaj_catre_client,"Introduceti parola: ");
write (fd, mesaj_catre_client, sizeof(mesaj_catre_client));
read (fd, parola, sizeof (parola));
printf ("[s]Mesajul a fost receptionat...%s\n", parola);
if(Verificare_Logare(user,parola) == 1)
{
// Logare reusita
Meniu client(fd, user);
client.MeniuAplicatie();
}
}
我得到了正确的登录和用户读数,但是当涉及到 parola 的读取时,它只返回 0,然后我才能在客户端窗口中输入。
【问题讨论】:
-
如果您可以将您的 cmets [和消息] 从罗马尼亚语翻译成英语,这可能会有所帮助。我知道他们是罗马尼亚人的原因是我将:
parametru functia executata de thread输入谷歌翻译并返回:parameter function executed by the thread。此外,这似乎是c++和 notc,因此您可能需要删除c标记。 -
但是,乍一看,您不是检查一些函数的返回值(例如
read和write)。而且,(例如)你正在做:sprintf(mesaj_catre_client,"Introduceti parola: "); write (fd, mesaj_catre_client, sizeof(mesaj_catre_client));但是,你可能会更好:size_t len = sprintf(mesaj_catre_client,"Introduceti parola: "); write (fd, mesaj_catre_client, len + 1);。 -
至少有两个主要问题: 1. 你总是写一个完整的缓冲区,所以如果用户输入“hi”你发送“hi\0\0\0\0\0\0\ 0\0\0\0\0[...]”。 2. 您假设客户端的一次写入变为服务器上的一次读取。这通常不是真的,当你写大缓冲区和读取小缓冲区时绝对不是真的。由于您似乎想要一个基于行的协议,您应该看看如何读取和写入行。
-
由于您正在执行
read(...,whatever,sizeof(whatever)),您可能需要查看返回值,因为 firstread可能会读取第一条消息,但缓冲区具有初始值第二条消息的一部分。 (例如)如果发送者发送两个消息(例如hello和world),接收者可能会收到helloworld作为第一个[也是唯一的]消息,因为它得到10个字节而不是仅仅5. -
代码cmets请使用英文。您可能需要阅读Advanced Linux Programming,然后阅读更多关于syscalls(2) 的信息,包括poll(2)。考虑使用Qt 或POCO 或Wt 或GTK 的某些部分。