A am writing a client-server program in C. It sends a directory name and receives a list of files as answer. The problem I have is that it gets stuck in an infinite loop.
If I send only one directory name it works, but if I send a list of directories it never ends and outputs nothing.
Server
while(recv(sock, name, BUFSIZE, 0) > 0){
if ((fddir=opendir(name)) == NULL){
send(sock, strerror(errno), strlen(strerror(errno)), 0);
close(sock);
return 1;
}
send(sock, name, strlen(name), 0);
send(sock, ":", strlen(":"), 0);
send(sock, "\n", strlen("\n"), 0);
while ((dirbuf = readdir(fddir)) != NULL){
buf[0] = '\0';
strcat(buf, dirbuf->d_name);
strcat(buf, "\t");
send(sock, buf, BUFSIZE, 0);
}
}
Client
for (int i=1;i<3;i++){
send(sock, argv[i], strlen(path), 0);
while(recv(sock, buf, BUFSIZE, 0) > 0)
printf("%s", buf);
}
The server waits until all directory names are received, and then the client wait until server send all files in it. How do I trace where the program gets stuck?
TCP is not message based, so you have no way of knowing where the boundaries between two client send() calls is when you call recv() on the server. Thus when you send multiple names back-to-back it is possible for the server to receive them all in a single recv() (or however many bytes you allocated for BUFSIZE). This is probably mangling your directory names, causing opendir to fail. This would be more obvious to you if you were checking for errors from send and recv and Captain Obvlious describes in another answer.
You need to check the calls to recv for errors. It returns 0 if the connection was disconnected and -1 on an error. You are only checking for values > 0 which will not work. The example below shows how to approach checking the errors.
while(true)
{
const int result = recv(sock, buf, BUFSIZE, 0);l
if(result == -1)
{
std::cout << "Error: " << errno << std::endl;
break;
}
else if(result == 0)
{
std::cout << "Disconnected" << std::endl;
break;
}
// process the data here. No errors
}
You should also be checking the value returned by send as it works in the same way.
Related
Ive been trying to build an SNMP trap receiver using C++ and the winsock2 library, I have a thread set up to receive UDP data across port 161... as you would expect, when using a python socket program that i quickly threw together i was able to receive UTF-8 encoded strings and print them to the console. However, when i tried generating a SNMP Trap test event using iDRAC-9 (Dell server remote management tool), i managed to receive the 400 bytes as expected, but only printed 0é☺î☻☺. Frustratingly, the recvfrom() function can only take a char * parameter to receive the data. Im hoping to be able to decode roughly the same detail of data that Wireshark is capturing. Any help would be amazing!
code sample below:
int bytes_received = 0;
BYTE serverBuf[1025]{}; //1 Kilobyte + Null Terminating Character
int serverBufLen = 1024;
struct sockaddr_in SenderAddr {};
int SenderAddrSize = sizeof(SenderAddr);
do {
//Recieve Data
bytes_received = recvfrom(serverSocket, (char*)serverBuf, serverBufLen, 0, (SOCKADDR*)&SenderAddr, &SenderAddrSize);
if (bytes_received == SOCKET_ERROR) {
printf("recvfrom failed with error %d\n", WSAGetLastError());
bytes_received = 0;
}
//Make The String Null Terminated
serverBuf[bytes_received] = '\0';
std::cout << "Bytes Received: ";
std::cout << bytes_received << std::endl;
std::cout << serverBuf << std::endl;
//Send Data Back
char sendBuf[] = "Received\0";
if (sendto(serverSocket, sendBuf, (sizeof(sendBuf) - 1), 0, (SOCKADDR*)&SenderAddr, SenderAddrSize) == SOCKET_ERROR) {
printf("Sending back response got an error: %d\n", WSAGetLastError());
}
} while (active);
Wireshark Info
When attempting to read UDP packets using recvfrom the function returns -1 indicating an error. I of course then call WSAGetLastError to find out what the problem is. The reported error number is 183. I cant seem to find any reference as to what that number means.
Edit:
while (bytesRecv != SOCKET_ERROR)
{
// get data from the server
bytesRecv = recvfrom(m_socket, (char*)&receiveData, sizeof(ReceiveData), 0, (struct sockaddr *) &server_addr, &server_addr_len);
logError("Bytes recieved: ", bytesRecv);
// if data was recieved from the server
if (bytesRecv > 0)
{
//Data packet processing code
}
else
{
if (bytesRecv == SOCKET_ERROR)
{
logError("Error: Reading data: ", WSAGetLastError());
}
}
}
Edit:
void logError(const std::string &text, int errorCode)
{
std::ofstream log_file("error_log_file.txt", std::ios_base::out | std::ios_base::app);
log_file << text << errorCode << "\n";
}
The problem is not with WSAGetLastError() itself. The real problem is that you are calling logError() before calling WSAGetLastError(), and logError() ends up resetting the last error code to 183.
logError() uses a std::ofstream to open a file for appending. On Windows, that operation will ultimately call CreateFile() with the OPEN_ALWAYS flag, for which its documentation states:
Opens a file, always.
If the specified file exists, the function succeeds and the last-error code is set to ERROR_ALREADY_EXISTS (183).
If the specified file does not exist and is a valid path to a writable location, the function creates a file and the last-error code is set to zero.
...
If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.
Internally, WSAGetLastError() simply maps to GetLastError() (a well-known but undocumented implementation detail). So, no matter whether the CreateFile() succeeds or fails in opening the file, the error code reported by WSAGetLastError() will get reset to the result of the open operation.
Your call to logError() is in the wrong place. It needs to be moved inside of your if (bytesRecv > 0) block (BTW, UDP supports 0-length datagrams, so you should be using >= instead of >):
while (true)
{
// get data from the server
bytesRecv = recvfrom(m_socket, (char*)&receiveData, sizeof(ReceiveData), 0, (struct sockaddr *) &server_addr, &server_addr_len);
// if data was received from the server
if (bytesRecv >= 0)
{
logError("Bytes received: ", bytesRecv); // <-- moved here!!!
//Data packet processing code
}
else // if (bytesRecv == SOCKET_ERROR)
{
logError("Error: Reading data: ", WSAGetLastError());
break;
}
}
Alternatively:
while (true)
{
// get data from the server
bytesRecv = recvfrom(m_socket, (char*)&receiveData, sizeof(ReceiveData), 0, (struct sockaddr *) &server_addr, &server_addr_len);
// if data was received from the server
if (bytesRecv == SOCKET_ERROR)
{
logError("Error: Reading data: ", WSAGetLastError());
break;
}
logError("Bytes received: ", bytesRecv); // <-- moved here!!!
//Data packet processing code
}
I am trying to send a file to a server using socket programming. My server and client are able to connect to each other successfully however I am expecting the while loop below to go through the entire file and add it to the server. The issue I am having is that it only send the first chunk and not the rest.
On the client side I have the following:
memset(szbuffer, 0, sizeof(szbuffer)); //Initialize the buffer to zero
int file_block_size;
while ((file_block_size = fread(szbuffer, sizeof(char), 256, file)) > 0){
if (send(s, szbuffer, file_block_size, 0) < 0){
throw "Error: failed to send file";
exit(1);
} //Loop while there is still contents in the file
memset(szbuffer, 0, sizeof(szbuffer)); //Reset the buffer to zero
}
On the server side I have the following:
while (1)
{
FD_SET(s, &readfds); //always check the listener
if (!(outfds = select(infds, &readfds, NULL, NULL, tp))) {}
else if (outfds == SOCKET_ERROR) throw "failure in Select";
else if (FD_ISSET(s, &readfds)) cout << "got a connection request" << endl;
//Found a connection request, try to accept.
if ((s1 = accept(s, &ca.generic, &calen)) == INVALID_SOCKET)
throw "Couldn't accept connection\n";
//Connection request accepted.
cout << "accepted connection from " << inet_ntoa(ca.ca_in.sin_addr) << ":"
<< hex << htons(ca.ca_in.sin_port) << endl;
//Fill in szbuffer from accepted request.
while (szbuffer > 0){
if ((ibytesrecv = recv(s1, szbuffer, 256, 0)) == SOCKET_ERROR)
throw "Receive error in server program\n";
//Print reciept of successful message.
cout << "This is the message from client: " << szbuffer << endl;
File.open("test.txt", ofstream::out | ofstream::app);
File << szbuffer;
File.close();
//Send to Client the received message (echo it back).
ibufferlen = strlen(szbuffer);
if ((ibytessent = send(s1, szbuffer, ibufferlen, 0)) == SOCKET_ERROR)
throw "error in send in server program\n";
else cout << "Echo message:" << szbuffer << endl;
}
}//wait loop
} //try loop
The code above is the setup for the connection between the client and server which works great. It is in a constant while loop waiting to receive new requests. The issue is with my buffer. Once I send the first buffer over, the next one doesn't seem to go through. Does anyone know what I can do to set the server to receive more than just one buffer? I've tried a while loop but did not get any luck.
Your code that sends the file from the server appears to send consecutive sections of the file correctly.
Your code that appears to have the intention of receiving the file from the client performs the following steps:
1) Wait for and accept a socket.
2) Read up to 256 bytes from the socket.
3) Write those bytes back to the socket.
At this point the code appears to go back to waiting for another connection, and keeping the original connection open, and, at least based on the code you posted, obviously leaking the file descriptor.
So, the issues seems to be that the client and the server disagreeing on what should happen. The client tries to send the entire file, and doesn't read from the socket. The server reads the first 256 bytes from the socket, and writes it back to the client.
Of course, its entirely possible that portions of the code not shown implement some of the missing pieces, but there's definitely a disconnect here between what the sending side is doing, and what the receiving side is doing.
buffer will only send once to the server
No, your server is only reading once from the client. You have to loop, just like the sending loop does.
I am new to C++ and socket programming. I studied with Beej's guide so my codes are almost same as the guide, but I am struggling really strange bugs.
First, my server's recv() returns 0. According to document, the client should gracefully close the connection for recv() to return 0. Not really in my case. It returns 0, at the same time, I still receive the data from the client. So, the way Beej's do to receive, does not work for me. Can someone explain how this can be possible?
char buf[MAXDATASIZE];
numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0);
buf[numbytes] = '\0';
the last line here, because numbytes is 0, it sweeps out all message I received. So I had to comment that out. Now, my code looks like this
char buf[MAXDATASIZE];
numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0);
//buf[numbytes] = '\0';
printf("received: %s\n", buf);
It now works with receiving some messages sent by client. However, I did some string manipulation (appending) in the client side, and then sent the message. Now, I send string length of 29 in the client side, but the server receives 41 bytes with strange characters.
What I sent: received: Login#1 Mary 123456 451912345
received: Login#1 Mary 123456 451912345ÿ>É„ÿy#ÿ>Ád
Here is how I receive in the server:
while(1) { // main accept() loop
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
char buf[MAXDATASIZE];
int numbytes;
if (numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0) == -1)
perror("recv");
//buf[numbytes] = '\0'; // this had to be commented out
printf("received: %s\n", buf); // prints out with weird characters
string msgRcved = buf;
close(new_fd);
}
This is how I send from client:
// string loginCredential is loaded with "1 Mary 123456 451912345" at this point
loginCredentials.insert(0, "Login#");
const char* msgToSend = loginCredentials.c_str();
int numbytesSent;
if (numbytesSent = send(sockfd, msgToSend, strlen(msgToSend), 0) == -1)
perror("send");
I'd like to know how my recv receives data while it returns 0 at the first place. And, I'd like to know what I am doing wrong to recv data from client/send data to server.
You have a precedence problem.
This:
if (numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0) == -1)
is equivalent to
if (numbytes = (recv(new_fd, buf, MAXDATASIZE-1, 0) == -1))
and
recv(new_fd, buf, MAXDATASIZE-1, 0) == -1
is 0 whenever recv succeeds.
The same problem is present on the sending end.
There's no reason to write such awkward and error-prone condition.
This is safer:
int numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0);
if (numbytes == -1)
perror("recv");
You have to test 'numbytes' for zero, separately, and if you get it close the socket and exit the read loop, because the peer has closed the connection. Otherwise, and assuming you have also tested for -1, you have to only process 'numbytes' bytes of the buffer. Not all of them. Otherwise you're liable to reprocess bytes you already processed. In this case that might mean restoring the line that null-terminated the buffer, or it might mean this:
printf("%.*s", numbytes, buf);
You are printing whatever garbage was in that stack-allocated buffer, not what the client sent. When recv(2) returns zero, nothing has been placed into the supplied buffer, so this is probably from some previous iteration of the loop.
Notes:
Connected TCP socket is is a bi-directional stream of bytes. This means you might send several of your "messages" and receive them in one chunk on the other side, or the other way around. Read from the socket in a loop until you have enough data to process, i.e. use explicit message separators, or pre-pend a length of your message that follows. This is your application-level protocol.
Don't mix C and C++ string handing like this. std::string has a size() method, use it instead of doing strlen( msgToSend.c_str() ).
Allocating any sizable buffers on the stack, especially ones receiving input from the network is a bad idea.
Printing, or otherwise passing further, unverified network input is a gross security violation leading to all sorts of problems.
Edit 0:
#molbdnilo's answer is the right one. I did not spot the precedence problem in the conditionals. My notes still apply though.
When i am connecting to an IRC server via telnet everything works fine, but in my program there is no respond from server after the greeting message. What's wrong?
PS when i am sending "JOIN #channel" server responds.
fragment of the code:
while (true)
{
ret = recv(pocket, buf, 512, 0);
if (ret == 0 || ret == SOCKET_ERROR)
{
printf("Serwer przerwal polaczenie");
break;
}
buf[ret] = '\0';
input = buf;
printf("%s\n", input.c_str());
if (fTime)
{
isend(pocket, "USER foox 0 0 :foox");
isend(pocket, "NICK foobar");
fTime = false;
}
memset(buf, 0, sizeof(buf));
}
isend function:
bool isend(SOCKET socket,std::string message)
{
int ret = send(socket, message.c_str(), message.size() + 1, 0);
if (!ret){
printf("Nie udalo sie wyslac pakietu: \"%s\"", message);
return false;
}
else
return true;
}
Don't read upon connection. Send the NICK and USER information as per RFC 2812. You're doing it in reverse order that is suggested. Both NICK and USER lines need to be correctly terminated with \r\n and then you can read.
Don't send message.size()+1 - do send message.size(). I don't understand why you were sending message.size()+1 and you didn't answer why in my comments.
If you get stuck I suggest using something like Wireshark with an unencrypted connection and log how IRC clients manage it.
You have three issues:
You do a blocking read, which will wait forever if there's nothing to read.
You need to send a carriage return and newline after each line.
You don't want to send the terminating zero byte.