Functions
- skipblank(): For every unwanted blanks, we create a function to erase them.
 
void skipblank(char *str){
    int i = 0;
    while(str[i++] == ' ');
    i--;
    int j = 0;
    while((j + i) < strlen(str)){
        str[j] = str[j + i];
        j++;
    }
    str[j] = '\0';
}
void skipblank(string &str){
    int i = 0;
    while(str[i++] == ' ');
    i--;
    int j = 0;
    while((j + i) < str.size()){
        str[j] = str[j + i];
        j++;
    }
    str[j] = '\0';
}
- tabToBlank(): For every unwanted tabs, we create a function to erase them.
 
void tabToBlank(char *str){
    for(int i=0;i<strlen(str);i++){
        if(str[i] == '\t')
            str[i] = ' ';
    }
}
- getIndex(): We get the index of the client from our user structure array, by comparing ports and addresses.
 
int getIndex(const string msg, struct user *arr){
    char addr[20];
    char port[20];
    int i = 0;
    while(msg[i]!=' '){
        addr[i] = msg[i];
        i++;
    }
    i++;    // so i will not be ' '
    int j = 0;
    while(msg[j + i]!=','){
        port[j] = msg[j + i];
        j++;
    }
    addr[i-1] = '\0';
    port[j] = '\0';
    for(i=0;i<FD_SETSIZE;i++){
        if(arr[i].addr == string(addr) && arr[i].port == string(port))
            return i;
    }
    return -1;
}
- changeName(): If the client comes before, we assign it to the previous index in user structure and clear the current index content.
 
int changeName(int index, string name){
    int j=0;
    string temp = "";
    while(name[j] != ' '){
        temp += name[j++];
        if(j == name.size())
            break;  // end
    }
    cout << j << ", " << name.size();
    if(j < name.size()){
        // get ' '
        while(name[j++] == ' ');
        cout << j << ", " << name.size();
        if(j == name.size() + 1){
            cout << ".............\n";
            cout << name << endl;
            name = temp;
            cout << name << endl;
        }
        else
            return 3;   //  name not alphabets
    }
    cout << name.size() << ",, " << name << endl;
    if(name.size() > 12 || name.size() < 2)
        return 0;   //  name size error
    if(name == "anonymous\0")
        return 1;   //  name = anonymous
    for(int i=0;i<FD_SETSIZE;i++){
        if(name == childarr[i].name && client[i] > 0)
            return 2;   //  name already exist
    }
    for(int i=0;i<name.size();i++){
        if(!((name[i] <= 'z' && name[i] >= 'a') || (name[i] <= 'Z' && name[i] >= 'A')))
            return 3;   //  name not alphabets
    }
    //  name permitted
    childarr[index].name = name;
    return 4;
}
- sendAll(): Broadcast a message to every clients.
 
void sendAll(string msg, int except){
    for(int i=0;i<FD_SETSIZE;i++){
        if(childarr[i].die == 0 && i != except)
            write(client[i], msg.c_str(), msg.size());
    }
}
- dealString(): Using previous funtions to deal with every messages.
 
void dealString(string msg){
    int index = getIndex(msg, childarr);
    char recvline[msg.size()];
    int i=0;
    while(msg[i++]!=',');
    //  ignore addr and port
    int j;
    for(j=i;j<msg.size();j++)
        recvline[j-i] = msg[j];
    recvline[j-i] = '\0';
        tabToBlank(recvline);
        skipblank(recvline);
        cout << string(recvline) << endl;
        
        if(recvline[string(recvline).size()-1] == '\n' && recvline[string(recvline).size()-2] == '\r')
            recvline[string(recvline).size()-2] = '\0';
        
        if(recvline[0] == '\x03'){
            cout << "Client terminates...\n";
            string msg = "[Server] " + childarr[index].name + " is offline.\n";
            sendAll(msg, index);
            close(client[index]);
            client[index] = -1;
            TOTALCLI--;
            FD_CLR(client[index], &allSet);
        }
        if(strlen(recvline) < 2){
            string msg = "ERROR: Error command.";
            write(client[index], msg.c_str(), msg.size());
            return;
        }
        else{
            char command[5];
            for(int j=0;j<4;j++)
                command[j] = recvline[j];
            command[4] = '\0';
            if(strcmp(command, "exit\0") == 0 || strcmp(command, "exit ") == 0){
                childarr[index].die = 1;    //  mark with die
                
                cout << "Client terminates...\n";
                string msg = "[Server] " + childarr[index].name + " is offline.\n";
                sendAll(msg, index);
                close(client[index]);
                client[index] = -1;
                TOTALCLI--;
                FD_CLR(client[index], &allSet);
                return;
            }
        }
        char temp[5];
        for(int i=0;i<4;i++)
            temp[i] = recvline[i];
        temp[i] = '\0';
        if(strcmp(temp, "who \0") == 0 || strcmp(recvline, "who\0") == 0 ){
        //  list all online users
            string msg = "";
            for(int j=0;j<FD_SETSIZE;j++){
                if(client[j] > 0){
                    if(index != j){
                        msg += "[Server] " + childarr[j].name + " " + childarr[j].addr + "/" + childarr[j].port + "\n";
                    }
                    else{
                        msg += "[Server] " + childarr[j].name + " " + childarr[j].addr + "/" + childarr[j].port + " ->me\n";
                    }
                }
            }
            msg += "\0";
            write(client[index], msg.c_str(), msg.size());
        }
        else{
            
            //  *********************************************
            char command[6];
            for(int j=0;j<5;j++)
                command[j] = recvline[j];
            command[5] = '\0';
            //  *********************************************
            if(strcmp(command, "name \0") == 0){
                string oldName = childarr[index].name;
                for(i=0;i<string(recvline).size()-5;i++)
                    recvline[i] = recvline[i + 5];
                recvline[i] = '\0';
                skipblank(recvline);
                int status = changeName(index, recvline);
                switch(status){
                    case 0:{
                        string msg = "[Server] ERROR: Username can only consists of 2~12 English letters.\n";
                        write(client[index], msg.c_str(), msg.size());
                        break;
                    }
                    case 1:{
                        string msg = "[Server] ERROR: Username cannot be anonymous.\n";
                        write(client[index], msg.c_str(), msg.size());
                        break;
                    }
                    case 2:{
                        string msg = "[Server] ERROR: " + string(recvline) + " has been used by others.\n";
                        write(client[index], msg.c_str(), msg.size());
                        break;
                    }
                    case 3:{
                        string msg = "[Server] ERROR: Username can only consists of 2~12 English letters.\n";
                        write(client[index], msg.c_str(), msg.size());
                        break;
                    }
                    case 4:{
                        for(int j=0;j<FD_SETSIZE;j++){
                            if(childarr[j].die == 0){
                                if(j == index){
                                    string msg = "[Server] You're now known as " + string(recvline) + ".\n";
                                    write(client[j], msg.c_str(), msg.size());
                                }
                                else{
                                    string msg = "[Server] " + oldName + " is now known as " + string(recvline) + ".\n";
                                    write(client[j], msg.c_str(), msg.size());
                                }   
                            }
                        }
                        break;
                    }
                    default:{
                        break;
                    }
                }
            }
            else if(strcmp(command, "tell \0") == 0){
                for(i=0;i<string(recvline).size()-5;i++)
                    recvline[i] = recvline[i + 5];
                recvline[i] = '\0';
                string name = "", msgToSend = "";
                
                skipblank(recvline);
                int i = 0;
                while(recvline[i] != ' ' && recvline[i] != '\n' && recvline[i] != '\0')
                    name += recvline[i++];
                i++;
                while(i < strlen(recvline))
                    msgToSend += recvline[i++];
                
                if(msgToSend == ""){
                    string msg = "ERROR: Error command.\n";
                    write(client[index], msg.c_str(), msg.size());
                    return;
                }
                
                skipblank(msgToSend);
                int selfNameError = 0, error = 0, toWho;
                string msg = "";
                if(childarr[index].name == "anonymous"){
                    selfNameError = 1;
                    error = 1;
                    msg += "[Server] ERROR: You are anonymous.";
                }
                if(name == "anonymous"){
                    error = 1;
                    if(selfNameError == 1)
                        msg += "\n";
                    msg += "[Server] ERROR: The client to which you sent is anonymous.";
                }
                else{
                    int j;
                    for(j=0;j<FD_SETSIZE;j++){
                        if(name == childarr[j].name && client[j] > 0)   //  name exist and still alive
                            break;
                    }
                    
                    if(j == FD_SETSIZE){
                        error = 1;
                        if(selfNameError == 1)  // have msg already
                            msg += "\n";
                        msg += "[Server] ERROR: The receiver doesn't exist.";   
                    }
                    else
                        toWho = j;
                }
                if(error == 1){
                    msg += "\n";
                    write(client[index], msg.c_str(), msg.size());
                }
                else{
                    //  no error
                    string msg = "[Server] SUCCESS: Your message has been sent.\n";
                    write(client[index], msg.c_str(), msg.size());
                    msg = "[Server] " + childarr[index].name + " tell you " + msgToSend + "\n";
                    write(client[toWho], msg.c_str(), msg.size());
                }
            }
            else if(strcmp(command, "yell \0") == 0){
                for(i=0;i<string(recvline).size()-5;i++)
                    recvline[i] = recvline[i + 5];
                recvline[i] = '\0';
                skipblank(recvline);
                if(strcmp(recvline, "") == 0){
                    string msg = "ERROR: Error command.\n";
                    write(client[index], msg.c_str(), msg.size());
                    return;
                }
                string msg = "[Server] " + childarr[index].name + " yell " + recvline + "\n";
                for(int j=0;j<FD_SETSIZE;j++){
                    if(childarr[j].die == 0)
                        write(client[j], msg.c_str(), msg.size());
                }
            }
            else{
                string msg = "ERROR: Error command.\n";
                write(client[index], msg.c_str(), msg.size());
            }
        }
}  
 
Main Function
int main(int argc, char ** argv)
{   
    if(argc < 2){
        cerr << "command error...\n";
        exit(0);
    }
    int maxi, maxfd, listenfd, connectfd, sockfd;
    int nready;
    socklen_t clientLen;
    struct sockaddr_in cliaddr, seraddr;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&seraddr, sizeof(seraddr));
    seraddr.sin_family = AF_INET;
    seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    seraddr.sin_port = htons(atoi(argv[1]));
    bind(listenfd, (sockaddr *) &seraddr, sizeof(seraddr));
    listen(listenfd, LISTENQ);
    maxfd = listenfd;
    maxi = -1;
    for(int i=0;i<FD_SETSIZE;i++)
        client[i] = -1; // initialize with no one in client array
    FD_ZERO(&allSet);   // initialize
    FD_SET(listenfd, &allSet);  //  listen to listenfd
    while(1){
        readSet = allSet;
        nready = select(maxfd + 1, &readSet, NULL, NULL, NULL);
        if(FD_ISSET(listenfd, &readSet)){
            clientLen = sizeof(cliaddr);
            connectfd = accept(listenfd, (sockaddr *) &cliaddr, &clientLen);
            int i;
            for(i=0;i<FD_SETSIZE;i++){
                if(client[i] < 0){
                    //  client in
                    client[i] = connectfd;
                    cout << "client: " << client[i] << endl;
                    char temp[5];
                    sprintf(temp,"%4d",(int)cliaddr.sin_port);
                    string msg = string("[Server] Hello, anonymous! From: " + string(inet_ntoa(cliaddr.sin_addr)) + '/' + temp + '\n');
                    string msg2 = "[Server] Someone is coming!\n";
                    childarr[i] = user("anonymous", string(inet_ntoa(cliaddr.sin_addr)), temp);
                    
                    write(connectfd, msg.c_str(), msg.size());  //  send welcoming msg to new client
                    sendAll(msg2, i);
                    TOTALCLI++;
                    break;
                }
            }
            if(i == FD_SETSIZE){
                cout << "Too many client...\n";
                exit(0);
            }
            
            FD_SET(connectfd, &allSet);
            if(connectfd > maxfd)
                maxfd = connectfd;
            if(maxi < i)
                maxi = i;
            if(--nready <= 0)
                continue;
        }
        for(int i=0;i<=maxi;i++){
            if((sockfd = client[i]) < 0)
                continue;
            if(FD_ISSET(sockfd, &readSet)){
                int n;
                char buffer[MAXLINE];
                bzero(buffer, MAXLINE);
                if((n = recv(sockfd, buffer, MAXLINE, 0)) > 0){
                    buffer[n] = 0;
                    string msg = childarr[i].addr + " " + childarr[i].port + "," + string(buffer);
                    
                    dealString(msg);
                }
                if(n == 0){
                    cout << "Client terminates...\n";
                    string msg = "[Server] " + childarr[i].name + " is offline.\n";
                    sendAll(msg, i);
                    close(sockfd);
                    TOTALCLI--;
                    FD_CLR(sockfd, &allSet);
                    client[i] = -1;
                }
                if(--nready <= 0)
                    break;  // no more readable descriptors
            }
        }
    }
    return 0;
}