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.