为什么我总是在C++套接字的服务器端接收数据



打开客户端和服务器之间的连接后,我需要使用命令read();处理发送到服务器的任何写入命令(即当客户端write();服务器时,服务器应该立即read();(。

这听起来是一个微不足道的问题。首先,我从客户端发送了 58 个字节。但是,我总是在服务器端接收大量数据。在这里,您可以找到代码的相关部分:

int sockfd, newsockfd;//, n0,n1,n2;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
int reuse=1;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
    cerr << "ERROR opening socket"<< endl;
if (setsockopt(sockfd, SOL_SOCKET,SO_REUSEADDR,&reuse, sizeof(int)) == -1)
    cerr << "ERROR on reusing port"<< endl;
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(iport);
serv_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
    cerr << "ERROR on binding"<< endl;
cout << "Listening on port: "<< iport<< endl;
listen(sockfd,1);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
    cerr << "ERROR on accept" << endl;
while (1) {
    size_t msgSize=0;
    int n = read(newsockfd,&msgSize,sizeof(size_t));
        cout << "Breakpoint " << msgSize<< endl;
    // Reading bytes size from socket until 10MB
    if ( n> 0 && msgSize< 10485760) {
        byte bytes [msgSize];
        if (read(newsockfd, bytes, msgSize) > 0) {
            char ip [16];
            memset (bytes + msgSize, '', MSGMAXSIZE - msgSize - 1);
            if (read(newsockfd,ip,15) > 0) {
                string cIP = (string)ip;
                //cout << "Sender Ip: " << cIP << endl;
                process p = currentView.getProcess(cIP);
                message m(bytes,p);
                cout << "*************************" << endl
                     << "Message received:"  << endl
                     << "*****************" << endl;
                m.print();
            }
        }
    }
}

这是我得到的结果:

Listening on port: 4444
Connected to: 127.0.0.1:6666
Breakpoint 58
*************************
Message received:
*****************
Message text: I am trying to send a message
Message size: 58
Message sender: 127.0.0.1
Message stability: 0
**************************************************
Breakpoint 825634866
Breakpoint 808600630
Breakpoint 842478647
Breakpoint 959854903
Breakpoint 926303542
Breakpoint 876032050
Breakpoint 808601142
Breakpoint 892744503
Breakpoint 875971894
Breakpoint 825634866
Breakpoint 1144401970
Breakpoint 859256118
Breakpoint 825635639
Breakpoint 892745526
Breakpoint 775369265
Breakpoint 774909488
Breakpoint 14897
Segmentation fault

在这里,您可以从客户端找到代码的相关部分:

while (1)
    {
    if (!bufferMsg(m)) break;
    }
bool bufferMsg(message m) // Sends a message (m) to a process (p)
{
    mtx.lock();
    if(fifoBuffer.size() < 5)
    {
        fifoBuffer.push_back(m);
        size_t sizeMsg = m.getHeader().sizeMsg;
        byte * bytes = m.getBytes();
        if (!write(sendsockfd,&sizeMsg,sizeof(size_t)) || !write(sendsockfd,bytes,sizeMsg) || !write(sendsockfd,(char*)m.getHeader().sender.getIp().c_str(),strlen(m.getHeader().sender.getIp().c_str())))
        cerr << "ERROR writing to socket"<< endl;
        mtx.unlock();
        return true;
    }
    else{
        mtx.unlock();
        return false;
    }
}

在这里,您可以找到消息的标头:

typedef struct HeaderType {
    size_t sizeMsg;
    process sender; // The header.sender process
    //view currentView; // the Current view
    //iClock C; // reserved for later use
    bool stability; // reserved for later use
}HeaderT;

PS:术语messageprocess是我已经创建的一些类,但出于我们的关注。如果您需要更多说明或信息,请随时。

  • 我的印象是你认为客户端写入应该是阻塞的,并等到数据被服务器吃掉。操作系统可以自由地在 TCP 流上提供任意数量的字节。

  • 代码中有很多 if if(read(newsockfd, bytes, msgSize) > 0),您似乎默默地假设读取要么完全失败,要么提供您正在等待的数据量。情况不一定如此。

这:

 if ( n> 0 && msgSize< 10485760) {
        byte bytes [msgSize];

很危险,因为字节数组(我假设它是一个 typedef(被分配到堆栈上,并且我假设地球上没有操作系统接受 10MB 的局部变量。但我可能是错的,甚至现代编译器也开始默默地将其分配给堆上。它是第一次消息大小 <10MB 时段错误的最佳候选者。最好做这样的事情:

std::auto_ptr<byte> bytes(new byte[msgSize]);

为了阅读msgSize,最好做一些事情:

int n = 0;
int nn = 0;
while((nn=read(newsockfd,((char *)&msgSize)+n,sizeof(size_t)-n)>0 
   && n<sizeof(size_t)) {
 n+=nn;
}

在客户端站点上,您可以执行以下操作:

write(sendsockfd,(char*)m.getHeader().sender.getIp().c_str(),strlen(m.getHeader().sender.getIp().c_str())

要传输类似 IP 的东西(我假设像 88.1.2.250 这样的字符串(,但在服务器端,您可以像这样阅读它:

read(newsockfd,ip,15)

不需要相互适应。这将导致您的读取出现帧偏移,下一个 msgSize 是假的。我可以假设你读过的第一个消息大小是正确的吗?假设第一次读取实际上提供了 sizeof(size-t(。

size_t msgSize=0;
int n = 0;
do{
    int t=read(newsockfd,((char*)&msgSize) + n, sizeof(size_t) - n);
    if(t<0)
        continue; //if no data is available (in nonblocking mode, or on timeout)
    if(t==0)
        break; //connection closed
    n+=t; //increase counter n by the amount actually read
} while(n<sizeof(size_t));
cout << "Breakpoint " << msgSize<< endl;
// Reading msgSize bytes from socket until 10MB
if ( n> 0 && msgSize< 10485760) {
    byte bytes [msgSize];
    n=0;
    int t;
    while((t=read(newsockfd, bytes + n, msgSize - n)) > 0 //if something was read
        && (n+=t)<msgSize //and the total is below msgSize, we continue reading
        || t<0)  //or when there is no data available, we will give it another attempt
    {
    }
    if(t>0){
        cout << "successful: " << n << endl;
    } else {
        cout << "only " << n << " of " << msgSize << "read" << endl;
    }
}

棘手的部分解释:

((char*)&msgSize) + n

这会将指针强制转换为size_t指向 char 的指针+ n 将指针递增 n 倍于它指向的类型的大小。

(t=read(newsockfd, bytes + n, msgSize - n)) > 0

赋值返回分配的值。它必须在括号内,因为如果没有括号,>比较的布尔结果将被分配给 t

旁注:

不应将整数值的原始二进制表示形式发送到另一台计算机。发送方可能使用 MSB 字节顺序,而接收方可能使用 LSB。应使用提供的方法从主机字节顺序转换为网络字节顺序。它们被称为htonlntohl(h:host,to:to,n:network l:long [4字节](。

最新更新