?
Solved

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

Posted on 2005-03-01
8
Medium Priority
?
1,784 Views
Last Modified: 2012-06-27
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
Comment
Question by:werehamster-
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
8 Comments
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 13438356
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
 

Author Comment

by:werehamster-
ID: 13438669
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
 

Author Comment

by:werehamster-
ID: 13448237
/*

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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 

Author Comment

by:werehamster-
ID: 13649957
I have since solved this problem on my own.  Please refund if no objections...  :)
0
 

Author Comment

by:werehamster-
ID: 13656484
Possibly, could ya give itsmeandnobodyelse like 50 points before closing?  He did try.  :)
0
 

Accepted Solution

by:
OzzMod earned 0 total points
ID: 13686592
Closed, 500 points refunded.
OzzMod
Community Support Moderator (Graveyard shift)
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
Suggested Courses

765 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question