I. Mission description

Both clients A and B run Ubuntu. Cloud server C, running Ubuntu.

User A and user B establish TCP connections with user C.

User A sends A TCP packet to user C, and user C forwards the PACKET to user B. B sends the packet back to C, and C forwards the packet to A. A displays the time spent.

Requirements: A, B, C end all programs written in C/C++, can use external library, but only C/C++ written library.

The overall implementation idea is shown in the figure:

Two, server implementation

General idea: set the socket connection to non-blocking, use a structure array on the server to maintain the information of each connected client, disassemble the messages sent by the client (including the recipient index and the body message), and send them to the specified client according to the index, so as to achieve the function of message forwarding.

IP: indicates the host where the server is running

Port: 8888

echo_server.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define PORT  8888
#define BACKLOG 10
#define MAXCONN 100
#define BUFFSIZE 1024
typedef unsigned char BYTE;
// Client information
typedef struct ClientInfo
{
    struct sockaddr_in addr;
    int clientfd;
    int isConn;
    int index;
} ClientInfo;
pthread_mutex_t activeConnMutex;
pthread_mutex_t clientsMutex[MAXCONN];
pthread_t threadID[MAXCONN];
ClientInfo clients[MAXCONN];

// Convert to lowercase
void tolowerString(char *s)
{
    int i=0;
    while(i < strlen(s))
    {
        s[i] = tolower(s[i]); ++i; }}// Create a new thread to manage the respective clients
void clientManager(void* argv)
{
    ClientInfo *client = (ClientInfo *)(argv);

    BYTE buff[BUFFSIZE];
    int recvbytes;

    int i=0;
    int clientfd = client->clientfd;
    struct sockaddr_in addr = client->addr;
    int isConn = client->isConn;
    int clientIndex = client->index;

    // Receive data
    while((recvbytes = recv(clientfd, buff, BUFFSIZE, 0)) != - 1)
    {
        tolowerString(buff);    // Format conversion

        char msg[BUFFSIZE]; // Client message body
        int dest = clientIndex; // The client to which the message is sent

        sscanf(buff, "%d%s", &dest, msg);

        fprintf(stdout."Sender index: %d Receiver index: %d, message: %s\n",clientIndex, dest, msg);

        // Add a mutex for the target client
        pthread_mutex_lock(&clientsMutex[dest]);

        //printf(" sender index: %d receiver index: %d\n",clientIndex,dest);
        char str[10];
        sprintf(str,"%d ",clientIndex);
        strcat(str,msg);
        // Send information to the target client
        if(send(clients[dest].clientfd, str, strlen(str)+1.0) = =- 1)
        {
            fprintf(stderr."Message sending failed \n");
            pthread_mutex_unlock(&clientsMutex[dest]);
            break;
        }
        printf("Message sent successfully \n");
        / / releases the lock
        pthread_mutex_unlock(&clientsMutex[dest]);


    }   //end while

}

int main(a)
{
    int i=0;
    for(; i<MAXCONN; ++i)// Initialize the client array mutex
        pthread_mutex_init(&clientsMutex[i], NULL);

    for(i=0; i<MAXCONN; ++i)// Initialize the client connection
        clients[i].isConn = 0;

    int listenfd;
    struct sockaddr_in  servaddr;

    // Create a socket
    if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) = =- 1)
    {
        fprintf(stdout."Failed to create socket \n");
        exit(0);
    }
    else
        fprintf(stdout."Socket created successfully \n");

    fcntl(listenfd, F_SETFL, O_NONBLOCK);       F_SETFL: sets the file status flag

    // Set the server address
    memset(&servaddr, 0.sizeof(servaddr));  // Initialize the service address
    servaddr.sin_family = AF_INET;           //AF_INET uses TCP
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    // Any input address
    servaddr.sin_port = htons(PORT);            // Set the port

    / / bind socket
    if(bind(listenfd, (struct sockaddr*)(&servaddr), sizeof(servaddr)) == - 1)
    {
        fprintf(stdout."Failed to bind socket \n");
        exit(0);
    }
    else
        fprintf(stdout."Socket binding successful \n");

    / / listen state
    if(listen(listenfd, BACKLOG) == - 1)
    {
        fprintf(stdout."Socket listening failed \n");
        exit(0);
    }
    else
        fprintf(stdout."Listening on socket successfully \n");


    while(1)
    {
        // Find an empty location for the new connection
        int i=0;
        while(i<MAXCONN)
        {
            / / lock
            pthread_mutex_lock(&clientsMutex[i]);
            if(! clients[i].isConn) { pthread_mutex_unlock(&clientsMutex[i]);break;
            }
            / / releases the lock
            pthread_mutex_unlock(&clientsMutex[i]);
            ++i;
        }
        // The connection is full, initialize the first connection
        if (i == MAXCONN) i = 0;

        / / the accept state
        struct sockaddr_in addr;
        int clientfd;
        int sin_size = sizeof(struct sockaddr_in);
        if((clientfd = accept(listenfd, (struct sockaddr*)(&addr), &sin_size)) == - 1)
        {
            sleep(1);
            continue;
        }
        else
            fprintf(stdout."Client %d connected successfully \n",i);

        // Lock the current client connection
        pthread_mutex_lock(&clientsMutex[i]);
        // Add client data
        clients[i].clientfd = clientfd;
        clients[i].addr = addr;
        clients[i].isConn = 1;
        clients[i].index = i;
        / / release
        pthread_mutex_unlock(&clientsMutex[i]);

        // Create a thread to manage the current client
        pthread_create(&threadID[i], NULL, (void *)clientManager, &clients[i]);

    }    / / end
}
Copy the code

Iii. Client A implementation

A_echo_client.c

IP and Port Enter the IP address and port of the server

General idea: use a new thread to receive data, send data in the main thread, record the time before sending data, record the time again when receiving data, the time difference is the time spent by echo.

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>// Count the time
#define PORT  8888/ / port
#define IP  "120.79.165.96"// Server IP address
#define BUFFERSIZE 1024
typedef unsigned char BYTE;
pthread_t receiveID;
time_t c_start,c_end;/ / time

// Character format conversion
void tolowerString(char *s)
{
    int i=0;
    while(i < strlen(s))
    {
        s[i] = tolower(s[i]); ++i; }}// Receives data from the server
void receive(void *argv)
{
    int sockclient = *(int*)(argv);
    BYTE recvbuff[BUFFERSIZE];
    while(recv(sockclient, recvbuff, sizeof(recvbuff), 0)! =- 1) //receive
    {

        BYTE msg[BUFFERSIZE];
        int dest = 0;// Stores the id of the sender on the server side, which is not needed here
        sscanf(recvbuff, "%d%s",&dest,msg);
        c_end   = clock();// Data is received

        fprintf(stdout."\n Received message \n sender index: %d message: %s\n", dest, msg);
        fprintf(stdout."Time %.2f ms \n\n",difftime(c_end,c_start)); }}int main(a)
{

    / / the new socket
    int sockclient = socket(AF_INET,SOCK_STREAM, 0);
    / / address
    struct sockaddr_in servaddr;
    memset(&servaddr, 0.sizeof(servaddr));

    // Set the initial value
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);  ///server port
    servaddr.sin_addr.s_addr = inet_addr(IP);  //server ip

    // Connect to the server
    if (connect(sockclient, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
    {
        fprintf(stderr."Failed to connect to server");
        exit(0);
    }
    fprintf(stdout."Successful connection to server \n");
    // Add a thread to receive data
    pthread_create(&receiveID, NULL, (void *)(receive), (void *)(&sockclient));
    BYTE buff[BUFFERSIZE];
    fprintf(stdout."Input format :(client index) (message) e.g. 0 hello! Ps: Index is in client startup order, starting from 0 \n");
    while (fgets(buff, sizeof(buff), stdin) != NULL)
    {
        tolowerString(buff);
        // The main thread sends data
        c_start = clock();// Start time
        if(send(sockclient, buff, strlen(buff)+1.0) = =- 1) //send
        {
            fprintf(stderr."Send failed \n");
            continue;
        }
        / / to empty
        memset(buff, 0.sizeof(buff));
    }
    close(sockclient);
    return 0;
}
Copy the code

Fourth, echo client B implementation

B_echo_client.c

IP and Port Enter the IP address and port of the server

General idea: create A new thread to receive data, the main thread to send data (directly copy the code of client A, can cancel this function), as for echo operation, can directly send back the data sent from the server, directly intact. (The data already contains the id of the sender, namely client A, according to which the server can directly return the data to client A.)

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>// Count the time
#define PORT  8888/ / port
#define IP  "120.79.165.96"// Server IP address
#define BUFFERSIZE 1024
typedef unsigned char BYTE;
pthread_t receiveID;

// Character format conversion
void tolowerString(char *s)
{
    int i=0;
    while(i < strlen(s))
    {
        s[i] = tolower(s[i]); ++i; }}// Receives data from the server
void receive(void *argv)
{
    int sockclient = *(int*)(argv);
    BYTE recvbuff[BUFFERSIZE];
    while(recv(sockclient, recvbuff, sizeof(recvbuff), 0)! =- 1) //receive
    {

        BYTE msg[BUFFERSIZE];
        int dest = 0;// Stores the id of the sender on the server side, which is not needed here
        sscanf(recvbuff, "%d%s",&dest,msg);

        fprintf(stdout."Received message \n Sender index: %d message: %s\n", dest, msg);
        fprintf(stdout."News echoes \n\n");
        // return echo data
        send(sockclient, recvbuff, strlen(recvbuff)+1.0); }}int main(a)
{
    / / the new socket
    int sockclient = socket(AF_INET,SOCK_STREAM, 0);
    / / address
    struct sockaddr_in servaddr;
    memset(&servaddr, 0.sizeof(servaddr));
    // Set the initial value
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);  ///server port
    servaddr.sin_addr.s_addr = inet_addr(IP);  //server ip

    // Connect to the server
    if (connect(sockclient, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
    {
        fprintf(stderr."Failed to connect to server");
    }
    fprintf(stdout."Successful connection to server \n");
    // Add a thread to receive data
    pthread_create(&receiveID, NULL, (void *)(receive), (void *)(&sockclient));

    BYTE buff[BUFFERSIZE];

    while (fgets(buff, sizeof(buff), stdin) != NULL)
    {
        tolowerString(buff);
        // The main thread sends data
        if(send(sockclient, buff, strlen(buff)+1.0) = =- 1) //send
        {
            fprintf(stderr."Send failed \n");
            continue;
        }
        memset(buff, 0.sizeof(buff));
    }
    close(sockclient);
    return 0;
}
Copy the code

Five, operation steps

5.1 Server Configuration

Open ali Cloud server port 8888 (for testing, you can skip this step and run directly on the local computer, and change the client IP to 127.0.0.1)

Go to the cloud server instance and find the security group

Click to enter a security group

Add an inbound rule and open port 8888

The cloud server configuration is complete

5.2 run echo_server. C

Use a file transfer tool to upload the C file to the Linux /home/c folder on the server

Use the Xshell tool to connect to the server and go to the /home/c folder

Run GCC echo_server.c -o echo_server -lpthread

To generate an executable file echo_server, run the./echo_server command

5.3 perform A_echo_client. C

In any Linux terminal where I open the C file, I’m using a virtual machine. Run GCC a_echo_client. c -o A_echo_client -lpthread

Run the./A_echo_client command

5.4 perform B_echo_client. C

Open the C file on any Linux terminal, where I’m also using a virtual machine. Run GCC b_echo_client. c -o B_echo_client -lpthread

Run./B_echo_client

5.4 test

According to the order of connecting to the server, we can determine that client 0 is A and client 1 is B, so we use A to send messages to B. Wait for B echo data. The results are as follows:

Visible server forwarding the data twice, once be sent on A to B, the second is the echo data, send A, B, because B receives the information contains A index, so only need on the server parsed, can the service side can directly send back to A), A client from sending information to the whole process of receiving information is always time-consuming

At this point, the whole project is complete.