I'm working on implementing a C++ client server chat program to learn more / practice socket programming. I'm using winsockV2.
Briefly,
the client program connects to a server, who stores the client socket in a vector
client program sends messages for the server to distribute to other clients in the vector.
The problem I think I'm running into is that the clients and server are receiving the message and storing it in a char message[256] and if the message is shorter than 256, strange chars are displayed when I std::cout << message; which I'm being told is uninitialized memory. Here's an example of the output:
k:message from client to other client╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠(■o
Is there some way of creating a character array of the size of the received message? i.e
char recvMessage[4096];
int s = recv(socket, recvMessage, sizeof(recvMessage),0);
char recvOutput[strlen(recvMessage)] = recvMessage;
std::cout << recvOutput << std::endl;
Otherwise what is your solution for recv'ing messages which you do not know the length of?
If I'm being a complete idiot, please be kind, I came from PHP. classes are below:
SVR.CPP
See receiveMessages() and distributeMessages() functions
#include "stdafx.h"
#include "svr.h"
svr::svr()
{
//WSA Business I don't understand
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
}
//End of WSA Business
//get addressSize
addressSize = sizeof(address);
//set address data members
address.sin_family = AF_INET;
address.sin_port = htons(444);
address.sin_addr.s_addr = INADDR_ANY;
//init sListen
sListen = socket(AF_INET, SOCK_STREAM, 0);
bind(sListen, (sockaddr*)&address, addressSize);
}
svr::~svr()
{
}
void svr::start()
{
std::thread newConnThread(&svr::newConnection, this);
newConnThread.join();
}
void svr::receiveMessages(int clientIndex)
{
std::cout << "\tsvr::recv thread started for client index:" << clientIndex << std::endl;
//create char arr
char recvMessage[256];
//forever
while (true)
{
//receive message and input it to recvMessage char arr.
recv(clients[clientIndex], recvMessage, sizeof(recvMessage), 0);
//if message is not null, send out to other clients
if (recvMessage != NULL)
{
std::cout << "\t\tINFO:Received message of length: " << std::strlen(recvMessage) << " size: " << sizeof(recvMessage) << " : " << recvMessage << std::endl;
distributeMessages(recvMessage, clientIndex);
}
}
}
//distributes messages to all clients in vector. called by receiveMessages function, normally in rMessages thread.
void svr::distributeMessages(std::string message, int clientIndex)
{
for (unsigned int i = 0; i < clients.size(); i++)
{
if (clientIndex != i)
{
send(clients[i], message.c_str(), message.length(), 0);
}
else
{
//would have sent to self, not useful.
}
}
}
//accepts new connections and adds sockets to vector.
void svr::newConnection()
{
//mark for accept, unsure of somaxconn value;
listen(sListen, SOMAXCONN);
std::cout << "\tSERVER: awaiting new connections..." << std::endl;
while (true)
{
//accept connection and push on to vector.
clients.push_back(accept(sListen, (sockaddr*)&address, &addressSize));
//responds to new clients.
const char *message = "Hi, you've successfully connected!";
int clientIndex = clients.size() - 1;
int sent = send(clients[clientIndex], message, 33, 0);
//start new receiveMessage thread
std::thread newClient(&svr::receiveMessages, this, clientIndex);
//detach here, let newConn thread operate without depending on receiveMessages
newClient.detach();
}
std::cout << "\tSERVER: no longer listening for new connections" << std::endl;
}
CLI.CPP
See cSend() and cRecv() functions
#include "stdafx.h"
#include "cli.h"
cli::cli(char *ip)
{
//WSA
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
// Use the MAKEWORD(lowbyte,highbyte) macro declared in windef.h
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
std::cout << "WSAStartup failed with the error: " << err;
}
}
//get addressSize
addressSize = sizeof(address);
//set address struct data members
address.sin_family = AF_INET;
address.sin_port = htons(444);
//if ip empty, prompt user;
if (ip == NULL)
{
std::string ipInput;
std::cout << "\n\tConnect to which IP: ";
std::cin >> ipInput;
address.sin_addr.s_addr = inet_addr(ipInput.c_str());
}
else
{
address.sin_addr.s_addr = inet_addr(ip);
}
sock = socket(AF_INET, SOCK_STREAM, 0);
std::cout << "\n\tYour username: ";
std::cin >> uname;
}
cli::~cli()
{
}
void cli::start()
{
try
{
//hold string
char message[33];
std::cout << "\n\tcli::start() called";
int conRet;
//connects to server socket & receives a message, stores in it message variable
conRet = connect(sock, (sockaddr*)&address, (int)addressSize);
recv(sock, message, sizeof(message), 0);
std::cout << "\n\tSERVER: " << message;
//starts threads, pass this for object scope.
std::thread sendThread(&cli::cSend, this);
std::thread recvThread(&cli::cRecv, this);
//this function (start) will return/end when send and recv threads end.
sendThread.join();
recvThread.join();
}
catch (std::exception e)
{
std::cerr << e.what() << std::endl;
}
}
void cli::cSend()
{
std::cout << "\n\tcli::send thread started";
//char arr for sending str;
std::string getLine;
while (true)
{
std::cout << "\n\t" << uname << ":" << std::flush;
//set to "" because i suspected the value remains in the string after a loop.
std::string message = "";
//get input, put it in message
std::getline(std::cin, message);
//get full message
std::string fullMessage = uname + ":" + message;
//get constant int, size of fullMessage
const int charArrSize = fullMessage.length();
std::cout << "\t\tINFO: Sending character array of length: " << charArrSize << " size: " << sizeof(fullMessage.c_str()) << " : " << fullMessage.c_str() << std::endl;
//sends it
send(sock, fullMessage.c_str(), charArrSize, 0);
}
}
void cli::cRecv()
{
std::cout << "\n\tcli::recv thread started";
//initialize arr to 0, will hopefully help avoid the weird chars in the cout
char recvMessage[256]{ '\0' };
while (true)
{
recv(sock, recvMessage, sizeof(recvMessage), 0);
std::cout << "\t\tINFO:Received message of length: " << std::strlen(recvMessage) << " size: " << sizeof(recvMessage) << " : " << recvMessage << std::endl;
std::cout << recvMessage << std::endl;
}
}
what is your solution for recv'ing messages which you do not know the
length of?
recv() tells you the length of the message it received. You don't have to wonder what it is. That's recv()'s return value.
int s = recv(socket, recvMessage, sizeof(recvMessage),0);
See -- there you go. It's right here in front of you. It's s. Of course if there was an error s would be negative and you need to check for that. But, ignoring that little detail, your worries are over: s is the length of your message you just received.
char recvOutput[strlen(recvMessage)] = recvMessage;
That's not going to work. What is strlen() doing here? strlen() computes the size of the string, expecting the string to be an old-fashioned, C-style character string that's terminated by a \0 byte. recv() does not terminate anything it receives with a \0 byte. Instead, it returns the actual character count.
And, besides, this won't work anyway. You can't initialize an array this way.
Your obvious intent here, apparently, is to expect to receive a text string as message. Well, since your language of choice is C++, and you tagged your question as such, the logical conclusion is that you should be using what C++ gives you to deal with text strings: the std::string class:
std::string recvOutput{recvMessage, recvMessage+s};
There you go. Mission accomplished. Since you already known the length of the received message in s, as we've determined before (and after double-checking that s is not negative), you can simply use std::string's existing constructor that initializes the new string given an iterator, or a pointer, to the start and the end of string.
When dealing with low-level operating system interfaces, like sockets, you have no choice but to use primitive data types, like plain char arrays and buffers, because that's the only thing that the operating system understands. But, with the rich set of templates and classes offered by the C++ library, your code should switch to using C++ classes and templates at the first opportunity, in order to be able to use all those resources. As such, as soon as you've determined how big is the text string recv() just came up with, just stuff it into a std::string before figuring out what to do with it.
Related
I have created a ClientSocket and a ServerSocket class for simplifying functions. while sending a data, at first I am sending a 16 bytes header containing the message length followed by the message. But I am having trouble while sending data from client to server on the 2nd time. At first it is sending the header and the message properly but after that I am getting 0 bytes output from read() in ServerSocket::get_message while reading the header from the client. Please help me out here.
Sending and receiving part in Server.cpp
string ServerSocket::get_message(int client_socket_fd) {
//char *header = client_buffers[client_socket_fd].read_header;
char *read_buffer = client_buffers[client_socket_fd].read_buffer;
char header[16];
memset(header, 0, sizeof(header));
int read_result = -1;
read_result = read(client_socket_fd, header, 16);
cout << read_result << endl;
if (read_result > 0){
int read_size = stoi(string(header));
cout << read_size << endl;
memset(read_buffer, 0, sizeof(read_buffer));
read_result = read(client_socket_fd, read_buffer,read_size);
if (read_result > 0) return string(read_buffer);
}
cerr << "Unable to recieve message from client socket " << client_socket_fd << endl;
return "";
}
int ServerSocket::_send(int client_socket_fd, string message) {
//char *header = client_buffers[client_socket_fd].write_header;
char *write_buffer = client_buffers[client_socket_fd].write_buffer;
char header[16];
memset(header, 0, sizeof(header));
string write_size = to_string(message.length());
copy(write_size.begin(), write_size.end(), header);
int write_result = write(client_socket_fd, header, 16); // sending size of message
if (write_result > 0) {
write_result = write(client_socket_fd, message.c_str(), message.length());
}
if (write_result <= 0)
cerr << "Unable to send to client socket fd : " << client_socket_fd << endl;
return write_result;
}
Sending and receiving part in Client.cpp
string ClientSocket::_recieve(){
char read_header[16];
memset(read_header, 0, sizeof(read_header));
int read_result = read(socket_fd, read_header, 16);
if (read_result >0) {
int read_size = stoi(string(read_header));
memset(recieve_buffer, 0, sizeof(recieve_buffer));
read_result = read(socket_fd, recieve_buffer, read_size);
}
if ( read_result > 0) return string(recieve_buffer);
cerr << "Unable to read from server." << endl;
return "";
}
int ClientSocket::_send(string message) {
char write_header[16];
memset(write_header, 0, sizeof(write_header));
cout << message.length() << endl;
string s = to_string(message.length());
copy(s.begin(),s.end(), write_header);
int write_result = write(socket_fd, write_header, 16);
if (write_result > 0)
write_result = write(socket_fd, message.c_str(), message.length());
if (write_result <=0) cerr << "Unable to send message : "<< message << endl;
return write_result;
}
The code exhibits the two most frequent errors when using sockets:
Socket send/write and recv/read may not send/receive the number of bytes requested. The code must handle partial reads/writes in order to work correctly.
The received socket data is not zero-terminated. You need to zero-terminate the received data before passing it to functions that expect zero-terminated stings (std::string and stoi here). memset doesn't help when recv fills the entire buffer, you need to reserve one extra byte for the null terminator that recv doesn't overwrite.
I've a problem with recv() function that I can't explain: it always returns 0. I've a client/server application in which the server simply has to receive a string from the client through the internet (different pc). There are no connectivity problems, and I also tried to send a string from server to client: it worked.
I search in the blog and what I found, recv() socket function returning data with length as 0, non-blocking recv returns 0 when disconnected, Recv returning zero incorrectly, didn't help me to understand.
Here I post the Network class:
Network.h
class Network
{
WSADATA wsaData;
WORD wVersionRequested;
int Port;
unsigned int byteReceived, byteSent;
SOCKET listeningSocket, connectedSocket;
SOCKADDR_IN serverAddr, senderInfo, clientAddr;
int caddrlen;
char buff[DIM];
string buffer;
public:
Network();
~Network();
void Recv();
};
Network.c
void Network::Recv() {
int n = recv(connectedSocket, buff, strlen(buff), 0);
setByteReceived(n);
buffer.assign(buff, n);
cout << "Buffer is: " << buff << endl;
if (byteReceived == 0)
cout << "\tConnection closed" << endl;
else if (byteReceived > 0) {
cout << "\tByte received: " << byteReceived << endl;
cout << getBuffer() << endl;
}
else {
myFormatMessage(WSAGetLastError());
}
}
The behaviour in the end is the following: client and server are connected, the server returns 0 from recv(). I tried also to run both programs on the same machine. Thank you for your help.
You have:
void Network::Recv() {
int n = recv(connectedSocket, buff, strlen(buff), 0);
...
If strlen(buff) returns 0 (because buff contains a null byte at index 0), you will be asking recv() to read 0 bytes, so it will return 0 bytes. You should be using sizeof(buff) instead of strlen(buff).
I have no idea why send data is 48 bytes 010,0,0..., someone can explain? the problem is buffer for data received, I don't know how big he should be, and even if I receive data, how to make normal time from it?
Here's the code:
#include <iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define WIN32_MEAN_AND_LEAN
#include <winsock2.h>
#include <windows.h>
#include <time.h>
using namespace std;
class HRException
{
public:
HRException() :
m_pMessage("") {}
virtual ~HRException() {}
HRException(const char *pMessage) :
m_pMessage(pMessage) {}
const char * what() { return m_pMessage; }
private:
const char *m_pMessage;
};
const int REQ_WINSOCK_VER = 2; // Minimum winsock version required
const char DEF_SERVER_NAME[] = "0.pl.pool.ntp.org";
const int SERVER_PORT = 123;
const int TEMP_BUFFER_SIZE = 128;
const char msg[48] = { 010,0,0,0,0,0,0,0,0 };
// IP number typedef for IPv4
typedef unsigned long IPNumber;
IPNumber FindHostIP(const char *pServerName)
{
HOSTENT *pHostent;
// Get hostent structure for hostname:
if (!(pHostent = gethostbyname(pServerName)))
throw HRException("could not resolve hostname.");
// Extract primary IP address from hostent structure:
if (pHostent->h_addr_list && pHostent->h_addr_list[0])
return *reinterpret_cast<IPNumber*>(pHostent->h_addr_list[0]);
return 0;
}
void FillSockAddr(sockaddr_in *pSockAddr, const char *pServerName, int portNumber)
{
// Set family, port and find IP
pSockAddr->sin_family = AF_INET;
pSockAddr->sin_port = htons(portNumber);
pSockAddr->sin_addr.S_un.S_addr = FindHostIP(pServerName);
}
bool RequestHeaders(const char *pServername)
{
SOCKET hSocket = INVALID_SOCKET;
char tempBuffer[TEMP_BUFFER_SIZE];
sockaddr_in sockAddr = { 0 };
bool bSuccess = true;
try
{
// Lookup hostname and fill sockaddr_in structure:
cout << "Looking up hostname " << pServername << "... ";
FillSockAddr(&sockAddr, pServername, SERVER_PORT);
cout << "found.\n";
// Create socket
cout << "Creating socket... ";
if ((hSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
throw HRException("could not create socket.");
cout << "created.\n";
// Connect to server
cout << "Attempting to connect to " << inet_ntoa(sockAddr.sin_addr)
<< ":" << SERVER_PORT << "... ";
if (connect(hSocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr)) != 0)
throw HRException("could not connect.");
cout << "connected.\n";
cout << "Sending request... ";
// send request part 1
if (send(hSocket, msg, sizeof(msg) , 0) == SOCKET_ERROR)
throw HRException("failed to send data.");
cout << "request sent.\n";
cout << "Dumping received data...\n\n";
// Loop to print all data
recv(hSocket, tempBuffer, sizeof(tempBuffer), 0); // <-- the problem
///
//part where we take time out of tempBuffer
///
}
catch (HRException e)
{
cerr << "\nError: " << e.what() << endl;
bSuccess = false;
}
if (hSocket != INVALID_SOCKET)
{
closesocket(hSocket);
}
return bSuccess;
}
int main(int argc, char* argv[])
{
int iRet = 1;
WSADATA wsaData;
cout << "Initializing winsock... ";
if (WSAStartup(MAKEWORD(REQ_WINSOCK_VER, 0), &wsaData) == 0)
{
// Check if major version is at least REQ_WINSOCK_VER
if (LOBYTE(wsaData.wVersion) >= REQ_WINSOCK_VER)
{
cout << "initialized.\n";
// Set default hostname:
const char *pHostname = DEF_SERVER_NAME;
// Set custom hostname if given on the commandline:
if (argc > 1)
pHostname = argv[1];
iRet = !RequestHeaders(pHostname);
}
else
{
cerr << "required version not supported!";
}
cout << "Cleaning up winsock... ";
// Cleanup winsock
if (WSACleanup() != 0)
{
cerr << "cleanup failed!\n";
iRet = 1;
}
cout << "done.\n";
}
else
{
cerr << "startup failed!\n";
}
int x;
cin >> x;
return iRet;
}
Most part of code is from madwizard.org
Ok it works, main part of code:
const char msg[48] = { 010,0,0,0,0,0,0,0,0 };
if (send(hSocket, msg, sizeof(msg) , 0) == SOCKET_ERROR)
throw HRException("failed to send data.");
cout << "request sent.\n";
cout << "Dumping received data...\n\n";
char tempBuffer[1024];
int bytes = recv(hSocket, tempBuffer, sizeof(tempBuffer), 0);
cout << "bytes received: " << bytes << endl;
time_t tmit;
tmit = ntohl(((time_t*)tempBuffer)[4]);
tmit -= 2208988800U;
cout << ctime(&tmit);
No idea why data that we send is
msg[48] = { 010,0,0,0,0,0,0,0,0 };
and why received data contains many numbers? for example if change code to
tmit = ntohl(((time_t*)tempBuffer)[6]);
I will get date 2008y, why?
Guys why so many minuses?, still waiting for an explanation :D
Here's whole code http://pastebin.com/Sv3ERGfV , dont forget to link ws2_32.lib
Similar to my issue when trying to query the time from a self-hostet Windows-NTP-Server with the C++ library NTPClient which uses boost for the network tasks, msg[48] = { 010,0,0,0,0,0,0,0,0 }; configures the ntp.flags.mode. After comparing the network traffic of w32tm /stripchart /computer:10.159.96.65 using Wireshark, flag 27 or 11 seem to be the choices for my usecase:
Comparison of NTP network packages
tmit = ntohl(((time_t*)tempBuffer)[6]); extracts the data from the received package. It looks like
4 yields the reference time (last sync with timeserver I assume),
8 the time when server received request and
10 the transmit time (which should be almost equal).
I begin to develop my tool, which works with net at the TCP level, which will present simple functions of web-server.
In testing my program I have got very bad mistakes:
Memory leaks
Creating thousands of threads immediately
In taskmgr.exe you may see about ~1,5 of threads and about ~50kb of allocated memory.
Also, I compiled program as 32 bit, but in vmmap utility you may see a lot of 64 bit stacks. My OS is 64 bit, but in taskmgr.exe you may see *32 , I don’t know how 32 bit program uses 64 bit stack, maybe it’s normal for launching 32 bit program in 64 bit OS, but I have no knowledge about this design of OS, so I shall be very pleased , if you give me a piece of advice on this question.
So, why did my program creates immediately a lot of threads? ( I guess , cause of while(true) block ).
But , I want the next:
Create each thread for each new request
When request has been handled, then terminate the thread and free the memory
How should I remake my code?
Thanks!
Here is my code ( MS VC ++ 9 ):
#include <iostream>
#include <Windows.h>
#pragma comment(lib, "Ws2_32.lib")
typedef struct Header
{
friend struct Net;
private:
WORD wsa_version;
WSAData wsa_data;
SOCKET sock;
SOCKADDR_IN service;
char *ip;
unsigned short port;
public:
Header(void)
{
wsa_version = 0x202;
ip = "0x7f.0.0.1";
port = 0x51;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(ip);
service.sin_port = htons(port);
}
} Header;
typedef struct Net
{
private:
int result;
HANDLE thrd;
DWORD exit_code;
void WSAInit(WSAData *data, WORD *wsa_version)
{
result = WSAStartup(*wsa_version, &(*data));
if(result != NO_ERROR)
{
std::cout << "WSAStartup() failed with the error: " << result << std::endl;
}
else
{
std::cout << (*data).szDescription << " " << (*data).szSystemStatus << std::endl;
}
}
void SocketInit(SOCKET *my_socket)
{
(*my_socket) = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if((*my_socket) == INVALID_SOCKET)
{
std::cout << "Socket initialization failed with the error: " << WSAGetLastError() << std::endl;
WSACleanup();
}
else
{
std::cout << "Socket initialization successful!" << std::endl;
}
}
void SocketBind(SOCKET *my_socket, SOCKADDR_IN *service)
{
result = bind((*my_socket), (SOCKADDR*)&(*service), sizeof(*service));
if(result == SOCKET_ERROR)
{
std::cout << "Socket binding failed with the error: " << WSAGetLastError() << std::endl;
closesocket((*my_socket));
WSACleanup();
}
else
{
std::cout << "Socket binding successful!" << std::endl;
}
result = listen(*my_socket, SOMAXCONN);
if(result == SOCKET_ERROR)
{
std::cout << "Socket listening failed with the error: " << WSAGetLastError() << std::endl;
}
else
{
std::cout << "Listening to the socket..." << std::endl;
}
}
static void SocketAccept(SOCKET *my_socket)
{
SOCKET sock_accept = accept((*my_socket), 0, 0);
if(sock_accept == INVALID_SOCKET)
{
std::cout << "Accept failed with the error: " << WSAGetLastError() << std::endl;
closesocket(*my_socket);
WSACleanup();
}
else
{
std::cout << "Client socket connected!" << std::endl;
}
char data[0x400];
int result = recv(sock_accept, data, sizeof(data), 0);
HandleRequest(data, result);
char *response = "HTTP/1.1 200 OK\r\nServer: Amegas.sys-IS/1.0\r\nContent-type: text/html\r\nSet-Cookie: ASD643DUQE7423HFDG; path=/\r\nCache-control: private\r\n\r\n<h1>Hello World!</h1>\r\n\r\n";
result = send(sock_accept, response, (int)strlen(response), 0);
if(result == SOCKET_ERROR)
{
std::cout << "Sending data via socket failed with the error: " << WSAGetLastError() << std::endl;
closesocket(sock_accept);
WSACleanup();
}
else
{
result = shutdown(sock_accept, 2);
}
}
static void HandleRequest(char response[], int length)
{
std::cout << std::endl;
for(int i = 0; i < length; i++)
{
std::cout << response[i];
}
std::cout << std::endl;
}
static DWORD WINAPI Threading(LPVOID lpParam)
{
SOCKET *my_socket = (SOCKET*)lpParam;
SocketAccept(my_socket);
return 0;
}
public:
Net(void)
{
Header *obj_h = new Header();
WSAInit(&obj_h->wsa_data, &obj_h->wsa_version);
SocketInit(&obj_h->sock);
SocketBind(&obj_h->sock, &obj_h->service);
while(true)
{
thrd = CreateThread(NULL, 0, &Net::Threading, &obj_h->sock, 0, NULL);
//if(GetExitCodeThread(thrd, &exit_code) != 0)
//{
// ExitThread(exit_code);
//}
}
delete &obj_h;
}
} Net;
int main(void)
{
Net *obj_net = new Net();
delete &obj_net;
return 0;
}
You should create the thread AFTER you accept a connection, not before.
What you are doing is creating a ton of threads, and then having each of them wait for a connection. Many of them have nothing to do. I don't even know if Windows' accept call is thread-safe - you might end up with multiple threads handling the same connection.
What you need to do instead is, in your main loop (Net's constructor while(true)), you need to call accept(). Since accept() blocks until it has a connection, this will cause the main thread to wait until somebody tries to connect. Then, when they do, you create another thread (or process - more likely on UNIX) to handle that connection. So, your loop now looks like this:
SOCKET sock_accept = accept((*my_socket), 0, 0);
if(sock_accept == INVALID_SOCKET)
{
std::cout << "Accept failed with the error: " << WSAGetLastError() << std::endl;
closesocket(*my_socket);
WSACleanup();
}
else
{
std::cout << "Client socket connected!" << std::endl;
}
thrd = CreateThread(NULL, 0, &Net::Threading, &obj_h->sock, 0, NULL);
//push back thrd into a std::vector<HANDLE> or something like that
//if you want to keep track of it for later: there's more than one thread
Then, delete that code you moved from SocketAccept into this loop. And then, for cosmetic purposes, I would change the name of SocketAccept to SocketHandleConnection.
Now, when your thread starts, it already has a connection, and all you need to do is handle the data (e.g. what you do starting at char data[0x400]).
If you want to handle cleanup for connections, there are a few ways to do this. One, since you are threaded, you can have the thread do its own cleanup. It shares memory with the main process, so you can do this. But in this example, I don't see anything you need to clean up.
Lastly, I think you don't understand what ExitThread does. According to MSDN:
ExitThread is the preferred method of exiting a thread in C code. However, in C++ code,
the thread is exited before any destructors can be called or any other automatic cleanup
can be performed. Therefore, in C++ code, you should return from your thread function.
So it appears that you don't need to call ExitThread- you just return from your function and the thread exits automatically. You don't need to call it from the main thread.
Finally, you should really (if you can) use the new standard C++ threads in c++11, and then if you put in a little bit of effort to port your code over to boost::asio, you'll have a completely cross platform application, with no need for windows API C ugliness :D
DISCLAIMER: I only have a passing understanding of Windows as most of my experience is related to UNIX. I have attempted to be as accurate as I can but if I have any misconceptions about how this knowledge converts over to Windows, well, I warned you.
Why are you creating threads in an infinite loop? This will, of course, create tons of threads. I am referring to this piece of code:
while(true)
{
thrd = CreateThread(NULL, 0, &Net::Threading, &obj_h->sock, 0, NULL);
}
Hey guys, here is my code.
int main() {
char buffer[BUFSIZE];
// define our address structure, stores our port
// and our ip address, and the socket type, etc..
struct sockaddr_in addrinfo;
addrinfo.sin_family = AF_INET;
addrinfo.sin_port = htons(PORT);
addrinfo.sin_addr.s_addr = INADDR_ANY;
// create our socket.
int sock;
if ( (sock = socket(addrinfo.sin_family, SOCK_STREAM, 0)) < 0) {
cout << "Error in creating the socket.";
}
// bind our socket to the actual adress we want
if (bind(sock, (struct sockaddr*)&addrinfo, sizeof(addrinfo)) != 0) {
cout << "Error in binding.";
}
// open the socket up for listening
if (listen(sock, 5) != 0) {
cout << "Error in opening listener.";
}
cout << "Waiting for connections...." << endl;
char *msg = "Success! You are connected.\r\n";
// continuously accept new connections.. but no multithreading.. yet
while(1) {
struct sockaddr_in client_addr;
socklen_t sin_size = sizeof(client_addr);
if(int client = accept(sock, (struct sockaddr*)&client_addr, &sin_size)) {
cout << "Recived new connection from " << inet_ntoa(client_addr.sin_addr) << endl;
send(client, msg, strlen(msg), 0);
while(1) {
send(client, buffer, recv(client, buffer, BUFSIZE, 0), 0);
cout << buffer << endl;
strcpy(buffer, "");
}
} else {
cout << "Error in accepting new connection." << endl;
}
}
close(sock);
return 0;
}
Now, I'm very new to sockets, Im just sort of trying to get a feel for them but I do have some experience with sockets in PHP. I'm using telnet via putty on my linux machine to test this, I don't know if thats causing any issues but the server is outputting some strange characters and I don't know why. I think it has something to do with the buffer, but I'm not really sure. I can send things like "hi" to the server via telnet and it outputs them just fine and sends them back to me but when I send things like "hoobla" it starts the funky character stuff. Any suggestions would be helpful!
Thanks in advance!
You're getting rubbish printed out because recv does not null-terminate your buffer.
The important section in the below code is:
int num = recv(client,buffer,BUFSIZE,0);
if (num < 1) break;
send(client, ">> ", 3, 0); // <<-- Nice to have.
send(client, buffer, num, 0);
buffer[num] = '\0'; // <<-- Really important bit!
if (buffer[num-1] == '\n') // <<-- Nice to have.
buffer[num-1] = '\0'; // <<-- Nice to have.
cout << buffer << endl;
which will properly terminate your buffer before trying to print it, as well as remove the trailing newline if present (and allow the client to distinguish between input and echoed lines).
This one (a complete program) works a little better:
using namespace std;
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#define BUFSIZE 1000
#define PORT 1234
int main() {
char buffer[BUFSIZE];
// define our address structure, stores our port
// and our ip address, and the socket type, etc..
struct sockaddr_in addrinfo;
addrinfo.sin_family = AF_INET;
addrinfo.sin_port = htons(PORT);
addrinfo.sin_addr.s_addr = INADDR_ANY;
// create our socket.
int sock;
if ( (sock = socket(addrinfo.sin_family, SOCK_STREAM, 0)) < 0) {
cout << "Error in creating the socket.";
return -1;
}
// bind our socket to the actual adress we want
if (bind(sock, (struct sockaddr*)&addrinfo, sizeof(addrinfo)) != 0) {
cout << "Error in binding.";
return -1;
}
// open the socket up for listening
if (listen(sock, 5) != 0) {
cout << "Error in opening listener.";
return -1;
}
char *msg = "Success! You are connected.\r\n";
// continuously accept new connections.. but no multithreading.. yet
while(1) {
cout << "Waiting for connections...." << endl;
struct sockaddr_in client_addr;
socklen_t sin_size = sizeof(client_addr);
if(int client =
accept(sock, (struct sockaddr*)&client_addr, &sin_size))
{
cout << "Recieved new connection from "
<< inet_ntoa(client_addr.sin_addr) << endl;
send(client, msg, strlen(msg), 0);
while(1) {
int num = recv(client,buffer,BUFSIZE,0);
if (num < 1) break;
send(client, ">> ", 3, 0);
send(client, buffer, num, 0);
buffer[num] = '\0';
if (buffer[num-1] == '\n')
buffer[num-1] = '\0';
cout << buffer << endl;
strcpy(buffer, "");
}
} else {
cout << "Error in accepting new connection." << endl;
}
}
close(sock);
return 0;
}
On the client side:
$ telnet 127.0.0.1 1234
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Success! You are connected.
hello
>> hello
my name is pax
>> my name is pax
and you?
>> and you?
<CTRL-D>
Connection closed by foreign host.
and, on the server side:
$ ./testprog
Waiting for connections....
Recived new connection from 127.0.0.1
hello
my name is pax
and you?
Waiting for connections....
The problem is that buffer is not guaranteed to contain a string-terminating null character. Add the line buffer[BUFSIZE-1] = '\0' just before your cout << buffer.
Even better, actually record how many bytes were received, and use that information to determine if you overran your buffer.