I have written code in C++ with Winsock. It converts a video to mjpeg stream and sends it over TCP using Winsock in windows. Now I am able to see the video in any browser with the link.
But the problem is that anyone with the link can see it. I want to prompt the user to enter username and password to access the feed, whenever he types the IP address.
Here is my code:
#include <winsock.h>
#include <windows.h>
#include <time.h>
#define PORT unsigned long
#define ADDRPOINTER int*
struct _INIT_W32DATA
{
WSADATA w;
_INIT_W32DATA() { WSAStartup( MAKEWORD( 2, 1 ), &w ); }
} _init_once;
#include <iostream>
using std::cerr;
using std::endl;
#include <iostream>
#pragma comment(lib, "wsock32.lib")
using namespace std;
class MJPGWriter
{
SOCKET sock;
fd_set master;
int timeout; // master sock timeout, shutdown after timeout millis.
int quality; // jpeg compression [1..100]
int _write( int sock, char *s, int len )
{
if ( len < 1 ) { len = strlen(s); }
return ::send( sock, s, len, 0 );
}
public:
MJPGWriter(int port = 0)
: sock(INVALID_SOCKET)
, timeout(20000)
, quality(30)
{
FD_ZERO( &master );
if (port)
open(port);
}
~MJPGWriter()
{
release();
}
bool release()
{
if ( sock != INVALID_SOCKET )
::shutdown( sock, 2 );
sock = (INVALID_SOCKET);
return false;
}
bool open( int port )
{
sock = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP) ;
SOCKADDR_IN address;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_family = AF_INET;
address.sin_port = ::htons(port);
if ( ::bind( sock, (SOCKADDR*) &address, sizeof(SOCKADDR_IN) ) == SOCKET_ERROR )
{
cerr << "error : couldn't bind sock "<<sock<<" to port "<<port<<"!" << endl;
return release();
}
if ( ::listen( sock, 10 ) == SOCKET_ERROR )
{
cerr << "error : couldn't listen on sock "<<sock<<" on port "<<port<<" !" << endl;
return release();
}
FD_SET( sock, &master );
return true;
}
bool isOpened()
{
return sock != INVALID_SOCKET;
}
bool write(const cv::Mat & frame)
{
fd_set rread = master;
struct timeval to = {0,timeout};
SOCKET maxfd = sock+1;
if ( ::select( maxfd, &rread, NULL, NULL, &to ) <= 0 )
return true; // nothing broken, there's just noone listening
std::vector<uchar>outbuf;
std::vector<int> params;
params.push_back(cv::IMWRITE_JPEG_QUALITY);
params.push_back(quality);
cv::imencode(".jpg", frame, outbuf, params);
int outlen = outbuf.size();
#ifdef _WIN32
for ( unsigned i=0; i<rread.fd_count; i++ )
{
SOCKET s = rread.fd_array[i]; // fd_set on win is an array, while ...
#else
for ( int s=0; s<maxfd; s++ )
{
if ( ! FD_ISSET(s,&rread) ) // ... on linux it's a bitmask ;)
continue;
#endif
if ( s == sock ) // request on master socket, accept and send main header.
{
int addrlen = sizeof(SOCKADDR);
SOCKADDR_IN address = {0};
SOCKET client = ::accept( sock, (SOCKADDR*)&address, &addrlen );
if ( client == SOCKET_ERROR )
{
cerr << "error : couldn't accept connection on sock " << sock<< " !" << endl;
return false;
}
maxfd=(maxfd>client?maxfd:client);
FD_SET( client, &master );
_write( client,"HTTP/1.0 200 OK\r\n",0);
_write( client,
"Server: Mozarella/2.2\r\n"
"Accept-Range: bytes\r\n"
"Connection: close\r\n"
"Max-Age: 0\r\n"
"Expires: 0\r\n"
"Cache-Control: no-cache, private\r\n"
"Pragma: no-cache\r\n"
"Content-Type: multipart/x-mixed-replace; boundary=mjpegstream\r\n"
"\r\n",0);
cerr << "new client " << client << endl;
}
else // existing client, just stream pix
{
char head[400];
sprintf(head,"--mjpegstream\r\nContent-Type: image/jpeg\r\nContent-Length: %lu\r\n\r\n",outlen);
_write(s,head,0);
int n = _write(s,(char*)(&outbuf[0]),outlen);
//cerr << "known client " << s << " " << n << endl;
if ( n < outlen )
{
cerr << "kill client " << s << endl;
::shutdown(s,2);
FD_CLR(s,&master);
}
}
}
return true;
}
};
I am using it by sending image frame and port number to writer.
Related
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.
The system I have to do has one tcp server and about 1000 tcp clients.
1000 clients will send data to tcp server every second.
To simulate this situation, At first I connected to tcp server with 50 sockets from a single pc with below code.
int main() {
const char *hello = "Hello from client";
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
serv_addr.sin_addr.s_addr = inet_addr("192.168.1.39");
vector<int> vec;
for ( uint8_t i = 0; i < 50; i++ ) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if ( sock < 0 ) {
cout << "... Cant Allocated Socket\n";
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
cout << "... Connection Failed \n";
return -1;
}
vec.push_back(sock);
}
for ( uint8_t i = 0; i < vec.size(); i++ ) {
send(vec[i], hello, strlen(hello), 0);
cout << "Message Send\n";
}
for ( uint8_t i = 0; i < vec.size(); i++ ) {
shutdown(vec[i], 0);
close(vec[i]);
}
return 0;
}
After the tcp clients connect to the tcp server, they send the data to the tcp server and close the socket. I can see from terminal that tcp clients can send packet without waiting(less than 10ms)
The above tcp client code can work successfully and send the data to tcp server successfully.
I show the data from the tcp client with the tcp server code below.
#define _DEF_TCP_SERVER_PORT 8080
#define _DEF_TCP_SERVER_MAX_QUEUE_LISTEN 12
bool finish_app = false;
struct TcpClient {
int clientSocket;
struct in_addr clientAddr;
};
vector<TcpClient> TcpClients;
struct _ServiceTcpServer {
bool enable;
int sock;
uint16_t connectedClient;
uint32_t sockLen;
sockaddr_in tcpServerAddr;
sockaddr_in remoteAddr;
};
struct _ServiceTcpServer _serviceTcpServer;
void init_tcp_server_socket() {
_serviceTcpServer.tcpServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
_serviceTcpServer.tcpServerAddr.sin_family = AF_INET;
_serviceTcpServer.tcpServerAddr.sin_port = htons(_DEF_TCP_SERVER_PORT);
_serviceTcpServer.sockLen = sizeof(_serviceTcpServer.remoteAddr);
int flag = 1;
for ( ;; ) {
_serviceTcpServer.sock = socket(AF_INET, SOCK_STREAM, 0);
if ( _serviceTcpServer.sock < 0 ) {
cout << "... Failed to allocate socket.\n";
this_thread::sleep_for(chrono::seconds(1));
continue;
}
if ( setsockopt(_serviceTcpServer.sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) ) {
cout << "... Set SockOpt failed.\n";
close(_serviceTcpServer.sock);
this_thread::sleep_for(chrono::seconds(1));
continue;
}
if( bind(_serviceTcpServer.sock, (sockaddr *) &_serviceTcpServer.tcpServerAddr, sizeof(_serviceTcpServer.tcpServerAddr)) == -1 ) {
cout << "... Socket bind failed.\n";
close(_serviceTcpServer.sock);
this_thread::sleep_for(chrono::seconds(1));
continue;
}
if ( listen(_serviceTcpServer.sock, _DEF_TCP_SERVER_MAX_QUEUE_LISTEN) != 0 ) {
cout << "... Socket listen failed.\n";
close(_serviceTcpServer.sock);
this_thread::sleep_for(chrono::seconds(1));
continue;
}
break;
}
cout << "Socket init done \n";
}
void tcp_user_accept_task() {
while ( finish_app == false ) {
int temp_sck = -1;
temp_sck = accept(_serviceTcpServer.sock, (sockaddr *) &_serviceTcpServer.remoteAddr, &_serviceTcpServer.sockLen);
if ( temp_sck == -1 ) {
this_thread::sleep_for(chrono::seconds(2));
continue;
}
TcpClient tcpClient;
tcpClient.clientAddr = _serviceTcpServer.remoteAddr.sin_addr;
tcpClient.clientSocket = temp_sck;
TcpClients.push_back( tcpClient );
cout << "... New connection request: " << temp_sck << endl;
++_serviceTcpServer.connectedClient;
this_thread::sleep_for(chrono::milliseconds(50));
}
}
uint8_t temp_recv[100];
void tcp_server_run() {
while ( finish_app == false ) {
for(uint16_t i = 0 ; i < _serviceTcpServer.connectedClient; i++ ) {
int temp_cs = TcpClients[i].clientSocket;
fcntl(temp_cs, F_SETFL, O_NONBLOCK);
int temp_recvLen = recv(temp_cs, temp_recv, 20, 0);
if( temp_recvLen > 0 ) {
time_t _time = chrono::system_clock::to_time_t(chrono::system_clock::now());
cout << "Message Received At:" << ctime(&_time) << " :";
cout << temp_recv << endl;
break;
} else {
this_thread::sleep_for(chrono::milliseconds(10));
}
}
if ( temp_recv[0] == 'q' ) {
finish_app = true;
}
}
close(_serviceTcpServer.sock);
}
int main() {
thread init_thread(init_tcp_server_socket);
init_thread.join();
thread accept_thread(tcp_user_accept_task);
thread run_thread(tcp_server_run);
accept_thread.join();
run_thread.join();
return 0;
}
But the problem is about 3-4 packets received in only 1 second as in the screen image.
Note:
When the code this_thread::sleep_for(chrono::milliseconds(10)); commented, the problem was solved. But since the processor is not sleep, the processor is working at 100%.
When the client is accepted, I added 10 us timeout to client recv with the code below and comment and fcntl(temp_cs, F_SETFL, O_NONBLOCK);
struct timeval _timeval;
_timeval.tv_sec = 0;
_timeval.tv_usec = 10;
setsockopt(tcpClient.clientSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*) &_timeval, sizeof(_timeval));
The problem continues as in "this_thread::sleep_for".
You should receive the socket simultaneously rather than querying every socket and sleeping for 10ms each time data is not yet ready.
The proper way to do it depends on the platform
posix - select
linux - poll, epoll, io_submit
windows - I/O Completion Ports
Usually, select which is a posix standard, will be sufficient for your needs.
If you want multiplatform you might also want to explorer 3rd party libraries such as libevent and libev which already wraps theses platform depent calls for you.
Happy Coding!
I am trying to implement a socket server by using epoll. I have 2 threads doing 2 tasks:
listening to incoming connection
writing on screen the data the client is sending.
For my test I have the client and the server on the same machine with 3 or 4 clients running.
The server works fine until I don't kill one of the client by issuing a CTRL-C: as soon I do that the server starts looping and printing at a very fast rate data from other client. The strange thing is that
the client sends data each 2 seconds but the rate of the server is higher
epoll_wait is also supposed to print something when one of the client disconnects as it is checking also for EPOLLHUP or EPOLLERR
epoll_wait should wait a bit before printing since I gave him a timeout of 3000 milliseconds.
Can you help? Could it be that I am passing in a wrong way the epoll descriptor to the other thread? I cannot understand since the code looks similar to many examples around.
Thanks a lot
Mn
// server.cpp
#include <iostream>
#include <cstdio>
#include <cstring>
extern "C" {
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <pthread.h>
}
#define MAX_BACKLOG 10
void* readerthread(void* args){
int epfd = *((int*)args);
epoll_event outwait[10];
while(true){
int retpw = epoll_wait( epfd, outwait,20, 3000 );
if( retpw == -1 ){
printf("epoll error %m\n");
}else if( retpw == 0 ){
printf("nothing is ready yet\n");
continue;
}else{
for( int i=0;i<retpw;i++){
if( outwait[i].events & EPOLLIN ){
int fd = outwait[i].data.fd;
char buf[64];
if( -1 == read(fd,buf,64) ){
printf("error reading %m\n");
}
printf("%s\n",buf);
}else{
std::cout << "other event" << std::endl;
}
}
}
}
}
int main(){
int epfd = epoll_create(10);
if( -1 == epfd ){
std::cerr << "error creating EPOLL server" << std::endl;
return -1;
}
pthread_t reader;
int rt = pthread_create( &reader, NULL, readerthread, (void*)&epfd );
if( -1 == rt ){
printf("thread creation %m\n");
return -1;
}
struct addrinfo addr;
memset(&addr,0,sizeof(addrinfo));
addr.ai_family = AF_INET;
addr.ai_socktype = SOCK_STREAM;
addr.ai_protocol = 0;
addr.ai_flags = AI_PASSIVE;
struct addrinfo * rp,* result;
getaddrinfo( "localhost","59000",&addr,&result );
for( rp = result; rp != NULL; rp = rp->ai_next ){
// we want to take the first ( it could be IP_V4
// or IP_V6 )
break;
}
int sd = socket( AF_INET, SOCK_STREAM, 0 );
if(-1==sd ){
std::cerr << "error creating the socket" << std::endl;
return -1;
}
// to avoid error 'Address already in Use'
int optval = 1;
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
if( -1==bind( sd, result->ai_addr, result->ai_addrlen ) ){
printf("%m\n");
std::cerr << "error binding" << std::endl;
return -1;
}
while(true){
std::cout << "listen" << std::endl;
if( -1== listen(sd, MAX_BACKLOG ) ){
std::cerr << "listen didn't work" << std::endl;
return -1;
}
std::cout << "accept" << std::endl;
sockaddr peer;
socklen_t addr_size;
int pfd = accept( sd, &peer ,&addr_size );
if( pfd == -1 ){
std::cerr << "error calling accept()" << std::endl;
return -1;
}
epoll_event ev;
ev.data.fd = pfd;
ev.events = EPOLLIN;
std::cout << "adding to epoll list" << std::endl;
if( -1 == epoll_ctl( epfd, EPOLL_CTL_ADD, pfd, &ev ) ){
printf("epoll_ctl error %m\n");
return -1;
}
}
}
// end of server.cpp
// client.cpp
#include <iostream>
#include <cstring>
#include <cstdio>
extern "C"{
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
}
int main(){
const char* servername = "localhost";
const char* serverport = "59000";
struct addrinfo server_address;
memset( &server_address, 0, sizeof(struct addrinfo) );
server_address.ai_family = AF_INET;
server_address.ai_socktype = SOCK_STREAM;
server_address.ai_protocol = 0; // any protocol
server_address.ai_flags = 0;
struct addrinfo * result, * rp;
int res = getaddrinfo( servername, serverport, &server_address, &result );
if( -1 == res ){
std::cout << "I cannot getaddress " << servername << std::endl;
return -1;
}
int fd = socket( server_address.ai_family
, server_address.ai_socktype
, server_address.ai_protocol );
if( -1 == fd ){
printf("I cannot open a socket %m\n");
return -1;
}
for( rp = result; rp != NULL; rp = rp->ai_next ){
std::cout << "************" << std::endl;
if( -1 == connect( fd, rp->ai_addr, rp->ai_addrlen ) ){
close(fd);
}else{
std::cout << "connected" << std::endl;
break;
}
}
if( rp == NULL ){
std::cerr << "I couldn't connect server " << servername << std::endl;
}
while(true){
sleep(2);
pid_t me = getpid();
char buf[64];
bzero( buf,sizeof(buf));
sprintf( buf,"%ld",me );
write(fd,buf,sizeof(buf));
printf("%s\n",buf);
}
}
// end of client.cpp
A client disconnection is signalled by an EOF condition on the file descriptor. The system considers EOF to be a state in which the file descriptor is 'readable'. But, of course, the EOF condition cannot be read. This is the source of your looping. epoll is acting like the file descriptor for the disconnected client is always readable. You can detect that you have an EOF condition by checking when read returns 0 bytes read.
The only way to deal with an EOF condition is to close the file descriptor in some way. Depending on exactly how the flow of things go, this could be with shutdown(sockfd, SHUT_RD), shutdown(sockfd, SHUT_RDWR) or close(sockfd);.
Unless you know that you need the shutdown(2) call for whatever reason, I would recommend you use close. Of course, you should remember to tell epoll that the file descriptor is no longer of interest before you close. I'm not sure what will happen if you don't, but one possibility is that epoll will error. Another is that epoll will mysteriously begin reporting events for a new file descriptor that has the same numeric value before you add it to the list epoll should care about.
Socket cleanly closed by the other side will become readable and read(2) will return 0, you have to check for that. As coded now - level-triggered poll - epoll_wait(2) returns every time without waiting telling that you still haven't read that end-of-stream.
Alternatively, you can switch to edge-triggered poll (EPOLLET) and react to EPOLLRDHUP too.
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).
I'm writing an app in C++ using openssl, and I can't seem to get the ssl socket connection to work.
I have an abstract class, with multiple functions implemented using various protocols by the inheriting classes and simple TCP and UDP ( posix sockets ) work fine.
I could not get the ssl working though and after some code browsing and tweaking I found out, that it is the fork() function which causes problems.
I wrote a very simple program to check things out, and this is what I get:
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <iostream>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/timeb.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <netdb.h>
#include <time.h>
#include </usr/local/include/ssl/ssl.h>
#include "SSL_CA.cpp"
int main( int argc, char **argv ) {
int retval;
SSL_CTX *ctx;
SSL *con;
int port = atoi(argv[1]);
sockaddr_in client_addr;
sockaddr_in serv_addr;
socklen_t client_len;
int max_clients = 40;
int serv_sock, client_sock;
if ( ( serv_sock = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
return -12;
SSL_library_init();
int SSL_CA = NowySSL();
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = port;
cout << "Conf " << endl;
if ( bind( serv_sock, ( sockaddr * ) &serv_addr, sizeof( serv_addr ) ) < 0 )
return -1;
if ( listen( serv_sock, max_clients ) < 0 )
return -1;
while (1) {
cout << "Waiting" << endl;
if ( ( client_sock = accept( serv_sock, ( sockaddr * ) &client_addr, &client_len ) ) < 0 )
return -1;
pid_t pid = fork();
if ( pid = 0 ) {
con = NULL;
ctx = NULL;
ctx = (SSL_CTX *)SSL_CTX_new( SSLv23_server_method() );
con = (SSL *)SSL_new( (SSL_CTX *)ctx );
if (
!( ( ( !SSL_CA ) && ( SSL_NO_CA_RUN( con, ctx, client_sock, 0 ) ) ) ||
( ( SSL_CA ) && ( SSL_CA_RUN( con, ctx, client_sock, true ) ) ) )
)
return -1;
char buf[10];
if ( ( retval = SSL_read( con, buf, 10 ) ) <= 0 )
return -16;
cout << "Got msg " << buf << " " << retval << endl;
sprintf(buf, "12345" );
if ( ( retval = SSL_write( con, buf, strlen(buf) ) ) <= 0 )
return -1;
cout << "Sent" << endl;
}
else cout << "Parent " << endl;
}
return 0;
}
These are the SSL_CA_RUN and SSL_NO_CA_RUN functions - not written by me, but i have to use it
int SSL_NO_CA_RUN (SSL *con, SSL_CTX *ctx, int msgsock, int exit_now)
{
// printf ("SSL NO CA\n");
// SSL_library_init();
// ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_server_method());
// con=(SSL *)SSL_new((SSL_CTX *)ctx);
if ((!SSL_set_fd (con,msgsock))||
(!SSL_use_RSAPrivateKey_file (con,"ktsa_u_linux.k",1))||
(!SSL_use_certificate_file (con,"ktsa_u_linux.c",1))||
(!SSL_accept (con)))
{
printf ("SSL ERROR (%s)\n",strerror(errno));
if (exit_now)
{
zamknij_polaczenie (con,ctx,msgsock);
exit(0);
}
return false;
}
// printf ("SSL NO CA OK!!!\n");
return true;
}
int SSL_CA_RUN (SSL *con, SSL_CTX *ctx, int msgsock, int exit_now)
{
int ret, ok=true;
X509 *peer;
FILE *fp;
char error[250];
// printf ("SSL CA\n");
//MARCIN (START)
//robimy linki do certyfikatow Marcin
certhash(CA_DIR,CRL_DIR,getpid());
//dostep_read (890);
// SSL_library_init();
// printf("\n");
// ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_server_method());
// con=(SSL *)SSL_new((SSL_CTX *)ctx);
cb=pem_passwd_cb;
SSL_CTX_set_default_passwd_cb_userdata(ctx,haslo);
SSL_CTX_set_default_passwd_cb(ctx,cb);
//SSL_set_verify_depth(con,10); default = 9
SSL_set_verify(con,SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,NULL);
// SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,NULL);
//SSL_CTX_set_cipher_list(ctx,"SHA1+3DES:+RSA:HIGH");
//SSL_set_cipher_list(con,"SHA1+3DES:+RSA:HIGH");
if ((!SSL_set_fd (con,msgsock))||
(!SSL_use_RSAPrivateKey_file (con,KEY_FILE,1))||
(!SSL_use_certificate_file (con,CERT_FILE,1))||
// (!SSL_CTX_load_verify_locations(ctx,CA_FILE,NULL))||
(!SSL_CTX_load_verify_locations(ctx,NULL,linkdir))||
(!SSL_accept (con))
)
{
if (exit_now)
{
remove_symdir(getpid());
// printf("error connect\n");
zamknij_polaczenie (con,ctx,msgsock);
exit(0);
} else remove_symdir(getpid());
ok=false;
}
if (ok)
{
peer=SSL_get_peer_certificate(con);
if(peer!=NULL)
{
//ret=1 - odwolany
//ret=0 - wszystko ok
//ret = inne - jakis inny blad, np. brak wlasciwej listy crl
//ret=check_peer_crl_dir(peer,error,CRL_DIR); //sprawdzenie odwolania certyfikatu przychodzacego
ret=check_peera(peer,error,0); //sprawdzenie odwolania certyfikatu przychodzacego
// printf("peer certyfikat:%s:%i\n",error,ret);
}
if ((peer==NULL)||(ret==1))
{
//nie akceptujemy polaczenia bo nie dostalismy certyfikatu lub certyfikat jest odwolany
// printf("polaczenie odrzucone - certyfikat peera odwolany, brak certyfikatu lub wystawiony przez inne CA \n");
if (exit_now)
{
remove_symdir(getpid());
zamknij_polaczenie (con,ctx,msgsock);
exit(2);
}
ok=false;
}
}
if (ok)
{
X509_free(peer); //potrzebne?
peer=NULL;
//mozna sprawdzic tez nasz certyfikat
if (!(fp = fopen (CERT_FILE, "r"))) //"/router/ssl/cert/sslcert.cer"
printf ("Error reading my certificate file\n");
if (!(peer = PEM_read_X509 (fp, NULL, NULL, NULL)))
printf ("Error reading my certificate in file\n");
if (fp)fclose (fp);
if(peer!=NULL)
{
//ret=1 - odwolany
//ret=0 - wszystko ok
//ret = inne - jakis inny blad, np. brak wlasciwej listy crl
//ret=check_peer_crl_dir(peer,error,CRL_DIR); //sprawdzenie odwolania certyfikatu przychodzacego
ret=check_peera(peer,error,0); //sprawdzenie odwolania certyfikatu naszego
// printf("nasz certyfikat:%s:%i\n",error,ret);
}
if ((peer==NULL)||(ret==1))
{
//nie akceptujemy polaczenia bo nasz certyfikat ma problem lub jest odwolany
// printf("polaczenie odrzucone- nasz certyfikat odwolany\n");
if (exit_now)
{
remove_symdir(getpid());
zamknij_polaczenie (con,ctx,msgsock);
exit(2);
}
ok=false;
}
}
if (ok)
{
X509_free(peer); //potrzebne?
peer=NULL;
}
// printf("connected...ok\n");
remove_symdir(getpid());
return ok;
}
If I don't call fork, all works fine, the message gets through, if the fork's there it gets stuck on ssl_accept.
Any help?
Not sure if this is just a typo or your real problem but this isn't going to do what you intend:
pid_t pid = fork();
if ( pid = 0 )
{
con = NULL;
//.............
Your child code is not going to execute.