C Sockets: recv() blocks when all data is downloaded [duplicate] - c

This question already has an answer here:
Differ between header and content of http server response (sockets)
(1 answer)
Closed 4 years ago.
I'm writing a wrapper for Berkley sockets on Windows and Linux. The test program got problem here:
char buf[BUFSIZE];
int res = 0;
while((res = NetRecv(sock, buf, BUFSIZE, 0)) > 0) // 'NetRecv' is pointing to 'recv'
{
buf[res-1] = '\0';
printf("%s", buf);
}
The response is to a HTTP-Get request of a web-page content. The socket is streaming.
The 'NetRecv' is initialized correctly - that is, no type mismatch of the functions' pointers there is, I've checked it.
So, Windows version works flawlessly, the Linux one is stuck after reading all page. Namely, the previous to the last 'NetRecv' call accepts last chunk of the response, outputs it, and the next (last) call just blocks. Closing the terminal causes 'SIGHUP' signal.
Looks like the Linux version just doesn't realize, that it received the last chunk of data and waits for more.
Is it as it should be? Don't understand then, for what reason there is blocking call possibility.
Now, I surely could make non-blocking call and use 'select', but do I really have to?
Thanks in advance)
EDIT: Minimal working example (all checks are omitted and net functions are the standard ones, which also were tested):
int sock = socket(AF_INET, SOCK_STREAM, 0);
// Here getting good IP address of google.com - no problem here
char serv_ip[IPADDR_BUFSIZE];
GetHostAddrByName(AF_INET, "www.google.com", serv_ip, IPADDR_BUFSIZE);
// ip ver site out buf out buf size
// The routine above is made with 'getaddrinfo', to be precise
printf("Current IP of '%s' is '%s'.\n", SERV_URL, serv_ip);
// Copying IP string to address struct
struct sockaddr_in addr;
NetIpFromStr(AF_INET, serv_ip, &addr.sin_addr);
addr.sin_family = AF_INET;
addr.sin_port = NetHtons(80);
connect(sock, (const struct sockaddr*)&addr, sizeof(addr));
const char* msg = "GET / HTTP/1.1\r\n\r\n";
send(sock, msg, strlen(msg), 0);
char buf[BUFSIZE];
int res = 0;
while((res = recv(sock, buf, BUFSIZE-1, 0)) > 0)
{
buf[res] = '\0';
printf("%s", buf);
}
EDIT 2: Important notice: the Windows version also blocks the call, when all the data is read. Closing the terminal just doesn't crash the program, like it happens in Linux. Therefore, the whole question is such: How to realize that all data is read?

The problem is that you are blindly reading from the socket in a loop until an error occurs. Once you have received the entire response, you go back to the socket and keep reading, which then blocks because there is nothing left to read. The only error that can occur at this point is when the connection is closed (or lost), which the server is likely not doing since you are sending an HTTP 1.1 request, where keep-alive is the default behavior for 1.1 (see RFC 2616 Section 8.1 Persistent Connections)
The correct solution is to parse the HTTP response and stop reading from the socket when you reach the end of the response, NOT simply relying on the server to close the socket. Read RFC 2616 Section 4.4 Message Length for how to detect when you have reached the end of the response. DO NOT read more than the response indicates! Once you stop reading, then you can decide whether to close your end of the socket, or reuse it for a new request.
Have a look at this pseudo code for the type of parsing and reading logic you need to use.
Also, your HTTP request is malformed, as you are not sending a required Host header, so no matter what, you will always receive a 400 Bad Request response from any HTTP 1.1 compliant server:
const char* msg = "GET / HTTP/1.1\r\n"
"Host: www.google.com\r\n" // <-- add this!
"\r\n";

The solution was to shutdown the socket for reading, both in Windows and Linux:
// after sending a request:
shutdown(sock, SD_SEND); // or 'SHUT_WR' in Linux
// now read loop
Curiously, 'shutdown' was called in Winsock tutorials too, but I thought that was unnecessary.

Related

Send http URL through C code using visual studios in Windows

I have a video encoder which can start/stop recording by sending a URL.
Start recording: http://192.168.1.5/blah-blah/record/record.cgi
Stop recording: http://192.168.1.5/blah-blah/record/stop.cgi
I don't have to worry about getting a response. I just have to send it.
I was exploring the GET method and also trying to use HappyHTTP library.
Is there a simple way to do it without using any library?
I am using visual studios on Windows.
You can use TCP connection to the server, after connected, you can send the plain HTTP request.
The following program shows this, connecting to google with TCP protocol, sending HTTP get request, and getting the first 2K of the response.
Note: See #Chase great comments below, the program is not complete: usage of gethostbyname should be avoided, as this method is deprecated, error handling should be changed to use WSAGetLastError and not errno, the program should call WSACleanup before it ends.
I don't have time now to fix the program, and I leave it as it, as someone may benefit from it as it is now.
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <winsock.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
#define SERVER_PORT ((u_short )80)
#define SERVER_HOST "www.google.com"
int main(int argc, char* argv[])
{
int ret;
SOCKET sd;
struct hostent* he = { 0 };
struct sockaddr_in addr_info = { 0 };
WSADATA wsaData = { 0 };
WSAStartup(MAKEWORD(2, 2), &wsaData);
he = gethostbyname(SERVER_HOST);
if (he == NULL)
{
printf("failed to get host information for '%s': %s\n", SERVER_HOST, strerror(errno));
exit(-1);
}
sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sd == INVALID_SOCKET)
{
printf("failed to create socket: %s\n", strerror(errno));
exit(-1);
}
addr_info.sin_family = AF_INET;
addr_info.sin_port = htons(SERVER_PORT);
addr_info.sin_addr = *((struct in_addr*)he->h_addr);
memset(&(addr_info.sin_zero), 0, sizeof(addr_info.sin_zero));
printf("connecting to %s ....\n", SERVER_HOST );
ret = connect(sd, (struct sockaddr*)&addr_info, sizeof(struct sockaddr));
if ( ret == -1)
{
printf("failed to connect: %s\n", strerror(errno));
exit(-1);
}
printf("sending GET /index.html request ....\n");
char send_buffer[] = "GET /index.html HTTP/1.1\r\nHost : " SERVER_HOST "\r\n\r\n";
ret = send(sd, send_buffer, sizeof(send_buffer), 0);
if ( ret == -1)
{
printf("failed to send request: %s\n", strerror(errno));
exit(-1);
}
printf("getting response (first 2K)\n");
char response_buffer[2048] = "";
//using sizeof(response_buffer)-1 to have the buffer terminate with zero, even if it is fully consumed by the recv call. this is because I want to print it later on.
ret = recv(sd, response_buffer, sizeof(response_buffer)-1, 0);
if (ret == -1)
{
printf("failed to get response: %s\n", strerror(errno));
exit(-1);
}
printf("received (first 2KB): %s\n", response_buffer);
closesocket(sd);
return 0;
}
program output is:
connecting to www.google.com ....
sending GET /index.html request ....
getting response (first 2K)
received: HTTP/1.1 200 OK
Date: Tue, 29 Dec 2020 08:56:34 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
Server: gws
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Set-Cookie: 1P_JAR=2020-12-29-08; expires=Thu, 28-Jan-2021 08:56:34 GMT; path=/; domain=.google.com; Secure
Set-Cookie: NID=205=a7W2c7M39ojimAWRRgn7nwedmdouUrZfo8uBa1wJeEwo8DUk7ibclM-xwp5ozhKO2BYmcRnQ1l4wwjb_DYfOVsDoi-UdtqBmgySL_KlcG6zMjembghO8OL81e2iHee0_cnlDZvSCCGvPnaC0LHNzFtqeWaYSELF7-1t5Khuv4Yc; expires=Wed, 30-Jun-2021 08:56:34 GMT; path=/; domain=.google.com; HttpOnly
Accept-Ranges: none
Vary: Accept-Encoding
Transfer-Encoding: chunked
...
The barebones way of sending a request to server is """pretty simple""".
Firstly, I'll be assuming you know the exact host and port of the url. I assume this since I cannot access the link you've provided. In most cases, you know the host being the domain you type in the url and the port being 80 for http, or 443 for https - I cannot confirm this for the given link however.
In any case, first things first - the headers you'd require on windows for socket programming is-
#include <WinSock2.h>
#include <WS2tcpip.h>
You'll also need to link the winsock2 library, you can do this by putting #pragma comment(lib, "Ws2_32.lib") after your headers. Or you can put Ws2_32.lib in Project Configuration -> Linker -> Input -> Additional Dependencies in Visual Studio.
Now, winsock2 requires you to initiate the library (and also to close it). You can do this in your main function ideally, since the startup is required for any socket connection to be made-
/* Initialize wsock2 2.2 */
WSADATA wsadat;
if (WSAStartup(MAKEWORD(2, 2), &wsadat) != 0)
{
printf("WSAStartup failed with error %d\n", WSAGetLastError());
return 1;
}
And after everything is done-
/* Cleanup wsock2 */
if (WSACleanup() == SOCKET_ERROR)
{
printf("WSACleanup failed with error %d\n", WSAGetLastError());
return 1;
}
Now you need to obtain information about the server you want to connect to, we do that using getaddrinfo. (Relevant posix docs)
static struct addrinfo const hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM };
struct addrinfo* addrs;
int gai_retcode = getaddrinfo("192.168.1.5", "http", &hints, &addrs);
if (gai_retcode != 0)
{
fprintf(stderr, "Error encountered during getaddrinfo: %s\n", gai_strerrorA(gai_retcode));
return 1;
}
Let's walk through this a bit, getaddrinfo takes a node parameter - which is either a hostname string or an ip address string.
The second parameter is service - which is a string specifying the port to connect to, or the service being used (http, https, telnet etc - you can find a list of services in the IANA port list or in /etc/services of your linux box).
The third parameter is the address of an addrinfo struct containing "hints" to aid in selecting the right address. For connecting to a server, you usually need to set only the ai_family and ai_socktype members, everything else should be 0 initialized (which is what the compound initializer does automatically). AF_UNSPEC on ai_family means "find me either ipv4 or ipv6 addresses - no particular requirement", SOCK_STREAM on ai_socktype means "find me stream servers addresses" (aka TCP servers, not Datagram servers). HTTP is a stream based protocol.
The final parameter is the address of a pointer to an addrinfo struct. This is where the results of the lookup are stored in.
getaddrinfo returns 0 on success and fills up the pointer to addrs pointer. addrs is now a linked list of addresses returned from the lookup - the first one in the list is directly accessible, the next one is accessible through .ai_next and so on until you reach NULL. The regular shenanigans.
For websites having multiple servers around the world, getaddrinfo will return many addresses. They are arranged in the most relevant way determined by your machine. That is, the first one is the address most likely to work well. But you should ideally try the next ones in the linked list in case the first one fails.
For non zero returns, you can use gai_strerror to print the error message. This is named gai_strerrorA on windows for whatever reason. There's also a gai_strerror on windows but it returns a WCHAR* as opposed to a char*. On linux, you'll be using gai_strerror, not gai_strerrorA. Relevant linux docs on gai_strerror
Now that you have a list of addresses, you can create a socket and connect to one of them-
int sfd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
if (sfd == INVALID_SOCKET)
{
fprintf(stderr, "Error encountered in socket call: %d\n", WSAGetLastError());
return 1;
}
if ((connect(sfd, addrs->ai_addr, addrs->ai_addrlen)) == -1)
{
fprintf(stderr, "Error encountered in socket call: %d\n", WSAGetLastError());
closesocket(sfd);
return 1;
}
NOTE: This is only trying the first address in the linked list, if the first one fails - it just gives up. This isn't ideal for a real world usecase. But for your specific usecase, it probably won't matter.
Pretty self explanatory, socket takes the ai_family, ai_socktype and ai_protocol values from your addrinfo structure and creates a socket. It returns INVALID_SOCKET on failure (this is just -1 on linux). Relevant linux docs on socket.
connect takes the socket descriptor you just got from socket, and the ai_addr and ai_addrlen values from your addrinfo structure to connect to the server. It returns SOCKET_ERROR on failure (again, just -1 on linux). Relevant linux docs on socket.
Now all the low level socket shenanigans is done. Next comes HTTP shenanigans. To make an HTTP request, you need to know the HTTP spec, you can read the entire thing here.
Just kidding, here's a more digestible version - courtesy of MDN.
But basically, the format of a message is sort of like this-
<HTTP-VERB> <REQUEST-PATH> HTTP/<VERSION>\r\n[HEADERS-TERMINATED-WITH-CRLF]\r\n[BODY]
So a GET request to /blah-blah/record/record.cgi using HTTP 1.0 and no headers (no body either, since this is a GET request) looks like-
GET /blah-blah/record/record.cgi HTTP/1.0\r\n\r\n
Before moving on, I need to mention why I'm demonstrating HTTP/1.0 instead of the more sensible for the times - HTTP/1.1. Since not all servers actually support 1.0 fully. The real difference between the 2 is that 1.1 requires a Host: header, and it allows keeping the connection alive - which is more efficient for multiple requests to the same server.
About the Host: header, if you know what hostname the server is expecting in the request header, great! Change it to HTTP/1.1 and put the Host: hostname in. Where hostname is...well, the hostname. For all I know, your server might just be accepting 192.168.1.5 as the hostname, but it also may not.
The second part is the keep-alive connection part. A keep-alive connection gets a bit complicated with blocking sockets. If you decide to use a keep-alive connection and try to recv the response from the server, when there is nothing more to read from the stream, recv will keep blocking until there's something - since the connection is still alive. There are ways to solve this ofcourse, the behaviors of which vary from linux to windows. So I'd recommend just setting the Connection header to close if using HTTP/1.1. I assume this would be "good enough" for your usecase. The HTTP request would then look like-
GET /blah-blah/record/record.cgi HTTP/1.1\r\nHost: hostname\r\nConnection: close\r\n\r\n
Now, to send the request-
char const reqmsg[] = "GET /blah-blah/record/record.cgi HTTP/1.0\r\n\r\n";
if (send(sfd, reqmsg, sizeof reqmsg, 0) == SOCKET_ERROR)
{
fprintf(stderr, "Error encountered in send call: %d\n", WSAGetLastError());
closesocket(sfd);
return 1;
}
send takes the socket descriptor, the message, and the message length and returns the number of bytes sent or SOCKET_ERROR on failure (-1 on linux). Relevant docs for linux on send
There's some peculiarity here that should be noted. send may send less bytes than the length of the full message. This is normal for stream sockets. When this happens, you have to send the rest by calling send again and starting from where send left off (add the return value to the message string pointer to obtain the continuation point). This is omitted for brevity but it is highly unlikely to actually happen for such a short message. If you have any machine from the last decade - send will send that tiny message in full.
Please don't rely on that in an actual project though.
Congrats! you've successfully sent a request to the server. Now you can receive its response. The response format is like this-
HTTP/<VERSION> <STATUS-CODE> <STATUS-MSG>\r\n[HEADERS-TERMINATED-WITH-CRLF]\r\n[BODY]
You don't have to receive if you don't want to, the data will just be sitting there in the stream buffer, which will "vanish" when you close the socket. But here's a small example on parsing out just the status code-
int status;
char stats[13];
if (recv(sfd, stats, 13, 0) != 13)
{
fprintf(stderr, "Error encountered in recv call: %d\n", WSAGetLastError());
closesocket(sfd);
return 1;
}
sscanf(stats, "HTTP/1.0 %d ", &status);
recv, works much like send, just the other way around. It fills the buffer you pass as its second parameter and the maximum number of bytes it'll read can be mentioned in its third parameter. The return value of recv is the number of bytes read. Relevant docs for linux on recv
The peculiarity mentioned before is present here too. recv may read less than the bytes you mention in the third param. But with any half decent internet connection, 13 bytes will definitely be read in one go.
Why 13 bytes? That's the length of the HTTP/1.0 + a space + 3 digit status code + a space. We're only interested in the status code for this. sscanf will then parse out the status code (assuming the server responded correctly) and put it into status.
If you want to read any marginally large size (and/or execute multiple recv calls), be sure to do it in a loop and use the return value of the number of bytes read to appropriately use the buffer.
After all of this, be sure to call freeaddrinfo on addrs and closesocket on sfd.
Further reading: MS docs on Winsock2
On linux, everything is very similar - but with much less headaches :) - I recommend checking out beej's guide to network programming if you're looking for implementing this on linux.
(In fact, it's a great read for windows programmers as well! Up to date with modern functions that support ipv4 and ipv6 seamlessly, no manual and painful struct filling etc)
Everything will be super similar, you just need to change the headers - get rid of the stupid windows specific startups and replace WSAGetLastError inside fprintf to just a regular perror.
Edit: Replacement for compound literal (static struct addrinfo const hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM };)-
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
Suffice to say, if you have access to a compiler that supports C99 and above - use compound literals instead - it's plain better :)

Send returns 29200 on ARM

I'm using a c program to respond to API calls. I want to reply using JSON.
I created a streaming socket listening on my port and create a GET request using a browser (firefox in my case). I then reply using the "send" method based on the request received.
The problem is when my reply is bigger than 29200 bytes. Then the send method returns 29200 and only sends the first 29200 bytes, then it just stops. I cannot find why it would stop at this number.
I tried google and found:
C++ socket programming Max size of TCP/IP socket Buffer?
My socket is blocking, so the send() function should block until all data is sent.
I also tried to find if linux blocks anything, but when I checked (not sure how I checked, cannot find the stackoverflow issue describing this) it was set to something way bigger than 29200.
I would like to know why my socket stops at 29200 and, if possible, how I can change the socket to make it send more data?
Edit:
Did some testing with the following results:
Created a test program to just send back 29999 bytes of data: https://pastebin.ca/4010317
I'm using curl to receive the data using
curl -X GET -i 'http://:12345'
When running on my computer the response is:
received: -1 bytes
received:
sent 29999 bytes
I can see that on my computer (x64) the receive does not work, but the send does (Curl does receive the data)
but when running on the ARM device the response is:
received: 83 bytes
received: GET / HTTP/1.1
Host: 192.168.1.118:12345
User-Agent: curl/7.47.0
Accept: */*
J
sent 29200 bytes
Here, curl receives 29200 bytes of data.
When trying to loop the send (https://pastebin.ca/4010318), the result is:
received: -1 bytes
received:
sent 29199 bytes
Here, curl receives 29200 bytes, the second send returns -1. So looping is not possible.
I will keep trying, but the help is appreciated.
Your code work.
Well, to be more exact, it work despite many little misusing function.
First, I asked you an mvce : you give a code with some useless line of code that add just complexity : if you don't need the information on the client connexion, the you can pass NULL to accept.
accept4(socketId, NULL, NULL, SOCK_NONBLOCK);
This way, we do not have 2 useless variables.
Second : Checking for error is cool, but displaying something (or logging) is better, because you can have hint why this doesn't work.
For example, the "received: -1 bytes" that you interpret as non-working is in fact working : The error (errno) is EAGAIN, meaning that since your socket is non-blocking, the data is not currently available so you have to loop your recv to read the incomming data. Looping recv will "solve" your false problem.
And finally : No, you do not loop your send either : you just merely detect if you haven't send all the data and try again one more time : do a LOOP !
Edit :
You can see how I do your "initServerSocket" function for the "check error and logging" part :
int InitServerSocket(int portNum, int nbClientMax)
{
int socketId = -1;
struct sockaddr_in addressInfo;
int returnFunction = -1;
if ((socketId = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
// Log !
goto END_FUNCTION;
}
addressInfo.sin_family = AF_INET;
addressInfo.sin_addr.s_addr = htonl(INADDR_ANY);
addressInfo.sin_port = htons(portNum);
if (bind(socketId, (struct sockaddr *)&addressInfo, sizeof(addressInfo)) == -1) {
// Log !
goto END_FUNCTION;
}
const int flags = fcntl(socketId, F_GETFL, 0);
fcntl(socketId, F_SETFL, flags ^ O_NONBLOCK);
// Error detection & log ?
if (listen(socketId, nbClientMax) == -1) {
// Log !
goto END_FUNCTION;
}
returnFunction = socketId;
socketId = -1;
/* GOTO */END_FUNCTION:
if (socketId != -1) {
close(socketId);
}
return (returnFunction);
}
I finally found the issue, the problem was that the
accept4(socket, (struct sockaddr *) &addrInfoFromClient, &sizeAddrInfo, SOCK_NONBLOCK);
was setting the socket to non blocking, while in the init the socket was set to blocking. On the ARM device, this caused the "write" function to stop writing after 29200 bytes (not sure why).
When i changed the accept4 to:
accept(socket, (struct sockaddr *) &addrInfoFromClient, &sizeAddrInfo);
It worked.

C Sockets recv: connection reset by peer

I'm trying to use sockets to get a small JSON test file, which is hosted on my website (http://a-cstudios.com/text.json). When I do this
long numbytes;
char *request = malloc(sizeof(char) * 300);
sprintf(request, "GET %s \r\nHOST:%s \r\n\r\n", restOfURL, baseServer);
// restOfURL = "/text.json" baseServer = "www.a-cstudios.com"
send(sockfd, request, strlen(request) + 1, 0);
char buf[1024];
if ((numbytes = recv(sockfd, buf, 1024-1, 0)) == -1) {
perror("recv");
}
I get recv: connection reset by peer. But if I use the same code, where restOfURL is /index.html and baseServer is www.google.com, this works fine, and buf will contain the text of index.html. Why won't this work for the file on my website?
Since you didn't post full code, I am going to take a stab at it and make an assumption:
You populate the format string of "GET %s \r\nHOST:%s \r\n\r\n" with restOfURL and baseServer
However, during the time of the sprintf call restOfURL isn't initilized so you're pushing garbage data into the first %s
Either post more of your code or make sure you initialize resOfURL
As #Kninnug pointed out, you need the HTTP version field (e.g., HTTP/1.1) at the end of the first line of the request. I just want to point out that you should not include the null terminator when you send the request. That is, change the send statement to
send(sockfd, request, strlen(request), 0);
Also, it is a good practice to always use snprintf instead of sprintf to prevent buffer overflow, although to be really safe you still need to check for truncation.

Trouble when trying to define a write_all() socket function in C

I am currently using this function in a C client program. Everything seems to work fine but when the server to which this client is connected is shut down, write_all() returns 4 (that's len) instead of the expected -1.
int write_all(int sock, const void *buf, size_t len)
{
int buf_size = len;
while(len > 0)
{
int result = write(sock, buf, len);
if(result < 0)
{
if(errno == EINTR)
continue;
return result;
}
buf += result;
len -= result;
}
return buf_size;
}
Is there anything I am missing in this function? Is there any other function I can call beforehand to make sure the server is still up?
Thanks
You say "shut down", do you mean that you switch the power off, without gracefull TCP closing?
In that case write call returns with success. Data is in TCP sending buffer, and TCP stack does not yet know that peer is down. Program will get EPIPE or other error during later calls.
TCP stack will try retransmission a while, before making decision of connection failure.
To me this looks like you won't get around implementing some sort of hand shake.
As if it's not enough for your sender to know the data it send had been fully received (what I assume is the case), but it also needs to know if any kind on processing had been done on it by the receiver, you expect more from the socket's mechanics than they can provide ...
The sockets are just the transmitter.
Note: I'm assuming TCP here.
From the return value, I gather that the client managed to write 4 bytes to the send buffer before learning that the server closed its end or otherwise disappeared. If it disappeared without proper closing, the only way to know would be a timed-out send. The next write, shutdown or close after that will get the error.
If you want to get prompt notification of disappearing endpoints without having to constantly send data, you can activate the socket keepalive option. In Linux, that would be a setsockopt(..., SOL_SOCKET, SO_KEEPALIVE, ...), and TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT at the SOL_TCP level.

how read all the server's responses from a socket?

I have a very big problem... I'm working with sockets in C. I send a request to the server which sends me many responses. The problem is that the client receives the first response and then the connection is closed. What can I do? I tried with setsockopt()... SO_KEEPALIVE or SO_LINGER but I haven't resolved the problem. Can you help me? Thanks a lot
To be more clear here is the code. The socket is automatically closed after a certain amount of time or after the client received the first response... I'm not sure.
char* demarre_client( client_args * c_args,char* message, /*char* SERVEUR_PORT*/int port){
int socket_client=socket(PF_INET,SOCK_STREAM,0);
memset(&(c_args->adresse_serveur),0,sizeof(c_args->adresse_serveur));
c_args->adresse_serveur.sin_family=AF_INET;
c_args->adresse_serveur.sin_addr.s_addr=inet_addr(SERVEUR_IP);
//int port=APHash(SERVEUR_PORT,strlen(SERVEUR_PORT));
c_args->adresse_serveur.sin_port=htons(port);
int err=0;
if ((err=connect(socket_client, (struct sockaddr *) &(c_args->adresse_serveur), sizeof(c_args->adresse_serveur)))<0)
{
printf("CONNECT error %d\n", errno);
exit(-1);
}
if (send(socket_client, message, strlen(message), 0)!=strlen(message))
{
printf("send error!!\n");
exit(-2);
}
char* raspFin=(char* )malloc(sizeof(char)*1024);
strcpy(raspFin,"");
int num=-1;
int nn=0;
char* rasp=(char* )malloc(sizeof(char)*1024);
memset (rasp, 0, 1024 * sizeof(char ));
/* here it reads the first response and after he get out of while */
while ((num=recv(socket_client, rasp,1024,MSG_WAITALL))>0)
{
printf("recu %s mun=%d\n" , rasp,num);
strcat(raspFin,rasp);
strcat(raspFin,"\n");
rasp=(char* )malloc(sizeof(char)*1024);
memset (rasp, 0, 1024 * sizeof(char ));
}
if (num<0)
{
printf("rasp error!!\n");
exit(-3);
}
Are you sure you don't get all responses on first call?
TCP/IP is stream protocol without flow control built-in so different messages, sent using separate send() calls, can be received in one recv(). Because you use printf(), it prints the buffer until it sees null-terminator - maybe other responses beyond the terminator?
Try to use some flow control, like sending message length prefix or using some special characters (like STX/ETX, but make sure your message doesn't contain such characters). You'd need to implement some flow-control anyway if you plan to use this software.
For now try replacing your printf() with
char *ptr;
for (ptr = buffer; ptr <= buffer + num; ptr += strlen(ptr)+1;)
printf("%s\n", ptr);
It will print all strings from your response buffer.
And you don't need malloc() inside the loop - you leak memory.
BTW SO_KEEPALIVE and SO_LINGER have nothing to do with this problem.
My suggestion would be to fire up Wireshark network analyzer and see what's happening packet-wise. In filters set
tcp.srcport == <insert_server_port> || tcp.dstport == <insert_server_port>
You should see what data actually gets sent to and who closes the connection (sends FIN/RST packets).

Resources