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).
Related
I am trying to establish two sockets that are both able to multicast transmit. And the should both be members of each others multicast group.
I have two problems.
I have tried to disable the loopback, so that a transmitter does not receive its own messages. This does not seem to work.
The two transmitters should be the same, but only one of them receives the message sent on their multicast group.
The code looks like this:
#define ERROR_CODE -1
int init(const char *rx, const char *tx, uint16_t port, struct sockaddr_in& txaddress)
{
int status = 0;
int enable = 1;
int disable = 0;
int mainSocket = ::socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, 0);
if (mainSocket == ERROR_CODE)
std::cout << "Error creating socket." << std::endl;
if (::setsockopt(mainSocket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) == ERROR_CODE)
std::cout << "System failed to set reuse address." << std::endl;
if (::setsockopt(mainSocket, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable)) == ERROR_CODE)
std::cout << "System failed to set reuse port." << std::endl;
status = setsockopt(mainSocket, IPPROTO_IP, IP_MULTICAST_LOOP, &disable, sizeof(disable));
if (status == ERROR_CODE)
std::cout << "Unable to disable multicast loopback." << std::endl;
struct sockaddr_in rxaddress = address(INADDR_ANY, port);
txaddress = address(inet_addr(tx), port);
status = ::bind(mainSocket, reinterpret_cast<sockaddr*>(&rxaddress), sizeof(sockaddr_in));
if (status == ERROR_CODE)
std::cout << "Error binding socket." << std::endl;
struct in_addr localInterface;
memset(&localInterface, 0, sizeof(localInterface));
localInterface.s_addr = inet_addr("127.0.0.1");
status = setsockopt(mainSocket, IPPROTO_IP, IP_MULTICAST_IF, (char*) &localInterface, sizeof(localInterface));
if (status == ERROR_CODE)
std::cout << "Unable to set interface to multicast." << std::endl;
struct ip_mreq group;
memset(&group, 0, sizeof(group));
group.imr_multiaddr.s_addr = inet_addr(rx);
group.imr_interface.s_addr = INADDR_ANY;
status = setsockopt(mainSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &group, sizeof(group));
if (status == ERROR_CODE)
std::cout << "Unable to add to multicast group." << std::endl;
return mainSocket;
}
Where
struct sockaddr_in address(uint32_t ip, uint16_t port)
{
struct sockaddr_in address;
memset(&address, 0, sizeof(sockaddr_in));
address.sin_family = AF_INET;
if (ip == INADDR_BROADCAST)
address.sin_addr.s_addr = htonl(INADDR_BROADCAST);
else
if (ip == INADDR_ANY)
address.sin_addr.s_addr = htonl(INADDR_ANY);
else
address.sin_addr.s_addr = ip;
address.sin_port = htons(port);
return address;
}
And the testcode is
#include <iostream>
#include <arpa/inet.h>
#include <cstring>
#include <thread>
#define MULTICAST_SERVER_ADDRESS "224.0.0.1"
#define MULTICAST_CLIENT_ADDRESS "224.0.0.0"
int main (int argc, char** argv)
{
char message1[16] = "server says hi.";
char message2[16] = "client says hi.";
char buffer1[100] = {0};
char buffer2[100] = {0};
socklen_t length = sizeof(sockaddr_in);
struct sockaddr_in servertxaddress;
struct sockaddr_in clienttxaddress;
memset(&servertxaddress, 0, length);
memset(&clienttxaddress, 0, length);
int serversocket = init(MULTICAST_CLIENT_ADDRESS, MULTICAST_SERVER_ADDRESS, 2000, servertxaddress);
int clientsocket = init(MULTICAST_SERVER_ADDRESS, MULTICAST_CLIENT_ADDRESS, 2000, clienttxaddress);
sendto(serversocket, message1, 16, 0, (const sockaddr*) &servertxaddress, length);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
recvfrom(clientsocket, buffer1, 16, 0, nullptr, nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << buffer1 << std::endl;
sendto(clientsocket, message2, 16, 0, (const sockaddr*) &clienttxaddress, length);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
recvfrom(serversocket, buffer2, 160, 0, nullptr, nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << buffer2 << std::endl;
return 0;
}
When I run the program, both sockets receive the multicast message (message1), so the IP_MULTICAST_LOOP I tried does not work, as I would expect it to. And the last transmit seems to get transmitted, but it does not arrive in the other end.
So the output is:
server says hi.
server says hi.
And I would like it to be:
server says hi.
client says hi.
Please advise.
Simple code first:
#include "NetFunc.h"
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
using namespace std;
void test()
{
// SOCKET listenfd = OpenListenFD("127.0.0.1", "19287");
SOCKET connfd = OpenClientFD("127.0.0.1", "12389", "127.0.0.1", "19287");
send(connfd, "test", 5, 0);
close(connfd);
using namespace std::chrono_literals;
this_thread::sleep_for(100ms);
connfd = OpenClientFD("127.0.0.1", "12389", "127.0.0.1", "19287");
send(connfd, "test2", 6, 0);
close(connfd);
// close(listenfd);
cout << "over" << endl;
return;
}
int main()
{
SOCKET listenfd = OpenListenFD("127.0.0.1", "12389");
sockaddr_storage clientaddr;
socklen_t clientlen = sizeof clientaddr;
// SOCKET connfd = 0;
const int maxNameLen = 0x800;
char* buffer = new char[0x800 + 1];
char* hostname = new char[maxNameLen + 1], * port = new char[maxNameLen + 1];
std::thread newThread{&test};
newThread.detach();
SOCKET connfd;
while (true)
{
if ((connfd = accept(listenfd, (sockaddr*)&clientaddr, &clientlen)) < 0)
{
std::cerr << "Not accept correctly at InteractWithClients" << std::endl;
}
else{
getnameinfo((sockaddr*)&clientaddr, clientlen, hostname, maxNameLen, port, maxNameLen, NI_NUMERICHOST);
std::string completeAddr = string(hostname) + ":" + string(port);
std::cout << "Addr : " << completeAddr << std::endl;
recv(connfd, buffer, 0x800, 0);
std::cout << buffer << std::endl;
}
close(connfd);
}
delete[] buffer; delete[] hostname; delete[]port;
return 0;
}
Here, the NetFunc.h has necessary headers and #define SOCKET int. It corresponds with NetFunc.cpp, which has two functions for server&client as below:
(Note that I'm trying to generate a client with a specific port, for I just need two address-port to connect rather than to distinguish a server from a client. Besides, I already use SO_REUSEADDR.)
#include "NetFunc.h"
#include <iostream>
#include <string.h>
SOCKET OpenListenFD(const char* addr, const char* port)
{
struct addrinfo hints, * listp, * p;
SOCKET listenfd;
int optval = 1;
/* Get a list of potential server addresses */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM; /* Accept connections */
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* ... on any IP address */
hints.ai_flags |= AI_NUMERICSERV; /* ... using port number */
hints.ai_family = AF_INET;
getaddrinfo(addr, port, &hints, &listp);
/* Walk the list for one that we can bind to */
for (p = listp; p; p = p->ai_next) {
/* Create a socket descriptor */
if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
continue; /* Socket failed, try the next */
/* Eliminates "Address already in use" error from bind */
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, //line:netp:csapp:setsockopt
(setsockopt_ptr)&optval, sizeof(int));
/* Bind the descriptor to the address */
if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
{
break; /* Success */
}
closesocket(listenfd); /* Bind failed, try the next */
}
/* Clean up */
freeaddrinfo(listp);
if (!p) /* No address worked */
return -1;
/* Make it a listening socket ready to accept connection requests */
if (listen(listenfd, LISTENQ) < 0) {
closesocket(listenfd);
return -1;
}
return listenfd;
}
SOCKET OpenClientFD(const char* serverAddr, const char* port, const char* localAddr, const char* localPort)
{
struct sockaddr_in my_addr, my_addr1;
SOCKET client = socket(AF_INET, SOCK_STREAM, 0);
if (client < 0)
std::cerr<<"Error in client creating\n";
else
std::cout << "Client Created\n";
int optval = 1;
setsockopt(client, SOL_SOCKET, SO_REUSEADDR, //line:netp:csapp:setsockopt
(setsockopt_ptr)&optval, sizeof(int));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY;
my_addr.sin_port = htons(atoi(port));
// This ip address will change according to the machine
inet_pton(AF_INET, serverAddr, &my_addr.sin_addr.s_addr);
// Explicitly assigning port number 12010 by
// binding client with that port
my_addr1.sin_family = AF_INET;
my_addr1.sin_addr.s_addr = INADDR_ANY;
my_addr1.sin_port = htons(atoi(localPort));
// This ip address will change according to the machine
inet_pton(AF_INET, localAddr, &my_addr1.sin_addr.s_addr);
if (bind(client, (struct sockaddr*)&my_addr1, sizeof(struct sockaddr_in)) == 0)
std::cout << "Binded Correctly\n";
else
{
std::cerr << "Unable to bind\n";
return -1;
}
socklen_t addr_size = sizeof my_addr;
int con = 0;
do { con = connect(client, (struct sockaddr*)&my_addr, sizeof my_addr); } while (con != 0);
if (con == 0)
std::cout << "Client Connected\n";
else
std::cerr << "Error in Connection\n";
return client;
}
My OS is Ubuntu20.04 and such code will work with normal output. However, when I remove comments in test() for listenfd, it will output Unable to bind infinitely.
I'm really a newbie in socket and network. I have no idea why that will happen. Also, is there a way for an address-port to be both server and client at the same time? (Maybe to be exact, can an address-port both connect actively & accept others' connection passively?) Thank you!
my_addr1.sin_family = AF_INET;
my_addr1.sin_addr.s_addr = INADDR_ANY;
my_addr1.sin_port = htons(atoi(localPort));
// This ip address will change according to the machine
inet_pton(AF_INET, localAddr, &my_addr1.sin_addr.s_addr);
if (bind(client, (struct sockaddr*)&my_addr1, sizeof(struct sockaddr_in)) == 0)
std::cout << "Binded Correctly\n";
This code binds a listening socket to port 19287, localPort is 19287.
// SOCKET listenfd = OpenListenFD("127.0.0.1", "19287");
And so does the commented-out code. Un-commenting it out results in two sockets attempting to listen on the same port. Surprise, surprise: this doesn't work.
The fact that the first socket that binds this port uses SO_REUSEADDR is irrelevant, and makes no difference. With or without SO_REUSEADDR, no two sockets can listen on the same port.
SO_REUSEADDR does not allow you to have multiple sockets listening on the same port, at the same time. All that SO_REUSEADDR does is allow a listening socket to be bound on a port that's temporarily "take out of commission", in specific edge cases, until a prescribed timeout expires. It allows a port to be bound as long as nothing else is listening on it, even if it's sitting in a "timeout room".
But it still won't allow you to have two different sockets listening on the same port, at the same time.
The project I am working on uses TCP and UDP to create a file transport protocol. The TCP connection generates a random port in which it returns to the client and the client then connects to the server on that port number using UDP. UDP is then used to transfer a text file four characters a time to the server and the server will send back the characters capitalized in which will then be displayed on the client. The issue is that the client is hanging up when waiting for the server to send back the capitalized version of the characters. I will leave the code below. The part of the code that is not working is commented out towards the end of the client and server files. Any help is appreciated!
Client Code
// Libraries
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <unistd.h>
using namespace std;
int main(int argc, char* argv[]) {
// Variables
int port = strtol(argv[2], NULL, 10);
string file = argv[3];
int r_port;
FILE* fp;
string fileString;
string dataBuffer;
int charCounter = 0;
char c;
// *** Declare TCP socket ***
int tcpsocket = 0;
tcpsocket = socket(AF_INET, SOCK_STREAM, 0);
if (tcpsocket == -1) {
cerr << "Can't create a socket";
return 1;
}
// Get host IP address
struct hostent *s;
s = gethostbyname(argv[1]);
// Setting destination info
struct sockaddr_in server;
memset((char *) &server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
bcopy((char *) s->h_addr, (char *) &server.sin_addr.s_addr, s->h_length);
// Connect to server
int connectRes = connect(tcpsocket, (sockaddr*)&server, sizeof(server));
if (connectRes == -1) {
cerr << "Can't connect to socket";
return 1;
}
// Sending data to server
char payload[512] = "117";
int sendRes = send(tcpsocket, payload, 512, 0);
if (sendRes == -1) {
cerr << "Could not send to server";
return 1;
}
// Receive r_port from server
memset(payload, 0, sizeof(payload));
recv(tcpsocket, payload, 512, 0);
r_port = strtol(payload, NULL, 10);
close(tcpsocket);
// *** Declare UDP socket ***
int udpsocket = 0;
udpsocket = socket(AF_INET, SOCK_DGRAM, 0);
if (udpsocket == -1) {
cerr << "Can't create a socket";
return 1;
}
// Get host IP address
s = gethostbyname(argv[1]);
// Setting destination info
memset((char *) &server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(r_port);
bcopy((char *) s->h_addr, (char *) &server.sin_addr.s_addr, s->h_length);
// File manipulation
fp = fopen(file.c_str(), "r");
while (c != EOF) {
c = fgetc(fp);
fileString += c;
charCounter++;
}
fclose(fp);
// UDP file transfer
for (int i = 4; i < 8; i++) {
dataBuffer += fileString[i];
}
socklen_t slen = sizeof(server);
memset(payload, 0, sizeof(payload));
strcpy(payload, dataBuffer.c_str());
sendRes = sendto(udpsocket, payload, 32, 0, (struct sockaddr *) &server, slen);
if (sendRes == -1) {
cerr << "Could not send to server";
return 1;
}
// Receive ack
// slen = sizeof(server);
// memset(payload, 0, sizeof(payload));
// recvfrom(udpsocket, payload, 32, 0, (sockaddr*)&server, &slen);
// cout << "Capitalized data: " << payload;
close(udpsocket);
return 0;
}
Server Code
// Libraries
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <cstdlib>
#include <time.h>
#include <ctype.h>
using namespace std;
// Variables
int r_port;
string fileData;
// Generate Random Port
int randomPort() {
srand(time(NULL));
return rand() % ((65535 - 1024) + 1) + 1024;
}
// Capitalization Function
string capitalize(char* payload) {
int i = 0;
char c;
string charArr;
while(payload[i]) {
c = payload[i];
charArr += toupper(c);
i++;
}
return charArr;
}
int main(int argc, char* argv[]) {
// Variables
int port = strtol(argv[1], NULL, 10);
// *** Declare TCP socket ***
int tcpsocket = 0;
tcpsocket = socket(AF_INET, SOCK_STREAM, 0);
if (tcpsocket == -1) {
cerr << "Can't create a socket";
return -1;
}
// Receive data
struct sockaddr_in server;
memset((char *) &server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(tcpsocket, (struct sockaddr *) &server, sizeof(server)) == -1) {
cerr << "Can't bind to IP/Port";
return -2;
}
if (listen(tcpsocket, SOMAXCONN) == -1) {
cerr << "Can't listen";
return -3;
}
struct sockaddr_in client;
char payload[512];
socklen_t clen = sizeof(client);
int clientSocket = accept(tcpsocket, (sockaddr*)&client, &clen);
if (clientSocket == -1) {
cerr << "Problem with client connecting";
return -4;
}
recv(clientSocket, payload, 512, 0);
// Check client data
if (strtol(payload,NULL,10) == 117) {
r_port = randomPort();
cout << "Handshake detected. Selected the random port " << r_port << "\n";
}
else {
cout << "Error occurred\n";
}
// Return random port
memset(payload, 0, sizeof(payload));
sprintf(payload,"%ld",r_port);
int sendRes = send(clientSocket, payload, 512, 0);
if (sendRes == -1) {
cerr << "Could not send to server\n";
return 1;
}
close(clientSocket);
close(tcpsocket);
// *** Declare UDP socket ***
int udpsocket = 0;
udpsocket = socket(AF_INET, SOCK_DGRAM, 0);
if (udpsocket == -1) {
cerr << "Can't create a socket";
return -1;
}
// Receive data
memset((char *) &server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(r_port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(udpsocket, (struct sockaddr *) &server, sizeof(server)) == -1) {
cerr << "Can't bind to IP/Port";
return -2;
}
clen = sizeof(client);
memset(payload, 0, sizeof(payload));
recvfrom(udpsocket, payload, 32, 0, (sockaddr*)&client, &clen);
fileData = capitalize(payload);
cout << "Payload: " << fileData << "\n";
// Send ack
// socklen_t slen = sizeof(server);
// memset(payload, 0, sizeof(payload));
// strcpy(payload, fileData.c_str());
// sendRes = sendto(udpsocket, payload, 32, 0, (struct sockaddr *) &server, slen);
// if (sendRes == -1) {
// cerr << "Could not send to server";
// return 1;
// }
close(udpsocket);
return 0;
}
ssize_t sendto(**int sockfd**, const void *buf, size_t len, int flags,
const struct **sockaddr *dest_addr**, socklen_t addrlen);
Reference : https://linux.die.net/man/2/sendto
Your destination address should be : (sockaddr *) &client
I made a small socket echo server with a blocking socket (see code below), but the select statement always returns 0 even when there is a message to be read. Everything else works. If you replace the select statement by simple assigning 1 to selectResult, the server works.
The server runs on Ubuntu in a VM, while the client is on the Host system (Windows 7 professional). My IDE for the server is Eclipse 3.8 and it uses OpenSSL 1.0.1j.
To get this code to work, you only need to include OpenSSL's root directory, add its library path to the linker and link to ssl, crypto and dl (in that order). Also you need a certificate and private key.
Thanks in advance!
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <iostream>
using namespace std;
#define MAX_BUFFER 1024
int main()
{
// Initializing...
SSL_CTX*_ctx = NULL;
SSL* _ssl = NULL;
fd_set _fdSet;
int _serverSocket = 0;
int _port = 9090;
timeval t;
const char* certPath = "/home/alex/Certificate/cacert.pem";
const char* pKeyPath = "/home/alex/Certificate/privkey.pem";
// Init OpenSSL
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
_ctx = SSL_CTX_new(TLSv1_1_server_method());
if (_ctx == NULL)
{
ERR_print_errors_fp(stderr);
abort();
}
// Set certificate and private key.
if (SSL_CTX_use_certificate_file(_ctx, certPath, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
abort();
}
if (SSL_CTX_use_PrivateKey_file(_ctx, pKeyPath, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
abort();
}
if (!SSL_CTX_check_private_key(_ctx))
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
// Initialize server socket:
// 1. set address
struct sockaddr_in addr;
int optval = 1;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(_port);
addr.sin_addr.s_addr = INADDR_ANY;
// 2. init socket, set socket options, bind it to address
_serverSocket = socket(PF_INET, SOCK_STREAM, 0);
setsockopt(_serverSocket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
if (bind(_serverSocket, (struct sockaddr*) &addr, sizeof(addr)) != 0)
{
perror("can't bind port");
abort();
}
// 3. Prepare the socket to accept connections
if (listen(_serverSocket, 1) != 0)
{
perror("Can't configure listening port");
abort();
}
cout << "Server finished initializing." << endl;
bool bServerStayAlive = true;
while (bServerStayAlive)
{
cout << "Waiting for connection..." << endl;
struct sockaddr_in addr;
unsigned int len = sizeof(addr);
int client = accept(_serverSocket, (struct sockaddr*) &addr, &len);
printf("Connection: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
_ssl = SSL_new(_ctx);
SSL_set_fd(_ssl, client);
if (SSL_accept(_ssl) == -1) /* do SSL-protocol accept */
ERR_print_errors_fp(stderr);
else
{
while (bServerStayAlive)
{
FD_ZERO(&_fdSet);
FD_SET(_serverSocket, &_fdSet);
t.tv_sec = 1;
t.tv_usec = 0;
int selectResult = select(_serverSocket + 1, &_fdSet, NULL, NULL, &t);
if (selectResult == 0)
{
cout << "timeout" << endl;
continue;
}
if (selectResult < 0)
{
cout << "Select error: " << selectResult << endl;
bServerStayAlive = false;
break;
}
cout << "Going to read something\n";
unsigned char buffer[MAX_BUFFER];
memset(buffer, 0, MAX_BUFFER);
int bytes = SSL_read(_ssl, buffer, MAX_BUFFER); /* get request */
if (bytes > 0)
{
cout << "Received message: " << endl;
for (int i = 0; i < bytes; i++)
cout << buffer[i];
cout << endl;
SSL_write(_ssl, buffer, bytes);
}
else
{
ERR_print_errors_fp(stderr);
break;
}
}
}
int sd = SSL_get_fd(_ssl); /* get socket connection */
SSL_free(_ssl); /* release SSL state */
close(sd); /* close connection */
cout << "Connection was closed.\n";
}
// Uninitializing
close(_serverSocket);
SSL_CTX_free(_ctx);
return 0;
}
I think you meant to select on the client socket that you just accepted, not the _serverSocket that you're accepting connections on.
Yesterday I started writing a code for server-client TCP/IP for chating.
It's code for server:
#include <iostream>
#include <winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
int main() {
WSAData wsa;
WORD Version = MAKEWORD(2, 1);
WSAStartup(Version, &wsa);
SOCKET Listen = socket(AF_INET, SOCK_STREAM, 0);
SOCKET Connect = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN Server;
Server.sin_addr.s_addr = inet_addr("127.0.0.1");
Server.sin_family = AF_INET;
Server.sin_port = htons(100);
bind(Listen, (SOCKADDR*)&Server, sizeof(Server));
listen(Listen, 1);
int size = sizeof(Server);
char buffer[512];
char *fn = "";
int iResult;
std::cout <<"Listening...";
if(Connect = accept(Listen, (SOCKADDR*)&Server, &size)) {
std::cout << "\nConnecting was reached :)";
}
do {
char *fn;
iResult = recv(Connect, buffer, sizeof(buffer), 0);
if (iResult > 0) {
buffer[iResult] = '\0';
fn = buffer;
std::cout << fn;
} else if (iResult == 0){
printf("Connection closing...\n");
iResult = 0;
}
} while (iResult > 0);
WSACleanup();
std::cin.get();
return 0;
}
And it's for client:
#include <iostream>
#include <winsock2.h>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
int main() {
WSAData wsa;
WORD Version = MAKEWORD(2, 1);
WSAStartup(Version, &wsa);
int iResult;
std::string text;
char buffer[512];
SOCKADDR_IN Client;
Client.sin_addr.s_addr = inet_addr("127.0.0.1");
Client.sin_family = AF_INET;
Client.sin_port = htons(100);
SOCKET Connect = socket(AF_INET, SOCK_STREAM, 0);
std::cout << "Wcisnij enter aby polaczyc";
std::cin.get();
if(connect(Connect, (SOCKADDR*)&Client, sizeof(Client))) {
std::cout << "Nawiazano polaczeniee";
}
do {
std::cout << "Waxer: ";
std::cin >> text;
text.insert( 0, "Waxer: " );
strcpy_s(buffer, text.c_str());
// Send an initial buffer
iResult = send(Connect, buffer, strlen(buffer), 0);
if (iResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(Connect);
WSACleanup();
return 1;
}
} while(text != "ban");
std::cout<< "Polaczenie wyslane!";
std::cin.get();
return 0;
}
My question is? What should I change in this code for multiple clients. I mean how to connect new clients?
Your question is quite open, but in general you need threads.
Pseudocode:
int main()
{
std::vector<std::thread> clients;
Socket listener("localhost", "1337");
for (;;)
{
Socket tmp = listener.accept();
clients.push_back(std::thread(&myHandlerFunction, tmp));
clients.back().detach();
}
}
In the end you have a thread for every connected client and can codify your chat logic so that they can communicate with each other.