I made a single server socket that i want to allow multiple connections in a multithreaded fasion but there's an issue. It drops messages from clients for no apparent reason
Each socket is handled by their own thread so my guess was that it shouldn't be an issue (may be it is).
Here is the code
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <vector>
#include <sstream>
#include <cassert>
// Taken from: https://stackoverflow.com/a/46104456/6119618
static std::string wsa_error_to_string(int wsa_error)
{
char msgbuf [256]; // for a message up to 255 bytes.
msgbuf [0] = '\0'; // Microsoft doesn't guarantee this on man page.
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags
nullptr, // lpsource
wsa_error, // message id
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // languageid
msgbuf, // output buffer
sizeof (msgbuf), // size of msgbuf, bytes
nullptr
);
if (! *msgbuf)
sprintf (msgbuf, "%d", wsa_error); // provide error # if no string available
return msgbuf;
}
#define PRINT_ERROR_AND_TERMINATE(MSG) do { std::cerr << (MSG) << std::endl; assert(0); } while(0)
struct wsa_lifetime
{
wsa_lifetime()
{
int result = ::WSAStartup(MAKEWORD(2,2), &wsa_data);
assert(result == 0);
is_initialized = true;
}
~wsa_lifetime()
{
::WSACleanup();
}
WSAData wsa_data {};
bool is_initialized {false};
};
static wsa_lifetime wsa_lifetime;
static SOCKET socket_create()
{
SOCKET socket = ::socket(AF_INET, SOCK_STREAM, 0);
assert(socket != INVALID_SOCKET);
return socket;
}
static void socket_destroy(SOCKET socket)
{
::closesocket(socket);
socket = INVALID_SOCKET;
}
static void socket_bind(SOCKET socket, const char *address, uint16_t port)
{
sockaddr_in addr {};
addr.sin_family = AF_INET;
inet_pton(AF_INET, address, &addr.sin_addr.s_addr);
addr.sin_port = htons(port);
int bind_result = ::bind(socket, reinterpret_cast<SOCKADDR *>(&addr), sizeof(addr));
if (bind_result == SOCKET_ERROR)
PRINT_ERROR_AND_TERMINATE(WSAGetLastError());
}
static void socket_connect(SOCKET socket, const char *address, uint16_t port)
{
sockaddr_in addr {};
addr.sin_family = AF_INET;
inet_pton(AF_INET, address, &addr.sin_addr.s_addr);
addr.sin_port = htons(port);
int connect_result = ::connect(socket, reinterpret_cast<SOCKADDR *>(&addr), sizeof(addr));
if (connect_result == SOCKET_ERROR)
PRINT_ERROR_AND_TERMINATE(WSAGetLastError());
}
static void socket_listen(SOCKET socket)
{
int listen_result = ::listen(socket, SOMAXCONN);
if (listen_result == SOCKET_ERROR)
PRINT_ERROR_AND_TERMINATE(WSAGetLastError());
}
static SOCKET socket_accept(SOCKET socket)
{
SOCKET accepted_socket = ::accept(socket, nullptr, nullptr);
if (accepted_socket == INVALID_SOCKET)
PRINT_ERROR_AND_TERMINATE(WSAGetLastError());
return accepted_socket;
}
static size_t socket_recv(SOCKET socket, char *buffer, size_t buffer_size, int flags = 0)
{
int bytes_received = ::recv(socket, buffer, static_cast<int>(buffer_size), flags);
if (bytes_received == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err == WSAECONNRESET)
return 0; // Disconnected client
PRINT_ERROR_AND_TERMINATE(WSAGetLastError());
}
return bytes_received;
}
static size_t socket_send(SOCKET socket, const char *data, size_t data_size, int flags = 0)
{
int bytes_sent = ::send(socket, data, static_cast<int>(data_size), flags);
if (bytes_sent == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err == WSAECONNRESET)
return 0; // Disconnected client
PRINT_ERROR_AND_TERMINATE(WSAGetLastError());
}
return bytes_sent;
}
static std::mutex output_mutex;
int main()
{
const char *server_address = "127.0.0.1";
uint16_t server_port = 23456;
bool server_terminate = false;
std::thread server_thread([server_address, server_port, &server_terminate](){
SOCKET server = socket_create();
socket_bind(server, server_address, server_port);
socket_listen(server);
std::vector<SOCKET> clients;
std::vector<std::thread> client_threads;
while (!server_terminate)
{
SOCKET incoming_client = socket_accept(server);
if (server_terminate)
break;
clients.push_back(incoming_client);
size_t client_id = clients.size();
std::thread incoming_client_thread([&incoming_client, client_id](){
const size_t data_size = 1024;
char data[data_size];
while (true)
{
size_t bytes_received = socket_recv(incoming_client, data, data_size, 0);
if (bytes_received == 0)
break;
std::string_view client_message(data, bytes_received);
{
std::unique_lock lock(output_mutex);
std::cout << "Client (" << client_id << "): " << client_message << std::endl;
}
}
});
client_threads.push_back(std::move(incoming_client_thread));
}
for (std::thread &client_thread: client_threads)
if (client_thread.joinable())
client_thread.join();
});
std::vector<SOCKET> clients;
std::vector<std::thread> client_threads;
for (int i = 0; i < 4; i++)
{
SOCKET client = socket_create();
clients.push_back(client);
}
for (SOCKET client : clients)
{
std::thread client_thread([server_address, server_port, client](){
socket_connect(client, server_address, server_port);
for (int i = 0; i < 10; i++)
{
std::string data_str = (std::stringstream() << "hello " << i).str();
socket_send(client, data_str.c_str(), data_str.size());
using namespace std::chrono_literals;
std::this_thread::sleep_for(100ms + 1ms * (rand() % 100));
}
});
client_threads.push_back(std::move(client_thread));
}
for (std::thread &client_thread : client_threads)
if (client_thread.joinable())
client_thread.join();
for (SOCKET client: clients)
socket_destroy(client);
clients.clear();
server_terminate = true;
SOCKET dummy_socket = socket_create();
socket_connect(dummy_socket, server_address, server_port); // just to unblock server's socket_accept() blocking call
socket_destroy(dummy_socket);
if (server_thread.joinable())
server_thread.join();
return 0;
}
Possible output:
Client (2): hello 0
Client (2): hello 0
Client (3): hello 1
Client (2): hello 2
Client (1): hello 3
Client (4): hello 4
Client (3): hello 5
Client (2): hello 6
Client (1): hello 7
Client (4): hello 8
Client (3): hello 9
I expected each client to send 10 messages, 40 in total but some messages are dropped as you can see. I think it shouldn't drop even with UDP transport because all job is done on my loopback network
Wireshark registers all the messages
When constructing the lambda incoming_client_thread, you capture incoming_client by reference and not by copy.
Since this variable is reset at the start of each loop by socket_accept, a thread might not be calling socket_recv on the same socket once another socket_accept succeeds.
Related
#include <stdio.h>
#include <time.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment(lib, "WS2_32.lib")
#define IP_ADDRESS "127.0.0.1"
#define PORT 20000
#define BUF_SIZE 64
#undef FD_SETSIZE
#define FD_SETSIZE 10000
void shuffle_buffer(char* buf, size_t size);
SOCKET create_socket();
void send_data(SOCKET sock);
int main()
{
WSADATA ws;
if (WSAStartup(MAKEWORD(2, 2), &ws) != 0)
{
printf("Init Windows Socket Failed::%d\n", GetLastError());
return -1;
}
const int CLIENT_SIZE = 1;
SOCKET socks[CLIENT_SIZE];
struct timeval tv = { 0, 10 };
fd_set fd_read, fd_write;
FD_ZERO(&fd_read);
FD_ZERO(&fd_write);
for (int i = 0; i < CLIENT_SIZE; i++) {
SOCKET sock = create_socket();
socks[i] = sock;
FD_SET(sock, &fd_write);
FD_SET(sock, &fd_read);
}
Sleep(1000);
int number_to_recv = CLIENT_SIZE;
while (number_to_recv > 0) {
int ret = select(CLIENT_SIZE, &fd_read, &fd_write, NULL, &tv);
for (int i = 0; i < CLIENT_SIZE; i++) {
if (FD_ISSET(socks[i], &fd_read)) {
char buf[BUF_SIZE];
int n = recv(socks[i], buf, BUF_SIZE, 0);
buf[n] = 0;
printf("%s\n", buf);
number_to_recv--;
}
if (FD_ISSET(socks[i], &fd_write)) {
send_data(socks[i]);
FD_CLR(socks[i], &fd_write);
//Sleep(1);
}
}
//printf("ret and number : %d, %d\n", ret, number_to_recv);
}
for (int i = 0; i < CLIENT_SIZE; i++) {
closesocket(socks[i]);
}
WSACleanup();
}
SOCKET create_socket()
{
SOCKET cli_sock;
struct sockaddr_in addr;
if ((cli_sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
printf("Create Socket Failed::%d\n", GetLastError());
return -1;
}
//inet_pton
memset(addr.sin_zero, 0x00, 8);
addr.sin_family = AF_INET;
inet_pton(AF_INET, IP_ADDRESS, (void*)(&addr.sin_addr.s_addr));
addr.sin_port = htons(PORT);
if (connect(cli_sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
{
printf("Connect Error::%d\n", GetLastError());
return -1;
}
return cli_sock;
}
void send_data(SOCKET sock)
{
const int SEND_SIZE = BUF_SIZE / 2;
char buf[SEND_SIZE] = { 0 };
memset(buf, 'a', SEND_SIZE);
shuffle_buffer(buf, SEND_SIZE);
if (send(sock, buf, SEND_SIZE, 0) == SOCKET_ERROR)
{
printf("Send Info Error::%d\n", GetLastError());
}
}
void shuffle_buffer(char* buf, size_t size)
{
for (int i = 0; i < size; i++) {
buf[i] += int(rand() % 26);
}
}
Code above is a socket client using select model run on Win10, the problem is after I send data, but I can not receive data(I am sure that server has sent back data), this code below doesn`t run, so what is the problem? Thanks
The first parameter in select is maxfdp, and I know the difference between Win and Unix, so on Windows, this parameter seems not necessary, and I can write data,
but can not receive it.
if (FD_ISSET(socks[i], &fd_read)) {
char buf[BUF_SIZE];
int n = recv(socks[i], buf, BUF_SIZE, 0);
buf[n] = 0;
printf("%s\n", buf);
number_to_recv--;
}
select removes the sockets from the fd_set if they are not readable/writable. You need to add them back in before the next time you call select.
The reason your code can write data is because sockets start out being writable, so they will still be set in fd_write and your code will write data. They don't start out being readable, if no data has been received yet, so they'll be removed from the fd_read set and then your code stops checking whether they are readable.
So I have a winsock application in which the computer tries to accept a client, and then starts a new thread with the a pointer to the class object with client info which it copies and then deletes with the delete keyword.
The backbone is copied but the beginning of the OOP-structure is my work and the current implementation seems to be giving me some issues.
I noticed the socket might not copy in the correct format, but have no idea on how to fix this. As the client tries to connect it throws me the error message of that the server refused the connection. Anyway, here is the code of the server.
UPDATE:
It's working.
// main.h
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <process.h>
#include <cstring>
#include <iostream>
#include <stdio.h>
#include <memory>
#pragma comment(lib,"ws2_32.lib" )
class clientData{
private:
SOCKET clientSocket;
std::string name;
public:
clientData(SOCKET &clientSock, const char *tName);
clientData(clientData *cliDat);
~clientData();
inline SOCKET *getClientSocket(){
return &clientSocket;
}
inline std::string *getName(){
return &name;
}
};
And here is the main cpp file
// main.cpp
#include "main.h"
unsigned int __stdcall ServClient(void *data);
int main(int argc, char *argv[])
{
WSADATA wsaData;
int iResult;
sockaddr_in addr;
SOCKET sock, client[10];
addr.sin_family = AF_INET;
addr.sin_port = htons(13337);
addr.sin_addr.S_un.S_addr = INADDR_ANY;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);//2.2
if (iResult)
{
printf("WSA startup failed");
return 0;
}
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
{
printf("Invalid socket");
return 0;
}
int addrlen = sizeof(sockaddr_in);
iResult = bind(sock, (sockaddr*)&addr, sizeof(sockaddr_in));
if (iResult)
{
printf("bind failed %u", GetLastError());
return 0;
}
iResult = listen(sock, SOMAXCONN);
if (iResult)
{
printf("iResult failed %u", GetLastError());
return 0;
}
int iterationCount = 0;
while ((iterationCount < 10 &&
(client[iterationCount] = accept(sock, (SOCKADDR*)&addr, &addrlen)) != SOCKET_ERROR))
{
if (client[iterationCount] == INVALID_SOCKET){
printf("invalid client socket",GetLastError());
continue;
}
++iterationCount;
char tempName[100] = { '\0' };
sprintf_s(tempName, sizeof(tempName), "Client %u", iterationCount);
clientData *tempCLdata = new clientData(client[iterationCount - 1], tempName);
_beginthreadex(0, 0, ServClient, (void*)tempCLdata, 0, 0);
tempCLdata = nullptr;
}
return 0;
}
unsigned int __stdcall ServClient(void *data)
{
clientData cliDat((clientData*)data);
delete (clientData*)data;
printf("Client connected\n");
int recvLen = 1;
char chunk[1024] = { '\0' };
while ((recvLen = recv(*cliDat.getClientSocket(), chunk, 1024, 0)) > 0){
printf("%.*s", recvLen, chunk);
}
if (recvLen == -1)
perror("Socket recv() problem..\n");
else
printf("End of data on socket, closing..\n");
closesocket(*cliDat.getClientSocket());
return 0;
}
clientData::clientData(SOCKET &clientSock, const char *tName){
clientSocket = clientSock;
name.assign(tName);
}
clientData::clientData(clientData *cliDat){
SOCKET *clientSocketPtr = cliDat->getClientSocket();
clientSocket = *clientSocketPtr;
name.assign(cliDat->getName()->c_str());
}
clientData::~clientData(){
}
Also here is the relevant bit of client code, as requested.
char buffer[1023] = { '\0' };
int heartbeatCount = 0;
while (true)
{
sprintf_s(buffer, sizeof(buffer), "Heartbeat %d", heartbeatCount + 1);
send(sock, buffer, strlen(buffer) + 1, 0);
++heartbeatCount;
Sleep(1000);
}
I have to make an app using C sockets on Mac-OS that sends data from one socket to other socket, like this.
Server waits for connections
Client connect to server(from 1). -> socket1
Server connects to an external server and obtains an socket. -> socket2
From now on the server job is finish. The data exchange should be made only between the client socket (from 2) and socket obtained from 3.
Current implementation:
Server makes the connection and then reads data from one socket and sends to other.
Any ides how after step 3 to pipe line the two sockets socket1 and socket2.
Well your problem can be solved in two ways:
1) You need to code the part related to the connection formation between client and external server. But this puts an extra overload on the client, because it needs to make two connections, to both the servers (and I strongly feel the middle server in this case is useless).
2) Second way of solving it is passing the sockets between the servers:
Client connects to the server, this middle server sends this socket to the external server. Now external server starts communication with the client. This can be done only if both the server processes run on the same machine. And the file-descriptors are usually passed using Unix Domain Sockets.
Here is the code which I have. You can use these two functions to either send or receive the file-descriptors. It works on my Linux machine. I don't know about Mac-OS.
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
/* this function passes 'fd_to_send'
file descriptor via
a unix domain socket 'sfd'...
*/
void pass_fd(int sfd, int fd_to_send)
{
struct msghdr msg;
/*allocate memory to 'msg_control' field in msghdr struct */
char buf[CMSG_SPACE(sizeof(int))];
/*the memory to be allocated should include data + header..
this is calculated by the above macro...(it merely adds some
no. of bytes and returs that number..*/
struct cmsghdr *cmsg;
struct iovec ve;
/*must send/receive atleast one byte...
main purpose is to have some error
checking.. but this is completely
irrelevant in the current context..*/
char *st ="I";
/*jst let us allocate 1 byte for formality
and leave it that way...*/
ve.iov_base = st;
ve.iov_len =1;
/*attach this memory to our main msghdr struct...*/
msg.msg_iov = &ve;
msg.msg_iovlen = 1;
/*these are optional fields ..
leave these fields with zeros..
to prevent unnecessary SIGSEGVs..*/
msg.msg_name = NULL;
msg.msg_namelen = 0;
/*here starts the main part..*/
/*attach the 'buf' to msg_control..
and fill in the size field correspondingly..
*/
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
/*actually msg_control field must
point to a struct of type 'cmsghdr'
we just allocated the memory, yet we need to
set all the corresponding fields..
It is done as follows:
*/
cmsg = CMSG_FIRSTHDR(&msg);
/* this macro returns the address in the buffer..
from where the first header starts..
*/
/*set all the fields appropriately..*/
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send));
/*in the above field we need to store
the size of header + data(in this case 4 bytes(int) for our fd..
this is returned by the 'CMSG_LEN' macro..*/
*(int*)CMSG_DATA(cmsg) = fd_to_send;
/*after the above three fields we keep the actual data..
the macro 'CMSG_DATA' returns pointer to this location
and we set it to the file descriptor to be sent..
*/
msg.msg_controllen = cmsg->cmsg_len;
/*now that we have filled the 'cmsg' struct
we store the size of this struct..*/
/*this one isn't required when you
pass a single fd..
but useful when u pass multiple fds.*/
msg.msg_flags = 0;
/*leave the flags field zeroed..*/
if(sendmsg( sfd, &msg, 0)==-1){ perror("snd:\n"); exit(1); }
/*send this over the UNIX deomain socoket..*/
printf("sent fd:%d\n", fd_to_send);
close(fd_to_send);
/*close the fd which was sent..*/
}
/*returns the received fd over the unix domain socket 'sfd'..*/
int recv_fd(int sfd)
{
struct msghdr msg;
/*do all the unwanted things first...
same as the send_fd function..*/
struct iovec io;
char ptr[1];
io.iov_base = ptr;
io.iov_len = 1;
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &io;
msg.msg_iovlen = 1;
/*-----------------------*/
char buf[CMSG_SPACE(sizeof(int))];
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
/*reasoning is same..as above*/
/*now here comes the main part..*/
if(recvmsg( sfd, &msg, 0)==-1)
{
/*some shit has happened*/
perror("recv\n");
exit(1);
}
struct cmsghdr *cm;
cm = CMSG_FIRSTHDR(&msg);
/*get the first message header..*/
if(cm->cmsg_type != SCM_RIGHTS)
{
/*again some shit has happened..*/
perror("unknown type..\n");
exit(1);
}
/*if control has reached here.. this means
we have got the correct message..and when you
extract the fd out of this message
this need not be same as the one which was sent..
allocating a new fd is all done by the kernel
and our job is jst to use it..*/
printf("received fd:%d\n", *(int*)CMSG_DATA(cm));
return *(int*)CMSG_DATA(cm);
}
In the below example:
ClientOne and ClientTwo connect to the server.
When the server receives both ClientOne and ClientTwo's socket descriptor, it sends ClientOne's information to ClientTwo and vice-versa.
The information it sends is the IP and the client is coming from. Server shuts down.
When each client receives their info, a socket is created and they connect to eachother. The server socket is then shutdown.
Socket Class:
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <windows.h>
#include <cstdint>
#include <string>
#include <stdexcept>
#include <iostream>
#include <thread>
#include <vector>
class Socket
{
private:
SOCKET socket;
std::uint32_t Port;
std::string Address;
bool Listen, Initialized, Asynchronous;
void Swap(Socket &S);
void UnInitialized();
public:
Socket();
Socket(std::uint32_t Port, std::string Address, bool Listen = false, bool Asynchronous = false);
Socket(const Socket &S) = delete;
Socket(Socket && S);
~Socket();
Socket& operator = (const Socket &S) = delete;
Socket& operator = (Socket && S);
int Recv(void* Buffer, std::uint32_t BufferLength);
int Recv(SOCKET S, void* Buffer, std::uint32_t BufferLength);
std::uint32_t RecvEx(void* Buffer, std::uint32_t BufferLength);
std::uint32_t RecvEx(SOCKET S, void* Buffer, std::uint32_t BufferLength);
int Send(void* Buffer, std::size_t BufferSize);
int Send(SOCKET S, void* Buffer, std::size_t BufferSize);
void Connect();
void Connect(std::uint32_t Port, std::string Address, bool Listen, bool Asynchronous);
SOCKET Accept(sockaddr* ClientInfo, int* ClientInfoSize);
void Close();
SOCKET GetSocket() const;
};
Socket::~Socket()
{
Close();
}
void Socket::Close()
{
if (socket)
{
shutdown(socket, SD_BOTH);
closesocket(socket);
socket = 0;
}
if (Initialized)
{
WSACleanup();
}
}
SOCKET Socket::GetSocket() const
{
return this->socket;
}
Socket::Socket(Socket && S) : socket(std::move(S.socket)), Port(std::move(S.Port)), Address(std::move(S.Address)), Listen(std::move(S.Listen)), Initialized(std::move(S.Initialized)), Asynchronous(std::move(S.Asynchronous)) {}
Socket::Socket() : socket(0), Port(0), Address(std::string()), Listen(false), Initialized(false), Asynchronous(false) {}
Socket::Socket(std::uint32_t Port, std::string Address, bool Listen, bool Asynchronous) : socket(0), Port(Port), Address(Address), Listen(Listen), Initialized(true), Asynchronous(Asynchronous)
{
Connect(Port, Address, Listen, Asynchronous);
}
void Socket::Connect()
{
UnInitialized();
Connect(Port, Address, Listen, Asynchronous);
}
void Socket::Connect(std::uint32_t Port, std::string Address, bool Listen, bool Asynchronous)
{
if (!socket)
{
this->Port = Port;
this->Address = Address;
this->Asynchronous = Asynchronous;
this->Initialized = true;
WSADATA wsaData;
struct sockaddr_in* sockaddr_ipv4;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
throw std::runtime_error("Error: WSAStartup Failed");
}
if (Address != "INADDR_ANY")
{
if (Address.find("http://") != std::string::npos)
{
Address = Address.substr(7);
}
std::size_t Position = Address.find("/");
if (Position != std::string::npos)
{
Address = Address.substr(0, Position);
}
struct addrinfo *it = nullptr, *result = nullptr;
getaddrinfo(Address.c_str(), nullptr, nullptr, &result);
for (it = result; it != nullptr; it = it->ai_next)
{
sockaddr_ipv4 = reinterpret_cast<sockaddr_in*>(it->ai_addr);
Address = inet_ntoa(sockaddr_ipv4->sin_addr);
if (Address != "0.0.0.0") break;
}
freeaddrinfo(result);
}
if ((this->socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
this->Close();
throw std::runtime_error("Error: Failed to create socket");
}
struct sockaddr_in SockAddr;
memset(&SockAddr, 0, sizeof(SockAddr));
SockAddr.sin_port = htons(Port);
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = (Address == "INADDR_ANY" ? htonl(INADDR_ANY) : inet_addr(Address.c_str()));
if (Listen && (bind(this->socket, reinterpret_cast<SOCKADDR*>(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR))
{
this->Close();
throw std::runtime_error("Error: Socket binding failed");
}
if (Listen && (listen(this->socket, SOMAXCONN) == SOCKET_ERROR))
{
this->Close();
throw std::runtime_error("Error: Socket Listening Failed");
}
if(!Listen && (connect(this->socket, reinterpret_cast<SOCKADDR*>(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR))
{
if(Asynchronous && WSAGetLastError() != WSAEWOULDBLOCK)
{
this->Close();
throw std::runtime_error("Error: Socket Connection failed");
}
else if (!Asynchronous)
{
this->Close();
throw std::runtime_error("Error: Socket Connection failed");
}
}
}
}
SOCKET Socket::Accept(sockaddr* ClientInfo, int* ClientInfoSize)
{
static int Size = sizeof(sockaddr);
return accept(this->socket, ClientInfo, (ClientInfo && ClientInfoSize ? ClientInfoSize : &Size));
}
Socket& Socket::operator = (Socket && S)
{
S.Swap(*this);
return *this;
}
int Socket::Recv(void* Buffer, std::uint32_t BufferLength)
{
return recv(this->socket, reinterpret_cast<char*>(Buffer), BufferLength, 0);
}
int Socket::Recv(SOCKET S, void* Buffer, std::uint32_t BufferLength)
{
return recv(S, reinterpret_cast<char*>(Buffer), BufferLength, 0);
}
std::uint32_t Socket::RecvEx(void* Buffer, std::uint32_t BufferLength)
{
return this->RecvEx(this->socket, Buffer, BufferLength);
}
std::uint32_t Socket::RecvEx(SOCKET S, void* Buffer, std::uint32_t BufferLength)
{
UnInitialized();
char* Pointer = reinterpret_cast<char*>(Buffer);
std::uint32_t TotalRead = 0;
while (BufferLength > 0)
{
int BytesRead = recv(S, Pointer, std::min(1024 * 1024, static_cast<int>(BufferLength)), 0);
if (BytesRead < 0)
{
if ((BytesRead == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK))
continue;
throw std::runtime_error("Error! RecvEx: Failed To Read Bytes.");
}
if (BytesRead == 0) break;
Pointer += BytesRead;
BufferLength -= BytesRead;
TotalRead += BytesRead;
}
return TotalRead;
}
int Socket::Send(void* Buffer, std::size_t BufferSize)
{
return send(this->socket, reinterpret_cast<char*>(Buffer), BufferSize, 0);
}
int Socket::Send(SOCKET S, void* Buffer, std::size_t BufferSize)
{
return send(S, reinterpret_cast<char*>(Buffer), BufferSize, 0);
}
void Socket::Swap(Socket &S)
{
using std::swap;
swap(socket, S.socket);
swap(Port, S.Port);
swap(Address, S.Address);
swap(Listen, S.Listen);
swap(Initialized, S.Initialized);
swap(Asynchronous, S.Asynchronous);
}
void Socket::UnInitialized()
{
if (!Initialized)
{
throw std::runtime_error("\nError! Socket Not Constructed!");
std::cout << "Socket Not Constructed!\n";
ExitProcess(0);
}
}
Server.cpp:
#include "Sockets.hpp"
#define PORT 27015
#define ADDRESS INADDR_ANY
#define CLIENTCOUNT 2
typedef struct
{
std::string ip;
int port;
SOCKET sock;
} ClientInfo;
template <typename T>
inline T ReadPointer(TCHAR* &Pointer)
{
T Result = *(reinterpret_cast<T*>(Pointer));
Pointer += sizeof(T) / sizeof(TCHAR);
return Result;
}
template <typename T>
inline void WritePointer(TCHAR* &Pointer, const T& Value)
{
*(reinterpret_cast<T*>(Pointer)) = Value;
Pointer += sizeof(T) / sizeof(TCHAR);
}
bool SendClient(ClientInfo* client, ClientInfo* receiver)
{
int datasize = sizeof(client->ip.size()) + client->ip.size() + sizeof(client->port);
std::vector<char> buffer(datasize, 0);
char* ptr = &buffer[0];
WritePointer(ptr, client->ip.size());
for (std::size_t i = 0; i < client->ip.size(); ++i)
WritePointer(ptr, client->ip[i]);
WritePointer(ptr, client->port);
std::cout << "Sending: " << &buffer[0] << "\n";
return send(receiver->sock, &buffer[0], datasize, 0) == datasize;
}
bool ReadClient(SOCKET sock, ClientInfo* client)
{
std::size_t ip_size = 0;
recv(sock, (char*) &ip_size, sizeof(client->ip.size()), 0);
client->ip.resize(ip_size);
recv(sock, &client->ip[0], ip_size, 0);
recv(sock, (char*) &client->port, sizeof(int), 0);
std::cout<<client->ip<<"\n";
return true;
}
int main()
{
Socket s;
s.Connect(PORT, "localhost", true, false);
char buffer[1024] = {0};
std::vector<ClientInfo> clients;
while(true)
{
if (clients.size() < CLIENTCOUNT)
{
sockaddr_in ClientAddressInfo = {0};
SOCKET sock = s.Accept(reinterpret_cast<sockaddr*>(&ClientAddressInfo), nullptr);
char* ip = inet_ntoa(ClientAddressInfo.sin_addr);
int port = (int) ntohs(ClientAddressInfo.sin_port);
ClientInfo info = {ip, port, sock};
clients.push_back(info);
std::cout << "Client Connected From: " << ip << " on port: " << port << "\n";
}
if (ReadAsync(s, buffer))
{
std::cout << "Connected\n";
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (clients.size() >= CLIENTCOUNT)
{
SendClient(&clients[0], &clients[1]);
SendClient(&clients[1], &clients[0]);
return 0;
}
}
}
Client.cpp:
#define PORT 27015
#define ADDRESS INADDR_ANY
#define CLIENTCOUNT 2
typedef struct
{
std::string ip;
int port;
SOCKET sock;
} ClientInfo;
template <typename T>
inline T ReadPointer(TCHAR* &Pointer)
{
T Result = *(reinterpret_cast<T*>(Pointer));
Pointer += sizeof(T) / sizeof(TCHAR);
return Result;
}
template <typename T>
inline void WritePointer(TCHAR* &Pointer, const T& Value)
{
*(reinterpret_cast<T*>(Pointer)) = Value;
Pointer += sizeof(T) / sizeof(TCHAR);
}
bool SendClient(ClientInfo* client, ClientInfo* receiver)
{
int datasize = sizeof(client->ip.size()) + client->ip.size() + sizeof(client->port);
std::vector<char> buffer(datasize, 0);
char* ptr = &buffer[0];
WritePointer(ptr, client->ip.size());
for (std::size_t i = 0; i < client->ip.size(); ++i)
WritePointer(ptr, client->ip[i]);
WritePointer(ptr, client->port);
std::cout << "Sending: " << &buffer[0] << "\n";
return send(receiver->sock, &buffer[0], datasize, 0) == datasize;
}
bool ReadClient(SOCKET sock, ClientInfo* client)
{
std::size_t ip_size = 0;
recv(sock, (char*) &ip_size, sizeof(client->ip.size()), 0);
client->ip.resize(ip_size);
recv(sock, &client->ip[0], ip_size, 0);
recv(sock, (char*) &client->port, sizeof(int), 0);
return true;
}
bool ReadAsync(const Socket &sock, ClientInfo* client)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sock.GetSocket(), &rfds);
for (int i = 0; i < 600; ++i)
{
if (select(sock.GetSocket(), &rfds, &rfds, NULL, &tv))
{
return ReadClient(sock.GetSocket(), client);
}
tv.tv_sec = 0;
tv.tv_usec = 100000;
}
return false;
}
int main()
{
Socket s;
s.Connect(PORT, "localhost", false, false);
std::vector<SOCKET> clients;
ClientInfo client = {};
while(true)
{
if (ReadAsync(s, &client))
{
std::cout<<"IP: "<<client.ip<<" PORT: "<<client.port<<"\n";
s = std::move(Socket(client.port, client.ip, true, false));
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
system("CLS");
std::cout<<"Connecting..\n";
}
}
Recently I have been working on some client-side code for sending and receiving messages from a server using threading. The below code behaves strangely when run. Upon inputting a message to send to the server, the code completes the task, albeit with a "socket already in use" error, the server gets it. But every subsequent message I attempt to send to the server is not received immediately, yet it is seemingly all received at once when the client program terminates.
(Additionally, I am certain the error is client-side, the strange behavior isn't exhibited if one comments the output function.)
How can I fix this error?
Client
#include <stdio.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string>
#include <iostream>
#include <errno.h>
#include <pthread.h>
void* input(void* ptr)
{
int on = 1;
bool *input_done = ((struct thread_args*)ptr)->process_done;
struct addrinfo *res = ((struct thread_args*)ptr)->result;
char msg[256];
int sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof(on));
bind(sock,res->ai_addr,res->ai_addrlen);
connect(sock,res->ai_addr,res->ai_addrlen);
cin.getline(msg,256);
if (msg[0] == '/') {exit(1);}
send(sock,msg,sizeof msg,0);
cout << "You:" << msg << endl;
*input_done = 1;
close(sock);
pthread_exit(NULL);
}
void* output(void* ptr)
{
int on = 1;
bool *output_done = ((struct thread_args*)ptr)->process_done;
struct addrinfo *res = ((struct thread_args*)ptr)->result;
char msg[256];
int sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
bind(sock,res->ai_addr,res->ai_addrlen);
connect(sock,res->ai_addr,res->ai_addrlen);
recv(sock,msg,sizeof msg,0);
cout << "Recieved:" << msg;
*output_done = 1;
close(sock);
pthread_exit(NULL);
}
void io_client()
{
//thread function variables
pthread_t t1,t2;
bool input_done = 1, output_done = 1;
//socket setup variables
struct addrinfo hints, *res;
memset(&hints,0,sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
getaddrinfo("localhost","8080",&hints,&res);
//setting up structures to pass data to threaded functions
struct thread_args i_args, o_args;
i_args.result = res; i_args.process_done = &input_done;
o_args.result = res; o_args.process_done = &output_done;
while(1)
{
if (output_done)
{
pthread_create(&t2,NULL,output,&o_args);
output_done = 0;
}
if (input_done)
{
pthread_create(&t1,NULL,input,&i_args);
input_done = 0;
}
}
}
int main()
{
io_client();
}
Server
void server()
{
struct addrinfo hints, *res;
int sock=-1, newsock=-1;
int length, on=1;
char **address_list; int entries = 0;
//fd_set read_fd;
//struct timeval timeout;
char buffer[100];
memset(&hints,0,sizeof hints);
res = NULL;
memset(&res,0,sizeof res);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
getaddrinfo("localhost","8080",&hints,&res);
sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof(on));
bind(sock,res->ai_addr,res->ai_addrlen);
listen(sock,10);
while(1)
{
struct sockaddr_storage addr;
char ipstr[INET6_ADDRSTRLEN];
socklen_t len;
len = sizeof addr;
newsock = accept(sock,NULL,NULL);
getpeername(newsock,(struct sockaddr*)&addr,&len);
struct sockaddr_in *s = (struct sockaddr_in*)&addr;
inet_ntop(AF_INET,&s->sin_addr,ipstr,sizeof ipstr);
length = 100;
setsockopt(newsock,SOL_SOCKET,SO_RCVLOWAT, (char*)&length,sizeof length);
recv(newsock,buffer,sizeof buffer,0);
cout << buffer << endl;
}
if (newsock != -1)
{
close(newsock);
}
if (sock != -1)
{
close(sock);
}
}
int main()
{
server();
}
It looks like you are trying to have your client bind() to the same port as the server. That's not necessary. And worse, you are trying to bind to to the IP address of the server - which is also a bigger problem. In general, for client sockets that are to call the connect() function, you should just have your socket bind to port 0 and IP 0, thus letting the OS pick a randomly available port for you and enabling use the right local IP address and adapter for the connection. You can call getsockname() to discover what port the OS picked for you after you call connect.
And if you let the OS pick the client port for you, you won't need that SO_REUSESADDR call. Although, your server code could call it for cases where it needs to restart after shutting down with connections still pending to close.
Also. you aren't checking the return value of any of your socket calls. That's probably why you are getting some mysterious results. The call to bind() is more likely failing because you are specifying the server IP, but connect() is succeeding because it will auto-bind the socket if it hasn't already.
Here's a cleaned up version of you input() function. Converting your output() function is an exercise left up to the reader. If you follow my example, you'll be in good shape.
void* input(void* ptr)
{
int on = 1;
bool *input_done = ((struct thread_args*)ptr)->process_done;
int ret;
int success = true;
struct sockaddr_in addrLocal = {};
struct addrinfo *res = ((struct thread_args*)ptr)->result;
char msg[256];
int sock = socket(AF_INET, SOCK_STREAM, 0);
success = (sock != -1);
if (success)
{
addrLocal.sin_family = AF_INET;
addrLocal.sin_port = INADDR_ANY; // INADDR_ANY == 0 --> pick a random port for me
addrLocal.sin_addr.s_addr = INADDR_ANY; // INADDR_ANY == 0 --> use all appropriate network
ret = bind(sock,(sockaddr*)&addrLocal,sizeof(addrLocal));
if (ret == -1) perror("bind: ");
success = (ret != -1);
}
if (success)
{
ret = connect(sock,res->ai_addr,res->ai_addrlen);
if (ret == -1) perror("connect: ");
success = (ret != -1);
}
if (success)
{
cin.getline(msg,256);
if (msg[0] == '/') {exit(1);}
ret = send(sock,msg,sizeof msg,0);
if (ret == -1) perror("send: ");
success = (ret != -1);
}
if (success)
{
cout << "You:" << msg << endl;
*input_done = 1;
}
if (sock != -1)
{
close(sock);
sock = -1;
}
return NULL;
}
I guess that "SO_REUSEADDR" socket option that you are giving is the problem.
Are you calling that function again and again without closing the client socket ? In that case it will not work. The purpose of this socket option is to "reuse the address when the already opened socket for the same address is in TIME_WAIT state else you will get the mentioned error".
If you client is opening a new connection each and every time, then I must say that you will have to structure your code more efficiently and handle the socket closing scenarios as well.
So I've been programming with TCP for quite a while, and decided to pick up UDP. I'm not quite sure what needs to be done in order for me to have communication both ways across the WAN(or lan for that matter, easier on lan because I could just open two ports) With UDP once I send information from client to server how can I respond on that socket. Is there a way to connect directly?
(Current quick functions)
int udpsock(int port, const char* addr){
int handle = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if (handle < 1)
return -1;
sockaddr_in address;
address.sin_family = AF_INET;
if (addr == INADDR_ANY)
address.sin_addr.s_addr = INADDR_ANY;
else
address.sin_addr.s_addr = inet_addr(addr);
address.sin_port = htons( (unsigned short) port );
if ( bind( handle, (const sockaddr*) &address, sizeof(sockaddr_in) ) < 0 )
return -1;
return handle;
}
string recvudp(int sock,const int size){
sockaddr_in SenderAddr;
int SenderAddrSize = sizeof (SenderAddr);
char buf[size];
int retsize = recvfrom(sock, buf, sizeof(buf), 0, (SOCKADDR *) & SenderAddr, &Sen derAddrSize);
if (retsize == -1){
cout << "\nRecv Error : " << WSAGetLastError();
if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == 0){
return "";
}
return "\0";
}
else if (retsize < size){
buf[retsize] = NULL;
}
return buf;
}
int sendudp(string str, string ip, unsigned short port, int sock){
sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr( ip.c_str() );
dest.sin_port = htons( port );
int ret = sendto(sock,str.c_str(),str.size(),0, (sockaddr*)&dest,sizeof(dest));
if (ret == -1){
cout << "\nSend Error Code : " << WSAGetLastError();
}
return ret;
}
With this it's pretty easy to make a socket with port xxxx and have the partner send on that port to get data to the client, the forth part is where I'm having some trouble =]
Make your sendudp function take a sockaddr_in. You get one back from recvfrom and can pass it to sendto. Alternatively, pass the received sockaddr_in to connect and use send from then on.
I assume that functions you posted should be shared between client and server. They need to be slightly modified in order to achieve that. E.g. on the server side, recvudp should return client address (possibly as an out parameter) as it is needed later for sending message back to it. Furthermore, as client address structure is already filled (in recvudp on the server side or manually on the client side) we can just pass it to sendudp as its argument.
I've played with this a bit and created two simple projects in Visual Studio 2010: UDP Server and client. They both use shared functions mentioned above. This code is far from perfect and is aimed only to show basic UDP socket communication.
Shared.h:
#ifndef SHARED_H
#define SHARED_H
#include <winsock2.h>
#include <string>
int udpsock(int port, const char* addr);
std::string recvudp(int sock, const int size, sockaddr_in& SenderAddr, int& SenderAddrSize);
int sendudp(std::string str, sockaddr_in dest, int sock);
#endif
Shared.cpp:
#include "Include\shared.h" // path to header - you might use different one
#include <iostream>
using namespace std;
int udpsock(int port, const char* addr)
{
int handle = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if (handle < 1)
return -1;
sockaddr_in address;
address.sin_family = AF_INET;
if (addr == INADDR_ANY)
address.sin_addr.s_addr = INADDR_ANY;
else
address.sin_addr.s_addr = inet_addr(addr);
address.sin_port = htons( (unsigned short) port );
if ( bind( handle, (const sockaddr*) &address, sizeof(sockaddr_in) ) < 0 )
return -1;
return handle;
}
// function should return sender address info (for the code the server)
string recvudp(int sock, const int size, sockaddr_in& SenderAddr, int& SenderAddrSize)
{
// TODO: use std::vector<char> here instead of char array
char* buf = 0;
buf = new char[size];
int retsize = recvfrom(sock, buf, size, 0, (sockaddr*) &SenderAddr, &SenderAddrSize);
if(retsize == -1)
{
cout << "\nRecv Error : " << WSAGetLastError();
if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == 0)
{
return "";
}
return "\0";
}
else if (retsize < size)
{
buf[retsize] = NULL;
}
string str(buf);
delete[] buf;
return str;
}
// On the client side, prepare dest like this:
// sockaddr_in dest;
// dest.sin_family = AF_INET;
// dest.sin_addr.s_addr = inet_addr(ip.c_str());
// dest.sin_port = htons(port);
int sendudp(string str, sockaddr_in dest, int sock)
{
int ret = sendto(sock,str.c_str(),str.size(),0, (sockaddr*)&dest,sizeof(dest));
if (ret == -1)
{
cout << "\nSend Error Code : " << WSAGetLastError();
}
return ret;
}
Server: main.cpp:
#include <winsock2.h>
#include <string.h>
#include <iostream>
#include "..\Shared\Include\shared.h"
// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
#define SERVER_PORT 27015
#define MAX_MSG 1024
using namespace std;
int main(int argc, char *argv[])
{
WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if(nResult != NO_ERROR)
{
cout << "WSAStartup failed with error: " << nResult << endl;
return 1;
}
sock = udpsock(SERVER_PORT, "127.0.0.1");
cout << "Waiting for datagram on port: " << SERVER_PORT << endl;
while(1)
{
sockaddr_in clientAddr;
// receive message
int clientAddrLen = sizeof(clientAddr);
cout << "Received message from the client: " << recvudp(sock, MAX_MSG, clientAddr, clientAddrLen) << endl;
sendudp("Hello from server!", clientAddr, sock);
}
WSACleanup();
return 0;
}
Client: main.cpp:
#include <winsock2.h>
#include <iostream>
#include "..\Shared\Include\shared.h"
using namespace std;
#define MAX_MSG 1024
// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
int main(int argc, char* argv[])
{
WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (nResult != NO_ERROR)
{
cout << "WSAStartup failed with error: " << nResult << endl;
return 1;
}
SOCKET sock = INVALID_SOCKET;
// Create a socket for sending data - it does not need to be binded like listening socket!
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sock == INVALID_SOCKET)
{
cout << socket failed with error: " << WSAGetLastError() << endl;
WSACleanup();
return 1;
}
unsigned short Port = 27015;
sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
dest.sin_port = htons(Port);
sendudp("Hello from client!", dest, sock);
sockaddr_in RecvAddr;
int recvaddrlen = sizeof(RecvAddr);
cout << "Received message from the server: " << recvudp(sock, MAX_MSG, RecvAddr, recvaddrlen) << endl;
cout << "Closing socket..." << endl;
nResult = closesocket(sock);
if(nResult == SOCKET_ERROR)
{
cout << "closesocket failed with error: " << WSAGetLastError() << endl;
WSACleanup();
return 1;
}
WSACleanup();
return 0;
}
If you run client twice output is:
Server:
Waiting for datagram on port: 27015
Received message from the client: Hello from client!
Received message from the client: Hello from client!
Client:
Received message from the server: Hello from server!
Closing socket...
UDP is connectionless protocol, server just needs to start listening on UDP port and client can send data (datagram) immediately, there is no need for connection establishment (e.g. with connect()/accept(), like in TCP).