I'm having trouble with a socket application I'm programming in C++. I'm doing my programming with Bloodshed Dev-Cpp on Windows XP. I made a class for handling all the message transfers and have a client and server program that both use that class for handling their services. The application itself is very basic, the only intent I have for it is to get all this to work.
The client, which is for sending messages, works like I expect it to. If my server is running, it doesn't have any errors when sending a message. If it's not running it'll pass an error. But my server continuously accepts weird gibberish. It's always the same data. When it receives the message there is no effect. If I have my client try to identify the server, it gets back gibberish.
I have included my source code here. The linker also brings in two extra parameters: -lwsock32 and an inclusion of the library libws2_32.a, which came with Dev-Cpp.
Here's the header for my Messager class:
#ifndef MESSAGER
#define MESSAGER
#include <string>
class Messager{
private:
int sendSocket;
int listenSocket;
public:
void init(void);
bool connect(std::string ip, std::string port);
bool bind(std::string port);
void listen(void);
void send(std::string message);
std::string receive(void);
};
#endif
These are my definitions for the Messager class:
#include "Messager.h"
#include <winsock2.h>
#include <sys/types.h>
#include <ws2tcpip.h>
#include <windows.h>
void Messager::init(void){
WSADATA wsaData;
WSAStartup(MAKEWORD(1,1), &wsaData);
}
bool Messager::connect(std::string ip, std::string port){
struct addrinfo hints;
struct addrinfo *res;
bool success = false;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
getaddrinfo(ip.c_str(), port.c_str(), &hints, &res);
sendSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
success = ::connect(sendSocket, res->ai_addr, res->ai_addrlen) != -1;
freeaddrinfo(res);
return success;
}
bool Messager::bind(std::string port){
struct addrinfo hints, *res;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(NULL, port.c_str(), &hints, &res);
listenSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if(listenSocket == INVALID_SOCKET){
return false;
}
if(::bind(listenSocket, res->ai_addr, res->ai_addrlen) == -1){
return false;
}
return true;
}
void Messager::listen(void){
::listen(listenSocket, 10);
}
int Messager::send(std::string message){
const std::string terminator = "\r\n";
std::string realMessage;
int size = 0;
int totalSent = 0;
realMessage = message;
realMessage += terminator;
size = realMessage.size();
totalSent = ::send(sendSocket, realMessage.c_str(), size, 0);
if(totalSent == 0 || totalSent == -1){
return 0; // There must be an error, 0 means it is an error
}
// This statement keeps adding the results of ::send to totalSent until it's the size of the full message
for(totalSent = 0; totalSent < size; totalSent += ::send(sendSocket, realMessage.c_str(), size, 0));
return totalSent;
}
// This function has been updated a lot thanks to #Luke
std::string Messager::receive(void){
const int bufferSize = 256;
const std::string terminator = "\r\n";
char buffer[bufferSize];
int i = 0;
int received = 0;
std::string tempString;
size_t term = 0;
for(i = 0; i < bufferSize; i++){
buffer[i] = 0;
}
received = ::recv(listenSocket, buffer, bufferSize, 0);
tempString = buffer;
term = tempString.find(terminator);
if(term != -1){ // Already have line
line = tempString;
}
while(received != -1 && received != 0){ // While it is receiving information...
// Flush the buffer
for(i = 0; i < bufferSize; i++){
buffer[i] = 0;
}
::recv(listenSocket, buffer, bufferSize, 0);
tempString += buffer;
term = tempString.find(terminator);
if(term != -1){ // Found terminator!
return tempString;
}
}
throw 0; // Didn't receive any information. Throw an error
}
Any ideas about what might be going on would be really appreciated. If necessary I can post the code the server and client use, but I can give a general outline:
Server:
messager.init()
messager.bind()
messager.listen()
messager.receive() <-- includes accept()
Client:
messager.init()
messager.connect()
messager.send()
Thanks in advance.
I see two concerns.
You can't safely use the string assignment operator in Message::receive(). The assignment operator relies on the character array being NULL-terminated, and in this case it is not. It's probably filling it up with a bunch of garbage data. You should get the number of characters actually received (i.e. the return value of recv()) and use the string::assign() method to fill the string object.
There is no code to ensure all the data has been sent or received. recv() is going to return as soon as any data is available; you really need to loop until you have received the entire message. For plain-text data, typically people use a CR-LF pair to indicate the end of a line. You keep calling recv() and buffering the results until you see that CR-LF pair and then return that line to the caller. You should also loop on send() until your entire buffer has been sent.
Typically this looks something like the following (this is all from memory so there are probably a few minor errors, but this is the gist of it):
bool Message::Receive(std::string& line)
{
// look for the terminating pair in the buffer
size_t term = m_buffer.find("\r\n");
if(term != -1)
{
// already have a line in the buffer
line.assign(m_buffer, 0, term); // copy the line from the buffer
m_buffer.erase(0, term + 2); // remove the line from the buffer
return true;
}
// no terminating pair in the buffer; receive some data over the wire
char tmp[256];
int count = recv(m_socket, tmp, 256);
while(count != -1 && count != 0)
{
// successfully received some data; buffer it
m_buffer.append(tmp, count);
// see if there is now a terminating pair in the buffer
term = m_buffer.find("\r\n");
if(term != -1)
{
// we now have a line in the buffer
line.assign(m_buffer, 0, term); // copy the line from the buffer
m_buffer.erase(0, term + 2); // remove the line from the buffer
return true;
}
// we still don't have a line in the buffer; receive some more data
count = recv(m_socket, tmp, 256);
}
// failed to receive data; return failure
return false;
}
Two suggestions:
check the return values of all the socket functions you call
ditch DevC++ - it is buggy as hell & is no longer being developed - use http://www.codeblocks.org/ instead.
I'd be a bit concerned about your receive code. It creates a char* buffer to receive the data but doesn't actually allocate any memory for it.
Now I can't tell whether you're calling the WinSock recv there since you don't explicitly say ::recv but I think you would need to either:
allocate some space with malloc first (id recv wants a buffer); or
pass the address of the buffer pointer (if recv allocates its own buffer).
I'm actually surprised that the doesn't cause a core dump since the value of buffer could be set to anything when you call recv.
Something like this may be better:
char *Messager::receive(void){
int newSocket = 0;
struct sockaddr_storage *senderAddress;
socklen_t addressSize;
char *buffer;
addressSize = sizeof senderAddress;
newSocket = accept(listenSocket, (struct sockaddr *)&senderAddress,
&addressSize);
buffer = new char[20];
recv(newSocket, buffer, 20, 0);
return buffer;
}
But you need to remember that the client of this function is responsible for freeing the buffer when it's finished with it.
In your receive function, the buffer local is never initialized to anything, so you end up reading your message into some random memory and probably causing corruption or crashing. You probably want char buffer[MAX_MSG_LENGTH]; instead of char *buffer
Related
This question already has answers here:
C: send file to socket
(4 answers)
Closed 2 years ago.
I want to send files over TCP sockets in C++ on Windows, all is working absolutely fine, however I can't send big files like this, I understand that TCP as any protocol has it's limitations, like I can't send more than 64KB per packet, my method works for small file sizes(tested all up to 12KB), but I would like to send LARGE files, like iso image of ubuntu or windows, which are surely bigger than 12 fully packed packets and etc.
Server
int filesize = 0;
int err = recv(conn, (char*)&filesize, sizeof(filesize), 0);
if (err <= 0)
{
printf("recv: %d\n", WSAGetLastError());
clean(conn);
}
printf("recv %d bytes [OK]\n", err);
char* buffer = new char[filesize];
ZeroMemory(buffer, filesize);
err = recv(conn, buffer, filesize, MSG_WAITALL);
if (err <= 0)
{
printf("recv: %d\n", WSAGetLastError());
clean(conn);
}
printf("recv %d bytes [OK]\n", err);
ofstream file("a.txt", ios::binary);
file.write(buffer, filesize);
delete[] buffer;
file.close();
Client
ifstream file("a.txt", ios::binary);
file.seekg(0, ios::end);
int size = file.tellg();
file.seekg(0, ios::beg);
char* buffer = new char[size];
file.read(buffer, size);
file.close();
int* fsize = &size;
int err = send(client, (char*)fsize, sizeof(int), 0);
if (err <= 0)
{
printf("send: %d\n", WSAGetLastError());
}
printf("send %d bytes [OK]\n", err);
err = send(client, buffer, size, 0);
if (err <= 0)
{
printf("send: %d\n", WSAGetLastError());
}
printf("send %d bytes [OK]\n", err);
delete[] buffer;
All values for both sides are initialised, and error handling is done well, and if I had problem then I would have said about that. I decided to use MSG_WAITALL because I guess that is suitable for this case, please correct my code for recieving/sending and if possible refactor it, it would be nicer if it would be with explainations, so that evrybody could learn to code better, thanks)))
The one main point that should be taken away from the comments below your question is that send and recv are fickle. Just because you write send(buffer with 100 bytes) doesn't mean it's going to send 100 bytes. It could send 25 bytes, or 99 bytes, or fail out completely. It's up to you to take the return value and compute what needs to still be sent.
Same goes with recv. If you write recv(buffer with 100 bytes) because you are expecting 100 bytes, it could only grab 25 bytes, or 99 bytes, or fail out completely. Again, it's up to you to use that return value and compute what still needs to be received.
File I/O is completely different. If you want to write 100 bytes to a file, those 100 bytes are guaranteed to be written if the method doesn't fail. So, when folks who have worked with file I/O move to socket I/O usually end up confused why things aren't sending or receiving correctly.
One of the trickier parts to socket programming is knowing how much data you will need to receive. You covered that by sending the length of the file first. The server will know to read in that value, then continue reading until that value is satisfied.
Some protocols, like HTTP, will use delimiters (in HTTP's case \r\n\r\n) to signal when a packet of data has ended. So, as a socket programmer, you would recv on a loop until those 4 bytes are read.
I put together an example on how you could accomplish sending and receiving a large file (this will handle files up to 9,223,372,036,854,775,807 in length). This isn't pure C++, I cheated in places because of lack of time. I used some Windows-only constructs for the same reason.
So let's take a look at it:
int64_t GetFileSize(const std::string& fileName) {
// no idea how to get filesizes > 2.1 GB in a C++ kind-of way.
// I will cheat and use Microsoft's C-style file API
FILE* f;
if (fopen_s(&f, fileName.c_str(), "rb") != 0) {
return -1;
}
_fseeki64(f, 0, SEEK_END);
const int64_t len = _ftelli64(f);
fclose(f);
return len;
}
///
/// Recieves data in to buffer until bufferSize value is met
///
int RecvBuffer(SOCKET s, char* buffer, int bufferSize, int chunkSize = 4 * 1024) {
int i = 0;
while (i < bufferSize) {
const int l = recv(s, &buffer[i], __min(chunkSize, bufferSize - i), 0);
if (l < 0) { return l; } // this is an error
i += l;
}
return i;
}
///
/// Sends data in buffer until bufferSize value is met
///
int SendBuffer(SOCKET s, const char* buffer, int bufferSize, int chunkSize = 4 * 1024) {
int i = 0;
while (i < bufferSize) {
const int l = send(s, &buffer[i], __min(chunkSize, bufferSize - i), 0);
if (l < 0) { return l; } // this is an error
i += l;
}
return i;
}
//
// Sends a file
// returns size of file if success
// returns -1 if file couldn't be opened for input
// returns -2 if couldn't send file length properly
// returns -3 if file couldn't be sent properly
//
int64_t SendFile(SOCKET s, const std::string& fileName, int chunkSize = 64 * 1024) {
const int64_t fileSize = GetFileSize(fileName);
if (fileSize < 0) { return -1; }
std::ifstream file(fileName, std::ifstream::binary);
if (file.fail()) { return -1; }
if (SendBuffer(s, reinterpret_cast<const char*>(&fileSize),
sizeof(fileSize)) != sizeof(fileSize)) {
return -2;
}
char* buffer = new char[chunkSize];
bool errored = false;
int64_t i = fileSize;
while (i != 0) {
const int64_t ssize = __min(i, (int64_t)chunkSize);
if (!file.read(buffer, ssize)) { errored = true; break; }
const int l = SendBuffer(s, buffer, (int)ssize);
if (l < 0) { errored = true; break; }
i -= l;
}
delete[] buffer;
file.close();
return errored ? -3 : fileSize;
}
//
// Receives a file
// returns size of file if success
// returns -1 if file couldn't be opened for output
// returns -2 if couldn't receive file length properly
// returns -3 if couldn't receive file properly
//
int64_t RecvFile(SOCKET s, const std::string& fileName, int chunkSize = 64 * 1024) {
std::ofstream file(fileName, std::ofstream::binary);
if (file.fail()) { return -1; }
int64_t fileSize;
if (RecvBuffer(s, reinterpret_cast<char*>(&fileSize),
sizeof(fileSize)) != sizeof(fileSize)) {
return -2;
}
char* buffer = new char[chunkSize];
bool errored = false;
int64_t i = fileSize;
while (i != 0) {
const int r = RecvBuffer(s, buffer, (int)__min(i, (int64_t)chunkSize));
if ((r < 0) || !file.write(buffer, r)) { errored = true; break; }
i -= r;
}
delete[] buffer;
file.close();
return errored ? -3 : fileSize;
}
Sending and Receiving Buffers
At the top we have two methods that works with buffers in memory. You can send it any buffer at any size (stay reasonable here), and those methods will send and receive until all the bytes passed in have been transmitted.
This does what I was talking about above. It takes the buffer and loops until all the bytes have been successfully sent or received. After these methods complete, you are guaranteed that all data is transmitted (as long as the return value is zero or positive).
You can define a "chunk size" which is the default size of the chunks of data the methods will use to send or receive data. I am sure these can be optimized by using more suitable values than what they are currently set at, but I don't know what those values are. It's safe to leave them at the default. I don't think that with the speed of today's computers you will notice too much of a difference if you change it to something else.
Sending and Receiving Files
The code for doing files is almost identical in nature to the buffer code. Same idea, except now we can assume that if the return value is greater than zero from the buffer methods then it was successful. So the code is a little simpler. I use a chunk size of 64KB... for no special reason. This time the chunk size determines how much data is read from the file I/O operations, not the sockets I/O.
Test Server and Client
Just to be complete, I used this code below to test this with a 5.3 GB file I have on disk. I basically just re-wrote Microsoft's client/server examples in a very slimmed down way.
#pragma comment(lib, "Ws2_32.lib")
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <fstream>
DWORD __stdcall ClientProc(LPVOID param) {
struct addrinfo hints = { 0 }, * result, * ptr;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if (getaddrinfo("127.0.0.1", "9001", &hints, &result) != 0) {
return ~0;
}
SOCKET client = INVALID_SOCKET;
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
client = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (client == SOCKET_ERROR) {
// TODO: failed (don't just return, cleanup)
}
if (connect(client, ptr->ai_addr, (int)ptr->ai_addrlen) == SOCKET_ERROR) {
closesocket(client);
client = INVALID_SOCKET;
continue;
}
break;
}
freeaddrinfo(result);
if (client == SOCKET_ERROR) {
std::cout << "Couldn't create client socket" << std::endl;
return ~1;
}
int64_t rc = SendFile(client, "D:\\hugefiletosend.bin");
if (rc < 0) {
std::cout << "Failed to send file: " << rc << std::endl;
}
closesocket(client);
return 0;
}
int main()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
{
struct addrinfo hints = { 0 };
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
struct addrinfo* result = NULL;
if (0 != getaddrinfo(NULL, "9001", &hints, &result)) {
// TODO: failed (don't just return, clean up)
}
SOCKET server = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (server == INVALID_SOCKET) {
// TODO: failed (don't just return, clean up)
}
if (bind(server, result->ai_addr, (int)result->ai_addrlen) == INVALID_SOCKET) {
// TODO: failed (don't just return, clean up)
}
freeaddrinfo(result);
if (listen(server, SOMAXCONN) == SOCKET_ERROR) {
// TODO: failed (don't just return, clean up)
}
// start a client on another thread
HANDLE hClientThread = CreateThread(NULL, 0, ClientProc, NULL, 0, 0);
SOCKET client = accept(server, NULL, NULL);
const int64_t rc = RecvFile(client, "D:\\thetransmittedfile.bin");
if (rc < 0) {
std::cout << "Failed to recv file: " << rc << std::endl;
}
closesocket(client);
closesocket(server);
WaitForSingleObject(hClientThread, INFINITE);
CloseHandle(hClientThread);
}
WSACleanup();
return 0;
}
Hello I am having a problem with a socket server and client.
The problem is that the messages get mixed up when I send them really fast. When I send them lets say 1 message per second everything runs good, but when I send them 1 message per 40ms they get mixed up.
here is my code for receiving:
std::string* AteneaClient::readSocket () {
std::string finalString = std::string("");
int size = MSG_SIZE;
bool receiving = true;
int timesBufferInc=0;
while (receiving) {
std::string temporalString;
//create an empty buffer
char* RCV_BUFFER = (char*) malloc (size* sizeof(char));
for(int i=0;i<size;i++){
RCV_BUFFER[i]=' ';
}
RCV_BUFFER[size-1]='\0';
int result = recv(sock,RCV_BUFFER,size-1,NULL);
if ( result== SOCKET_ERROR ) {
free(RCV_BUFFER);
return NULL;
}
else if(result<size-1){
receiving=false;
}
temporalString = std::string(RCV_BUFFER);
finalString+=temporalString;
}
return new std::string(finalString);
}
and here is my code for sending:
int sendThread(void* data){
SND_THREAD_DATA* parameters =(SND_THREAD_DATA*)data;
SOCKET* individualSocket = parameters->individualSocket;
std::string * message = parameters->message;
char RCV_BUFFER[MSG_SIZE];
std::string converter;
std::cout <<"(!)Thread: Iniciando sendThread Individual.."<<std::endl;
SOCKET default_socket = *individualSocket;
bool running=true;
while(running){
int length=message->length();
char *cstr = new char[length + 1];
strcpy(cstr, message->c_str());
if(::send(*individualSocket,cstr,length + 1,NULL)==SOCKET_ERROR){
logSendError();
running=false;
}
delete cstr;
Sleep(SLEEPTIME);
}
}
and here is the code when I set up the socket:
void AteneaClient::startUp(){
int iResult = 0;
iResult = WSAStartup(MAKEWORD(2, 2), &WinSockData);
if (iResult != NO_ERROR) {
wprintf(L"(!)Main:WSAStartup() failed with error: %d\n", iResult);
return;
}
ADDR.sin_addr.s_addr= inet_addr(IP);
ADDR.sin_family = AF_INET;
ADDR.sin_port = htons(PORT);
sock = socket(AF_INET,SOCK_STREAM,0);
running=true;
}
Anyone has any idea why socket messages get mixed up?
Thanks!
EDIT:
this is my current receive method with the improvements from Maxim comments:
std::string* AteneaClient::readSocket () {
int HEADER_SIZE=4;
std::string finalString = std::string("");
int sizeFirstBuffer = HEADER_SIZE*sizeof(char);
char* RCV_BUFFER=(char*) malloc(sizeFirstBuffer+1);
//clean new buffer
for(int i=0;i<HEADER_SIZE;i++){
RCV_BUFFER[i]=' ';
}
RCV_BUFFER[sizeFirstBuffer]='\0';
int result = recv(sock,RCV_BUFFER,sizeFirstBuffer,NULL);
//cout << "The Size to read is:" <<RCV_BUFFER << endl;
//now i create a buffer with that size
int sizeThatIHaveToRead= atoi(RCV_BUFFER);
int sizeSecondBuffer = sizeThatIHaveToRead*sizeof(char);
char* RCV_BUFFER_SECOND=(char*) malloc(sizeSecondBuffer+1);
//clean new buffer
for(int i=0;i<sizeSecondBuffer;i++){
RCV_BUFFER_SECOND[i]=' ';
}
RCV_BUFFER_SECOND[sizeSecondBuffer]='\0';
result = recv(sock,RCV_BUFFER_SECOND,sizeSecondBuffer,NULL);
//cout << "RCV_BUFFER_SECOND:" <<RCV_BUFFER_SECOND << endl;
finalString+=RCV_BUFFER_SECOND;
return new std::string(finalString);
}
You are sending strings through stream sockets and expect them to be sent and received atomically, e.g. either nothing is sent/received or the entire string is sent/received. This is not how stream sockets work.
Stream sockets often send only part of your data, so you need to keep sending until all data has been sent. Same is for receiving.
You also need to delimit the messages somehow, otherwise when receiving you won't know when a message ends and the next one starts. The two most common ways are a) prefix messages with their size, b) use a message delimiter (e.g. new-line symbol).
ZeroMQ can do both of these tasks for you: your applications end up sending and receiving complete messages, without you having to implement message framing and sending/receiving on byte level.
The updated code still does not correctly use send and recv calls.
Here is correct usage with functions to send and receive a std::string:
#include <stdexcept>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
ssize_t recv_all(int fd, void* buf, size_t buf_len) {
for(size_t len = buf_len; len;) {
ssize_t r = ::recv(fd, buf, len, 0);
if(r <= 0)
return r;
buf = static_cast<char*>(buf) + r;
len -= r;
}
return buf_len;
}
ssize_t send_all(int fd, void const* buf, size_t buf_len) {
for(size_t len = buf_len; len;) {
ssize_t r = ::send(fd, buf, len, 0);
if(r <= 0)
return r;
buf = static_cast<char const*>(buf) + r;
len -= r;
}
return buf_len;
}
void send_string(int fd, std::string const& msg) {
ssize_t r;
// Send message length.
uint32_t len = msg.size();
len = htonl(len); // In network byte order.
if((r = send_all(fd, &len, sizeof len)) < 0)
throw std::runtime_error("send_all 1");
// Send the message.
if((r = send_all(fd, msg.data(), msg.size())) < 0)
throw std::runtime_error("send_all 2");
}
std::string recv_string(int fd) {
ssize_t r;
// Receive message length in network byte order.
uint32_t len;
if((r = recv_all(fd, &len, sizeof len)) <= 0)
throw std::runtime_error("recv_all 1");
len = ntohl(len);
// Receive the message.
std::string msg(len, '\0');
if(len && (r = recv_all(fd, &msg[0], len)) <= 0)
throw std::runtime_error("recv_all 2");
return msg;
}
WinSock 2.2 send() function always returns me all the bytes I want it to send! I am connecting to google.com on port 80 and sending random t letters for data. I even tried to send as many as 1GB of t's. It still returns with all bytes sent. I was expecting it to return back with me with how many bytes it could fit in a packet and the rest would be handled by the while loop in the implementation, but it never enters inside the while loop as a result!
I am using TCP/IPv4 sockets. I know my internet is not lighting fast to send 1GB faster than I can blink.
This code is shown for most important chunks. For example a wrapper for the C-string send call is omitted that calls the send I shown.
Calling test code
//prepare the long data string
int buffSize = 1 * 1000 * 1000 * 1000;
char *buff = new char[buffSize];
memset(buff, 't', buffSize);
buff[buffSize - 3] = '\n';
buff[buffSize - 2] = '\n';
buff[buffSize - 1] = '\0';
//send thee data
int bytesSent = socket->send(buff);
//verify all thee data is sent
CHECK(bytesSent == strlen(buff));
Send implementatian
int mySocket::send(const void *data, int length)
{
int bytesSent = ::send(socketDescriptor, (char*)data, length, 0);
if (bytesSent == SOCKET_ERROR) return -1;
while(bytesSent < length)
{
int sent = ::send(socketDescriptor, (char*)data + bytesSent, length - bytesSent, 0);
if (sent == SOCKET_ERROR) return bytesSent;
bytesSent += sent;
}
return bytesSent;
}
EDIT 1
Since it started to confuse people here is the wrapper
Send wrapper
int mySocket::send(const char * data_string)
{
return send(dataString, strlen(data_string));
}
EDIT 2
here is a full working example you can debug. it produces the same result, it just instantly reports all bytes as sent.
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
class mySocket
{
public:
mySocket(SOCKET sockeDesc)
{
socketDescriptor = sockeDesc;
}
int mySocket::send(const void *data, int length)
{
int bytesSent = ::send(socketDescriptor, (char*)data, length, 0);
if (bytesSent == SOCKET_ERROR) return -1;
while (bytesSent < length)
{
int sent = ::send(socketDescriptor, (char*)data + bytesSent, length - bytesSent, 0);
if (sent == SOCKET_ERROR) return bytesSent;
bytesSent += sent;
}
return bytesSent;
}
int mySocket::send(const char * data_string)
{
return send(data_string, strlen(data_string));
}
private:
SOCKET socketDescriptor;
};
int main()
{
WSAData wsd;
if (WSAStartup(MAKEWORD(2, 2), &wsd) || wsd.wVersion != MAKEWORD(2, 2))
{
WSACleanup();
return 1;
}
//create ze socket
SOCKET socketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
//look up socket info
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
addrinfo *res;
int errCode = getaddrinfo("google.com", "80", &hints, &res);
if (errCode) return 1;
//connect ze socket
int connectStatusCode = ::connect(socketDescriptor, res->ai_addr, res->ai_addrlen);
if (connectStatusCode == SOCKET_ERROR) return 1;
//create zeeeee socket!
mySocket *socket = new mySocket(socketDescriptor);
//prepare ze long data string
int buffSize = 1 * 1000 * 1000 * 1000;
char *buff = new char[buffSize];
memset(buff, 't', buffSize);
buff[buffSize - 3] = '\n';
buff[buffSize - 2] = '\n';
buff[buffSize - 1] = '\0';
//send ze data
int bytesSent = socket->send(buff);
//verify all ze data is sent
bool success = (bytesSent == strlen(buff));
printf("requested bytes = %i\nsent bytes = \t %i", strlen(buff), bytesSent);
printf("\n\nsuccess = %s", success ? "true" : "false");
closesocket(socketDescriptor);
delete socket;
freeaddrinfo(res);
WSACleanup();
getchar();
return 0;
}
EDIT 3
https://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx
Remarks
...
The successful completion of a send function does not
indicate that the data was successfully delivered and received to the
recipient. This function only indicates the data was successfully
sent.
but #Karsten Koop, pointed to similar question on related behaviour:
What is the size of a socket send buffer in Windows?
i badly understand what's writtent there. but what i got is it says the function simply writes to a buffer and returns the bytes "sent". but this not only means it doesn't guarantee that the recepient received the data(which microsoft states) BUT it means it isn't actually sent at all... just in a buffer. am i missing something here or is microsoft misleading about its behaviour?
There is more than one type of socket: Winsock supports multiple protocols.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx
Just because the current implementation of socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) generally seems to block until all data is sent, doesn't mean that other socket types will also block.
In addition, send may send less than the full buffer if the process is interrupted e.g. by a network outage, or if the socket is closed either from the other end or by another thread.
Also, the Winsock API is designed to be compatible with the BSD and Posix sockets APIs, and on those platforms send will generally return with error EINTR if a signal is received during the call. (In Unixland most blocking calls can return with errno==EINTR, this is how the problem of handling external events on a single-threaded system was solved).
I am having a bit of difficulty trying to code this as I do not know a lot. I have a setup for two PCs that can communicate between each other. It works and all, but it only can send single characters to each other. One PC acts like a server if the command is executed with no IP address argument, and the other, given a server IP address, acts like a client to connect to the server.
The code is all here:
// Quick and dirty - error checks omitted for brevity.
#include <iostream>
#include <string>
#include <conio.h>
#include <WinSock2.h>
#include <ws2tcpip.h>
using namespace std;
void chat (int socket_d)
{
while (true)
{
if (_kbhit ())
{
char ch;
ch = _getche();
int n;
n = send (socket_d, &ch, 1, 0);
if (ch == '\r')
{
cout << "\n";
}
}
int n;
int count = 0;
char byte; // Read one byte at a time - is this efficient?
n = recv (socket_d, &byte, 1, 0);
if (n <= 0)
{
if (WSAGetLastError() != WSAEWOULDBLOCK) // A real problem - not just avoiding blocking.
{
cout << "Terminated " << WSAGetLastError() << "\n";
return;
}
}
else
{
cout << (char)byte;
if ((char) byte == '\r')
cout << "\n";
}
}
}
int main (int argc, char * argv [])
{
// Messy process with windows networking - "start" the networking API.
WSADATA wsaData;
int result = WSAStartup (MAKEWORD (2, 2), &wsaData);
unsigned short port = 25565;
// If argument is IP address - be a client and connect to it. Otherwise
// be a server.
if (argc > 1)
{
int socket_d;
socket_d = socket (AF_INET, SOCK_STREAM, 0);
// be a client.
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr (argv[1]); // Parse the string and create the 32 bit address.
server_addr.sin_port = htons (port); // Watch out for the endian conversion!
connect (socket_d, (sockaddr *) &server_addr, sizeof (server_addr));
u_long iMode=1;
ioctlsocket (socket_d, FIONBIO, &iMode); // put the socket into non-blocking mode.
chat (socket_d);
closesocket (socket_d);
}
else
{
// be a server
int listen_socket_d;
int connection_socket_d;
listen_socket_d = socket (AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // A placeholder that will be replaced with my own address.
server_addr.sin_port = htons (port); // Watch out for the endian conversion!
bind (listen_socket_d, (struct sockaddr *) &server_addr, sizeof (server_addr));
int backlog = 5;
listen (listen_socket_d, backlog);
// take only the first connection.
struct sockaddr_storage their_addr;
int their_addr_size = sizeof(their_addr);
connection_socket_d = accept (listen_socket_d, (struct sockaddr *) &their_addr, &their_addr_size);
u_long iMode=1;
ioctlsocket (connection_socket_d, FIONBIO, &iMode); // put the socket into non-blocking mode.
chat (connection_socket_d);
closesocket (connection_socket_d);
}
return 0;
}
What I am trying to achieve is to be able to send strings instead of single characters. The way I would like this to work is by increasing the byte size it sends instead of single byte currently. The way I am assuming it could work, let's say a total size of 64 bytes is sent and received at a time.
It's not really clear from this where your problem area is actually.
My solution is below. The basic idea is to allocate send and receive buffers of equal size, append characters to the send buffer on keyboard input, and transfer the buffer when it is full or the user hits the return key.
The non-blocking send function might not send the whole buffer at once (see the docs). I decided to go ahead and block here until the entire buffer was sent so I didn't have to track separate input and transmit buffers. You could improve on this, I'm sure.
The receive part just echoes whatever bytes have been received. In general, there's no way to know whether the other user was "done" sending data, so we just print what we received after the first call to recv.
I always memset the buffers to all zeros after each send and receive to prevent weird behavior from missing null terminators. It would be more optimized to simply append a null character to the end of the current string, but I get paranoid sometimes.
Here's my code:
#define BUFFER_SIZE 64
void chat (int socket_d)
{
char sendBuffer[BUFFER_SIZE] = { 0 };
char receiveBuffer[BUFFER_SIZE] = { 0 };
int bufferPosition = 0;
int charsSent = 0;
int charsReceived = 0;
while (true)
{
if (_kbhit ())
{
sendBuffer[bufferPosition++] = _getche();
if (sendBuffer[bufferPosition - 1] == '\r' || bufferPosition == BUFFER_SIZE - 1)
{
// This defeats the purpose of a non-blocking socket, I know.
// You can do better by keeping separate buffers for sending and
// collecting input.
while (charsSent < bufferPosition)
{
charsSent += send(
socket_d,
&sendBuffer[charsSent], // Treats the address of a character as a string.
bufferPosition - charsSent, // Only send the part that hasn't been sent yet.
0);
}
memset(sendBuffer, 0, bufferPosition); // Paranoid.
bufferPosition = charsSent = 0;
cout << "\n";
}
}
charsReceived = recv (socket_d, receiveBuffer, BUFFER_SIZE, 0);
if (charsReceived <= 0)
{
if (WSAGetLastError() != WSAEWOULDBLOCK) // A real problem - not just avoiding blocking.
{
cout << "Terminated " << WSAGetLastError() << "\n";
return;
}
}
else
{
cout << receiveBuffer;
if (receiveBuffer[charsReceived - 1] == '\r')
cout << "\n";
memset(receiveBuffer, 0, charsReceived); // Super paranoid.
}
}
}
Now, I wouldn't really consider this "good" until it supports UTF-8, or at least wchar_t. ASCII lacks a lot of characters that people expect to be able to use in a real chat application.
PS - According to Visual Studio 2013, the inet_addr function is deprecated. I used inet_ptons instead. Here's the documentation on the Winsock implementation for it.
Hello I am having a problem with a socket server and client.
The problem is that the messages get mixed up when I send them really fast. When I send them lets say 1 message per second everything runs good, but when I send them 1 message per 40ms they get mixed up.
here is my code for receiving:
std::string* AteneaClient::readSocket () {
std::string finalString = std::string("");
int size = MSG_SIZE;
bool receiving = true;
int timesBufferInc=0;
while (receiving) {
std::string temporalString;
//create an empty buffer
char* RCV_BUFFER = (char*) malloc (size* sizeof(char));
for(int i=0;i<size;i++){
RCV_BUFFER[i]=' ';
}
RCV_BUFFER[size-1]='\0';
int result = recv(sock,RCV_BUFFER,size-1,NULL);
if ( result== SOCKET_ERROR ) {
free(RCV_BUFFER);
return NULL;
}
else if(result<size-1){
receiving=false;
}
temporalString = std::string(RCV_BUFFER);
finalString+=temporalString;
}
return new std::string(finalString);
}
and here is my code for sending:
int sendThread(void* data){
SND_THREAD_DATA* parameters =(SND_THREAD_DATA*)data;
SOCKET* individualSocket = parameters->individualSocket;
std::string * message = parameters->message;
char RCV_BUFFER[MSG_SIZE];
std::string converter;
std::cout <<"(!)Thread: Iniciando sendThread Individual.."<<std::endl;
SOCKET default_socket = *individualSocket;
bool running=true;
while(running){
int length=message->length();
char *cstr = new char[length + 1];
strcpy(cstr, message->c_str());
if(::send(*individualSocket,cstr,length + 1,NULL)==SOCKET_ERROR){
logSendError();
running=false;
}
delete cstr;
Sleep(SLEEPTIME);
}
}
and here is the code when I set up the socket:
void AteneaClient::startUp(){
int iResult = 0;
iResult = WSAStartup(MAKEWORD(2, 2), &WinSockData);
if (iResult != NO_ERROR) {
wprintf(L"(!)Main:WSAStartup() failed with error: %d\n", iResult);
return;
}
ADDR.sin_addr.s_addr= inet_addr(IP);
ADDR.sin_family = AF_INET;
ADDR.sin_port = htons(PORT);
sock = socket(AF_INET,SOCK_STREAM,0);
running=true;
}
Anyone has any idea why socket messages get mixed up?
Thanks!
EDIT:
this is my current receive method with the improvements from Maxim comments:
std::string* AteneaClient::readSocket () {
int HEADER_SIZE=4;
std::string finalString = std::string("");
int sizeFirstBuffer = HEADER_SIZE*sizeof(char);
char* RCV_BUFFER=(char*) malloc(sizeFirstBuffer+1);
//clean new buffer
for(int i=0;i<HEADER_SIZE;i++){
RCV_BUFFER[i]=' ';
}
RCV_BUFFER[sizeFirstBuffer]='\0';
int result = recv(sock,RCV_BUFFER,sizeFirstBuffer,NULL);
//cout << "The Size to read is:" <<RCV_BUFFER << endl;
//now i create a buffer with that size
int sizeThatIHaveToRead= atoi(RCV_BUFFER);
int sizeSecondBuffer = sizeThatIHaveToRead*sizeof(char);
char* RCV_BUFFER_SECOND=(char*) malloc(sizeSecondBuffer+1);
//clean new buffer
for(int i=0;i<sizeSecondBuffer;i++){
RCV_BUFFER_SECOND[i]=' ';
}
RCV_BUFFER_SECOND[sizeSecondBuffer]='\0';
result = recv(sock,RCV_BUFFER_SECOND,sizeSecondBuffer,NULL);
//cout << "RCV_BUFFER_SECOND:" <<RCV_BUFFER_SECOND << endl;
finalString+=RCV_BUFFER_SECOND;
return new std::string(finalString);
}
You are sending strings through stream sockets and expect them to be sent and received atomically, e.g. either nothing is sent/received or the entire string is sent/received. This is not how stream sockets work.
Stream sockets often send only part of your data, so you need to keep sending until all data has been sent. Same is for receiving.
You also need to delimit the messages somehow, otherwise when receiving you won't know when a message ends and the next one starts. The two most common ways are a) prefix messages with their size, b) use a message delimiter (e.g. new-line symbol).
ZeroMQ can do both of these tasks for you: your applications end up sending and receiving complete messages, without you having to implement message framing and sending/receiving on byte level.
The updated code still does not correctly use send and recv calls.
Here is correct usage with functions to send and receive a std::string:
#include <stdexcept>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
ssize_t recv_all(int fd, void* buf, size_t buf_len) {
for(size_t len = buf_len; len;) {
ssize_t r = ::recv(fd, buf, len, 0);
if(r <= 0)
return r;
buf = static_cast<char*>(buf) + r;
len -= r;
}
return buf_len;
}
ssize_t send_all(int fd, void const* buf, size_t buf_len) {
for(size_t len = buf_len; len;) {
ssize_t r = ::send(fd, buf, len, 0);
if(r <= 0)
return r;
buf = static_cast<char const*>(buf) + r;
len -= r;
}
return buf_len;
}
void send_string(int fd, std::string const& msg) {
ssize_t r;
// Send message length.
uint32_t len = msg.size();
len = htonl(len); // In network byte order.
if((r = send_all(fd, &len, sizeof len)) < 0)
throw std::runtime_error("send_all 1");
// Send the message.
if((r = send_all(fd, msg.data(), msg.size())) < 0)
throw std::runtime_error("send_all 2");
}
std::string recv_string(int fd) {
ssize_t r;
// Receive message length in network byte order.
uint32_t len;
if((r = recv_all(fd, &len, sizeof len)) <= 0)
throw std::runtime_error("recv_all 1");
len = ntohl(len);
// Receive the message.
std::string msg(len, '\0');
if(len && (r = recv_all(fd, &msg[0], len)) <= 0)
throw std::runtime_error("recv_all 2");
return msg;
}