Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1863
  • Last Modified:

TCP Sockets and PThreads (Linux gcc and Mingw gcc for WinXP)

Greetings,

The Short version:

Looking for working examples of code that uses TCP for an integrated multi-client and server that is used for chatting which connects to other servers as well (sort of like a gateway) and treats it as another client so that the whole community can talk to each other.  This server itself would not have any GUI, just redirects data from one socket and sends it to all the other sockets (clients and servers alike).

Looking for working examples that will compile in Dev-C (for windows) and/or Fedora Linux via GCC which can easily be ported to the other if it doesn't support both.  Note that Dev-C uses a MingW ported GCC compiler so that most linux apps will compile with very little changes as long as it conforms to standards.

The Long version:

I am making a moderation bot for battle.net for a non-graphical linux server.  The short version above is just something that would get me started, but is actually much more complex then that as I will explain here...

I have already done this in borland delphi, so I do not need help with the authentication process of logging in or anything, but I do need help with TCP and threads for C++.

I am not ashamed that, despite my long programming career, I still have not grasped C++ in any great detail, and I am looking to use this project as an excuse to strengthen my skills in it.

Basically what I am looking for is something that I can compile in Dev-C and very easily port to Linux by using compliant code.  I would even use libraries such as HawkNL to do this which is available in both.

I would like working examples or code snippits.  Just giving me links really doesn't help me as I've tried multiple examples that I've found on google.com with very little success.

Anyway, this is my plan...

Set up a server that runs on a linux machine that will connect to another server (as a client) and join a channel for moderation purposes.  Would like the ability to connect more than once as well.

This server will have no GUI of any kind resident on the server so that it can be accessed via shell.

In addition, telnet clients can connect to this server to recieve status of what is going on, such as chat messages that are in the channel.  There should be the capability of more than one client at a time as well.  Later upgrades may even allow connected clients to talk to each other in a chat, but that is not a high priority.

Example output to the clients could be something like this:

telnet localhost:2000
  Enter Username: Rob
  Enter Password: #######
  [PHP OnLogin Response] Welcome Rob, you have access 100.
#list
  Current bots are: bot1 in woo, bot2 in woo, bot3 in blah.
#monitor bot1 as 1 <input>
  [bot1] Ralph has left the channel.
#monitor bot2 as 2 <input>
  [bot1] Bob says: hello
  [bot2] Bob says: hello
#2 hello bob!  This is rob.<input>
  [bot2] says [as Rob]: hello bob!  This is rob.
  [bot1] Bot2 says: hello bob!
  [Bot1] Bob whispers : !join blah
  [Bot1] PHP OnWhisper Response: /join blah
  [Bot1] Joins channel: blah
  [Bot1] Current Users: Willy, Rob, Etc.
  [Bot2] Bot1 has left the channel.
  [Bot1] Willy says: Hello bot!
#1 Hello Willy! <input>
  [Bot1] says [as Rob]: Hello Willy!
  [Bot1] says [as Joe]: Hi!
  [Message from Joe]: Hey, your on bots too!
#Joe Yes I am!
  [Message to Joe]: Yes I am!
#who
  [Users on bots]: Rob, Joe, Steve.

Note: that in the BNET channel, users off the bot would just see the bots talking to itself if it isn't a direct message.  :)

So, basically to accomplish this, I think I need to use threads.  PThreads is supported both by Linux and Windows via MingW's gcc compiler (and a DLL), so I think I was to go that route so that I can develop on my windows box and then port it over to the linux server later with minimal changes.

For the bots connecting to the BNET server (as a client), would need to maintain a client list, and all input recieved from BNET to be relayed to the monitoring clients of the bot server.

To complicate things even more, I want to do some automatic moderation and additional services via PHP scripts that is called after each event such as when a user joins or leaves channel, talks or whispers in the channel, or anything else that can happen.  With this, the PHP script can determine if that user has access via a DB to commands that is also parses.  The scripts themselves are a not a problem, just looking to see if there is a better way than shelling/piping PHP, or calling the script as if it was a webpage.  This is something I can figure out on my own, but I am putting in this question case anyone has any suggestions.

Any help would be appreciated!
0
werehamster-
Asked:
werehamster-
  • 4
1 Solution
 
itsmeandnobodyelseCommented:
I have a native C library for tcp/ip and threading that runs on Windows and VMS and was supposed to run on Linux also. The library project was based on a former client/server project that runs on NT/OS2/AIX. The library contains of 2 c-files, net.c and errlog.c, and four header files. It can be used like that:

// server.cpp
#include <net.h>
#include <errlog.h>

#include <iostream>
#include <string>
using namespace std;

extern "C"
{
    Int getEnableTracePointFromInifile(const char* pszTrcPtName, Int bDefaultSwitch)
    {
        return 0;
    }
}

const Int MAX_CONNECT = 32;

Bool      exitServer = False;
Int        clients    = 0;
ConnectId  connects[MAX_CONNECT] = { 0 };

// callback to get messages from a client
void readClient(void* pParam)
{  
    NetReadData* pNrd = (NetReadData*)pParam;
    ((char*)pNrd->pBuf)[pNrd->lenBuf] = '\0';
    string message = (char*)pNrd->pBuf;
    log2("readClient", SEVER_INFO, 0, "readClient %1: >%2<", ul2c(pNrd->connId), message.c_str());
    if (message == "exit")
        exitServer = True;
};

// error/disconnect callback
void connectError(void* pParam)
{  
    NetNotifyData* pNnd = (NetNotifyData*)pParam;
    for (int i = 0; i < clients; ++i)
    {
        if (connects[i] == pNnd->connId)
        {
            log1("connectError", SEVER_INFO, 0, "client %% disconnected!", ul2c(pNnd->connId-1));
            memmove(&connects[i], &connects[i+1], (MAX_CONNECT-i+1)*sizeof(ConnectId));
            --clients;
            break;
        }
    }
};

//
int completeIsTrue(void* pBuf, unsigned long lenBuf)
{
    return 1;
}

// callback for new clients
void clientConnect(void* pParam)
{
    NetAcceptData* pNpd = (NetAcceptData*)pParam;
    if (pNpd->connId > 0)
    {
        log1("clientConnect", SEVER_INFO, 0, "client %1 connected!", ul2c(pNpd->connId-1));

        connects[clients++] = pNpd->connId;

        net_read(pNpd->connId, readClient, NULL, completeIsTrue);
    }
};

int main()
{
#ifdef WIN32
   WSADATA wsaData;
   WSAStartup(MAKEWORD( 2, 2 ), &wsaData);
#endif

   initLog("server.log", TRUE);

   ConnectId cid;
   net_declare("PENTIUM1700", 43219, clientConnect, NULL, connectError, &cid);

   while (true)
   {
       if (exitServer)
       {
           log0("main", SEVER_INFO, 0, "Server stopped by client request");
           break;
       }
       delay(10);
   }
   return 0;
}

Note, there are a few things left to get it compiled at DEV-C.

If you are interested I could post the library files here in your thread (I already posted it in http:Q_21258199.html but that thread was a little messy...). The simplest way is to add the c-files to the server and client projects.

Regards, Alex
0
 
werehamster-Author Commented:
Alex,

Dunno.  Doesn't look like it would work.  Looks like the many examples I have seen that just don't compile.  :)

Feel free to email me the source to: werehamster (at) optonline.net  and I will try it out anyway.  :)
0
 
werehamster-Author Commented:
/*

Okay, this is what I got so far...

A problem I am still having is that I seem to be going into infinate loops...

I had this working where I would first just try to read the header, and then just finish reading only the rest of the data for the packet that the header belongs too, leaving the next read for the next header.

It dawned on me that I should read all the data and grab the information I need, and leave the remaining date for the next packet if there was any extra.

However, when I try to move the remaining data to the front of the queue, something has gone awry and I get an infinate loop of the last packet that was just recieved.

Perhaps someone could fix it up for me for the points seeing how I am pretty much answering my own question, cept for the listener part where I have clients connecting too.

Basically could I stick a listener routine in the same thread as my 2 servers?  So that I don't have to worry about thread safe code and put it in main instead of a pthread?  I assume as long as it is all non-blocking, it would be okay.

Any help?

*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nl.h"
#include "consts.h"
#include "shell.h"
#include "myBNLS.h"
#include "myBNET.h"

#if defined WIN32 || defined WIN64
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define sleep(x)    Sleep(1000 * (x))
#else
#include <unistd.h>
#endif

NLmutex     printmutex;             // Mutex for console display
NLboolean   shutdown = NL_FALSE;    // Server Shutdown Flag


void printErrorExit(void)
{
    NLenum err = nlGetError();
   
    if(err == NL_SYSTEM_ERROR)
    {
        printf("System error: %s\n", nlGetSystemErrorStr(nlGetSystemError()));
    }
    else
    {
        printf("HawkNL error: %s\n", nlGetErrorStr(err));
    }
    system("pause");
    nlShutdown();
    exit(1);
}

static void *mainClientLoop(void *s)
{
    NLsocket    sockBNET, sockBNLS, sockTEMP;
    NLaddress   addrBNET, addrBNLS, addrTEMP;

    NLushort    serverportBNET = 6112;    
    NLushort    serverportBNLS = 9367;    

    NLint     i, count = 4;
    NLbyte      str[256];
    char*    str2, str3;
    NLbyte  BNLS_Data[4096];
    NLbyte   BNET_Data[4096];
    int         BNLS_DataLength = 0;
    int         BNET_DataLength = 0;
    int         BNLS_PacketSize = 4096;
    int         BNET_PacketSize = 4096;
    bool       BNLS_Connected = FALSE;
    int blah = 0;
    NLenum err;

    nlStringToAddr("63.240.202.126", &addrBNET);  
    nlStringToAddr("24.20.36.240", &addrBNLS);
//    nlStringToAddr("127.0.0.1", &addrBNLS);

   
    sockBNLS = nlOpen(0, NL_RELIABLE);
    if(sockBNLS == NL_INVALID)
    {
        printf("BNLS: nlOpen error\n");
        return (void *)5;
    }
    nlSetRemoteAddr(sockBNLS, &addrBNLS);    
    nlSetAddrPort(&addrBNLS, serverportBNLS);

   
    //nlGetLocalAddr(sock, &addr);
    if(!nlConnect(sockBNLS, &addrBNLS))
    {
        printf("BNLS: nlConnect error\n");
    }
    nlMutexLock(&printmutex);
    printf("BNLS: Connected to %s\n", nlAddrToString(&addrBNLS, str));
    nlMutexUnlock(&printmutex);

    BNLS_Connected = TRUE;
    sleep(1);    

    nlMutexLock(&printmutex);
    printf("%d\n",BNLS_Send_REQUETVERSIONBYTE(sockBNLS));
    nlMutexUnlock(&printmutex);
   
    sleep(1);
    sockBNET = nlOpen(0, NL_RELIABLE);
    if(sockBNET == NL_INVALID)
    {
        printf("BNET: nlOpen error\n");
        return (void *)5;
    }
    nlSetAddrPort(&addrBNET, serverportBNET);
    if(!nlConnect(sockBNET, &addrBNET))
    {
        printf("BNET: nlConnect error\n");
    }
    printf("BNET: Connected to %s\n", nlAddrToString(&addrBNET, str));  
    sleep(1);
    str[0] = 1;
    blah = nlWrite(sockBNET, str, 1);
    if (blah == NL_INVALID) printErrorExit();
   
    while (1)
    {
        if(shutdown == NL_TRUE)
        {
            printf("SERVER: shutting down\n");
            return NULL;
        }
       
        // Check for BNLS Data

        if (BNLS_Connected)
        {
            blah = nlRead(sockBNLS, &BNLS_Data[BNLS_DataLength], sizeof(BNLS_Data)-BNLS_DataLength);
           
            if (blah == NL_INVALID) printErrorExit();
            if (blah > 0) BNLS_DataLength = BNLS_DataLength + blah;
            if (BNLS_DataLength > 1) BNLS_PacketSize = BNLS_Data[0] + (BNLS_Data[1] * 0x0F);
              else BNLS_PacketSize = 4096;
            if (BNLS_DataLength >= BNLS_PacketSize)
            {
                printf("BNLS PACKET! %.2x\n",BNET_Data[2]);
                               
                if (BNLS_Data[2] == BNLS_REQUESTVERSIONBYTE)
                {
                    printf("[BNLS] Recv: BNLS_REQUESTVERSIONBYTE (0x%.2x) \n", BNLS_Data[7]);
                    BNET_Send_SID_AUTH_INFO_Data(sockBNET, BNLS_Data[7]);                            
                }

                if (BNLS_DataLength >= BNLS_PacketSize)
                for (i=0;i<(BNLS_DataLength-BNLS_PacketSize);i++) //Put remaining data for next packet!
                    BNLS_Data[i] = BNLS_Data[i+BNLS_PacketSize];
                BNLS_DataLength = BNLS_DataLength - BNLS_PacketSize;                    
                BNLS_PacketSize = 4096;
            }
           
        }
       
           
        blah = nlRead(sockBNET, &BNET_Data[BNET_DataLength], sizeof(BNET_Data)-BNET_DataLength);
        if (blah == NL_INVALID) printErrorExit();
       
        if (blah > 0) BNET_DataLength = BNET_DataLength + blah;
        if (BNET_DataLength > 3) BNET_PacketSize = BNET_Data[2] + (BNET_Data[3] * 0x0F);
          else BNET_PacketSize = 4096;            
        if (BNET_DataLength >= BNET_PacketSize )
        {
            printf("BNET PACKET! %.2x\n",BNET_Data[1]);
           
            if (BNET_Data[1] == SID_AUTH_INFO)
            {
                printf("[BNET] Recv: SID_AUTH_INFO\n");
            }
            if (BNET_Data[1] == SID_NULL)
            {
                printf("[BNET] Recv: SID_NULL\n");
                nlWrite(sockBNET,BNET_Data, BNET_PacketSize);
            }
            if (BNET_Data[1] == SID_PING)
            {
                printf("[BNET] Recv: SID_PING\n");
                nlWrite(sockBNET,BNET_Data, BNET_PacketSize);
            }
           
            if (BNET_DataLength > BNET_PacketSize)
              for (i=0;i<(BNET_DataLength-BNET_PacketSize);i++)
                BNET_Data[i] = BNET_Data[i+BNET_PacketSize];

            BNET_DataLength = BNET_DataLength - BNET_PacketSize;
            BNET_PacketSize = 4096;
        }
    }
    nlClose(sockBNLS);
    printf("BNLS: Disconnected.\n");
      nlMutexUnlock(&printmutex);
    sleep(1);
    return (void *)4;
}



int main(int argc, char **argv)
{
    NLsocket        serversock;
    NLthreadID      tid;
    NLthreadID      BNETid;
//    NLenum          type = NL_IP;/* default network type */
    NLint           exitcode;

    system("pause");
    printf("-- START BNET --\n");
    if(!nlInit()) printErrorExit();
    if(!nlSelectNetwork(NL_IP)) printErrorExit();
   
    printf("Using HawkNL Version: %s\n", nlGetString(NL_VERSION));
   
    tid = nlThreadCreate(mainClientLoop, NULL, NL_TRUE);
    nlThreadJoin(tid, (void **)&exitcode);
    printf("mainClientLoop exited with code %d\n", exitcode);

    sleep(4);
    shutdown = NL_TRUE;    
    nlShutdown();
      nlMutexDestroy(&printmutex);
   
       
    printf("-- END BNET --\n");
    system("pause");
//    printf("-- START SHELL --\n");
//    shell();
//    printf("-- END SHELL --\n");
//    system("pause");    
    return 1;
}
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
werehamster-Author Commented:
I have since solved this problem on my own.  Please refund if no objections...  :)
0
 
werehamster-Author Commented:
Possibly, could ya give itsmeandnobodyelse like 50 points before closing?  He did try.  :)
0
 
OzzModCommented:
Closed, 500 points refunded.
OzzMod
Community Support Moderator (Graveyard shift)
0

Featured Post

Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now