I have two files: tcp-demo-client.c and tcp-demo-server.c
Functionality: If the connection succeeds, the client receives a simple timestamp from the server. I like to modify the code that the server only sends the timestamp if the client hits the space key. How can I do that?
(It's my first socket project)
tcp-demo-client.c:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
int main(int argc, char **argv)
{
int ret; // return value from functions
// Check command line arguments
if (argc != 3) {
fprintf(stderr,
"Missing parameters. Usage: %s <server-name-or-ip> <server-port>\n",
argv[0]);
return 1;
}
// Address information structure
struct addrinfo aii;
// Set whole structure to 0s
memset(&aii, 0, sizeof(aii));
// A stream (TCP) connection
aii.ai_socktype = SOCK_STREAM;
// We do not care whether it is IPv4 or IPv6
aii.ai_family = PF_UNSPEC;
struct addrinfo *aio;
// Get address information.
// First parameter is host string, either hostname or numerical IPv4/IPv6 address
// Second parameter is port/service string, either as port number
// or well-known identifier, e.g. http
// So, e.g. getaddrinfo( "www.compeng.uni-frankfurt.de", "http", ... getaddrinfo( "141.2.248.1", "80", ...
// Third parameter is input address info structure (cf. above)
// Fourth parameter is output address info structure, a linked list of potential addresses
ret = getaddrinfo(argv[1], argv[2], &aii, &aio);
if (ret) {
fprintf(stderr, "Error getting address for %s:%s: %s\n",
argv[1], argv[2], gai_strerror(ret));
return 1;
}
// File descriptor for the socket
int sock = -1;
struct addrinfo *iter;
// Iterate over linked list of specified output addresses,
// use first address to which a connection can be established
for (iter = aio; iter != NULL && sock == -1; iter = iter->ai_next) {
// Create socket given the parameters from the found address info.
sock =
socket(iter->ai_family, iter->ai_socktype,
iter->ai_protocol);
if (sock < 0)
continue; // Appropriate socket could not be created, try next address
// Socket created successfully, now try to connect to remote target address
// taken from address info
ret = connect(sock, iter->ai_addr, iter->ai_addrlen);
if (ret) {
// Socket could not be connected to remote target
close(sock); // Close socket
sock = -1;
continue; // try next address
}
}
freeaddrinfo(aio); // Release address information allocated in getaddrinfo
if (sock == -1) {
// No connection at all could be established to remote target
fprintf(stderr, "Unable to establish any connection to %s:%s\n",
argv[1], argv[2]);
return 1;
}
// Maximum size of incoming message
int msglen = 100;
// Buffer for message
char buf[msglen + 1]; // One more to ensure that there is a trailing NULL char.
memset(buf, 0, msglen + 1);
ret = read(sock, buf, msglen); // Return value is amount of bytes read, -1 in case of error
printf("Data read: '%s'\n", buf);
// Clean up after us and close the socket.
close(sock);
return 0;
}
tcp-demo-server.c:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#define MAXPENDING 5
int main(int argc, char **argv)
{
unsigned short listen_port; // Server port */
int listen_sock; // Socket descriptor for server
int client_sock; // Socket descriptor for client
struct sockaddr_in listen_addr; // Local address */
struct sockaddr_in client_addr; // Client address */
// Check command line arguments
if (argc != 2) {
fprintf(stderr, "Missing parameters. Usage: %s <server-port>\n",
argv[0]);
return 1;
}
// Create socket for incoming connections
if ((listen_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket() failed");
return 1;
}
// Construct local address structure
listen_port = atoi(argv[1]); // First arg: listening port number
memset(&listen_addr, 0, sizeof(listen_addr)); // Zero out structure
listen_addr.sin_family = AF_INET; // Internet address family
listen_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Any incoming interface
listen_addr.sin_port = htons(listen_port); // Local port
// Bind to the local address
if (bind
(listen_sock, (struct sockaddr *)&listen_addr,
sizeof(listen_addr)) < 0) {
perror("bind() failed");
return 1;
}
// Mark the socket so it will listen for incoming connections
if (listen(listen_sock, MAXPENDING) < 0) {
perror("listen() failed");
return 1;
}
for (;;) { /* Run forever */
socklen_t addr_len = sizeof(client_addr);
// Wait for a client to connect */
if ((client_sock =
accept(listen_sock, (struct sockaddr *)&client_addr,
&addr_len)) < 0) {
perror("accept() failed");
return 1;
}
// client_sock is connected to a client
printf("New connection from %s\n",
inet_ntoa(client_addr.sin_addr));
// Create message to send
time_t t = time(NULL);
char *msg = ctime(&t);
int msglen = strlen(msg) + 1;
int ret;
// Write the whole message in one go, fail if this does not work
ret = write(client_sock, msg, msglen);
// Return value is amount of bytes written, -1 in case of error
if (ret != msglen) {
perror("Error during write");
return 1;
}
close(client_sock);
}
/* NOT REACHED */
return 1;
}
I presume you mean you want the space char as unbuffered input. For POSIX, you could use something along the lines of this to capture the keypress:
#include <termios.h>
[...]
struct termios t;
int c, r;
[...]
tcgetattr(0, &t);
t.c_lflag &= ~(ICANON | ECHO);
t.c_cc[VMIN] = 1;
tcsetattr(0, TCSANOW, &t);
c = getchar();
r = send(sock, c, 1, 0);
Have a look at this for additional information:
setvbuf not able to make stdin unbuffered
http://c-faq.com/osdep/cbreak.html
Related
I work on the server side Socket (use Telnet client) in Linux. Client input a line with command(GET/PUT/DEL, key and an associated value (spaces to seperate in between). This key-value pair is then passed accordingly on to the function(GET/PUT/DEL), which saves the data in the shared memory (keyValueStore).
Expected client side: (> is the output from Server)
GET key1
> GET:key1:key_nonexistent
PUT key1 value1
> PUT:key1:value1
PUT key2 value2
> PUT:key2:value2
DEL key2
> DEL:key2:key_deleted
Questions:
1/ i tried to use strtok() and keyValueStore to seperate & save the tokens in a normal c file, but how should I do (or transform) it into the data transfer communication between server and client?
2/ when or where should I call the command functions (e.g. int put(char* key, char* value) )? in server.c after reading the input but before giving output?
Any advices is appreicated. Thanks for your kindness!
server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define BUFSIZE 1024 // Buffer Size
#define TRUE 1
#define PORT 5678
int main() {
int rfd; // Create-Descriptor
int cfd; // Connection-Descriptor (accept)
struct sockaddr_in client;
socklen_t client_len;
char in[BUFSIZE];
int bytes_read;
// 1. socket()
rfd = socket(AF_INET, SOCK_STREAM, 0);
if (rfd < 0 ){
fprintf(stderr, "Error\n");
exit(-1);
}
//Initialize the server address by the port and IP
struct sockaddr_in server;
memset(&server, '\0', sizeof(server));
server.sin_family = AF_INET; // Internet address family: v4 address
server.sin_addr.s_addr = INADDR_ANY; // Server IP address
server.sin_port = htons(PORT); // Server port
// 2. bind()
int brt = bind(rfd, (struct sockaddr *) &server, sizeof(server));
if (brt < 0 ){
fprintf(stderr, "Error\n");
exit(-1);
}
// 3. listen() = listen for connections
int lrt = listen(rfd, 5);
if (lrt < 0 ){
fprintf(stderr, "Error\n");
exit(-1);
}
while (1) {
// 4. accept()
cfd = accept(rfd, (struct sockaddr *) &client, &client_len);
// read() = read from a socket (Client's data)
bytes_read = read(cfd, in, BUFSIZE);
while (bytes_read > 0) {
printf("sending back the %d bytes I received...\n", bytes_read);
// write() = write data on a socket (Client's data)
write(cfd, in, bytes_read);
bytes_read = read(cfd, in, BUFSIZE);
}
close(cfd);
}
close(rfd);
}
Input.c
#include <stdio.h>
#include <string.h>
#include <stdio.h>
#define MAX_ARRAY 100
int main() {
typedef struct Value_ {
char key[MAX_ARRAY];
char value[MAX_ARRAY];
} KeyStorage;
KeyStorage storageKey[MAX_ARRAY];
char client_input[MAX_ARRAY];
char *argv[3];
char *token;
int count = 0;
while (1) {
printf("Input: ");
gets(client_input);
//get the first token
token = strtok(client_input, " ");
int i = 0;
//walk through other tokens
while (token != NULL) {
argv[i] = token;
i++;
token = strtok(NULL, " ");
}
argv[i] = NULL; //argv ends with NULL
// arg[0] = command z.B. GET, PUT
printf("Commend: %s\n", argv[0]);
strcpy(storageKey[count].key, argv[1]);
printf("Key: %s\n", storageKey[count].key);
strcpy(storageKey[count].value, argv[2]);
printf("Value: %s\n", storageKey[count].value);
count++;
if (strcmp(argv[0], "QUIT") == 0) {
break;
}
}
return 0;
}
There are a number of errors in your code. I have fixed all to build a working example. Of course, this is not your complete application and there is even a lot of room for enhancements.
I developed and tested my code with MSVC2019 under Windows but I used a #define to isolate Windows specific code so it should compile and run correctly under Linux as well (I have not tested that).
The main problem your code had is a misunderstanding of TCP connection. It is a stream oriented connection and you must assemble "command lines" yourself, receiving one character at a time.
It is only when a line is complete that you can parse it to detect the command sent by the client. I made simple: only one command "exit" does something (close the connection). Everything else is simply ignored.
I made line assembling the easy way. That means that there is no edit possible. Backspace, delete, cursor keys and more and input as any other characters and doesn't work a a user would expect. You should take care of that.
Finally, I kept the code close to what you used. This code is single user. It accept a connection, accept commands from it and only accept a new connection once the first is closed. This is not normally the way to create a server program. To make it multiuser, you should use non-blocking socket and select() or use multi-threading.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef WIN32
#include <WinSock2.h>
#include <io.h>
typedef int socklen_t;
#pragma warning(disable : 4996) // No warning for deprecated function names such as read() and write()
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define closesocket close
#endif
#define BUFSIZE 1024 // Buffer Size
#define TRUE 1
#define PORT 5678
int main(int argc, char *argv[])
{
#ifdef WIN32
int iResult;
WSADATA wsaData;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
#endif
int rfd; // Create-Descriptor
int cfd; // Connection-Descriptor (accept)
struct sockaddr_in client;
socklen_t client_len;
char in[BUFSIZE];
int bytes_read;
// 1. socket()
rfd = socket(AF_INET, SOCK_STREAM, 0);
if (rfd < 0) {
fprintf(stderr, "Error\n");
exit(-1);
}
// Initialize the server address by the port and IP
struct sockaddr_in server;
memset(&server, '\0', sizeof(server));
server.sin_family = AF_INET; // Internet address family: v4 address
server.sin_addr.s_addr = INADDR_ANY; // Server IP address
server.sin_port = htons(PORT); // Server port
// 2. bind()
int brt = bind(rfd, (struct sockaddr*)&server, sizeof(server));
if (brt < 0) {
fprintf(stderr, "Error\n");
exit(-1);
}
// 3. listen() = listen for connections
int lrt = listen(rfd, 5);
if (lrt < 0) {
fprintf(stderr, "Error\n");
exit(-1);
}
while (1) {
client_len = sizeof(client);
cfd = accept(rfd, (struct sockaddr*)&client, &client_len);
if (cfd < 0) {
fprintf(stderr, "accept failed with error %d\n", WSAGetLastError());
exit(-1);
}
printf("Client connected\n");
while (1) {
/*
// Send prompt to client
char* prompt = "> ";
if (send(cfd, prompt, strlen(prompt), 0) <= 0) {
fprintf(stderr, "send() failed with error %d\n", WSAGetLastError());
exit(1);
}
*/
// read a line from a socket (Client's data)
int bytes_idx = -1;
while (1) {
if (bytes_idx >= (int)sizeof(in)) {
fprintf(stderr, "input buffer overflow\n");
break;
}
// Receive on byte (character) at a time
bytes_read = recv(cfd, &in[++bytes_idx], 1, 0);
if (bytes_read <= 0) // Check error or no data read
break;
/*
printf("sending back the %d bytes I received...\n", bytes_read);
if (send(cfd, &in[bytes_idx], 1, 0) <= 0) {
fprintf(stderr, "send() failed with error %d\n", WSAGetLastError());
exit(1);
}
*/
if (in[bytes_idx] == '\n') {
// Received a complete line, including CRLF
// Remove ending CR
bytes_idx--;
if ((bytes_idx >= 0) && (in[bytes_idx] == '\r'))
in[bytes_idx] = 0;
break;
}
}
if (bytes_idx > 0) { // Check for empty line
printf("Received \"%s\"\n", in);
// Check for client command
if (stricmp(in, "exit") == 0)
break;
else {
printf("Client sent unknown command\n");
}
}
}
closesocket(cfd);
printf("Client disconnected\n");
}
closesocket(rfd);
#ifdef WIN32
WSACleanup();
#endif
}
Both programs compile, and I'm able to successfully create a socket, but the connection to the server fails. This is basically a TCP echo program.
PS. I'm new here so IDK how to use this, I don't have much programming experience so bare with me.
tcp echo client-1
tcp echo client-2
tcp echo server-1
tcp echo server-2
Compiling/Running server gives me: Server not fully implemented...
Compiling/Running client gives me: Socket successfully created..Error: connection to the server failed!
**// TCP echo client program**
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main (int argc, char* argv[ ]) // Three arguments to be checked later
{
struct sockaddr_in servAddr; // Server socket address data structure
char *servIP = argv[1]; // Server IP address from command line
int servPort = atoi(argv[2]); // Server port number from command line
char *message = argv[3]; // Message specified on the command line
char buffer [512 + 1];
char* ptr = buffer;
int len;
int max_len = sizeof(buffer);
int sock_descrip;
// Check for correct number of command line arguments
if(argc != 4) {
printf("tcp-echo-client [IP address] [Port] [Message]\n");
exit (1);
}
// Populate socket address for the server
memset (&servAddr, 0, sizeof(servAddr)); // Initialize data structure
servAddr.sin_family = AF_INET; // This is an IPv4 address
servAddr.sin_addr.s_addr = inet_addr(servIP); // Server IP address
servAddr.sin_port = servPort; // Server port number
// Create a TCP socket stream
int sock;
if ((sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
printf("Error: socket creation failed!\n");
exit (1);
}
else
printf("Socket successfully created..\n");
// Connect to the server
if ((connect (sock, (struct sockaddr*)&servAddr, sizeof(servAddr))) == -1) {
printf("Error: connection to the server failed!\n");
exit (1);
}
else
printf("Connected to the server..\n");
// Send data to the server...
send(sock_descrip, message, strlen(message),0);
int x;
while ((x = recv(sock_descrip, ptr, max_len,0))>0)
{
ptr += x;
max_len -= x;
len += x;
}
buffer[len] = '\0';
printf("Echoed string received: %s %c", buffer,*message);
// Receive data back from the server..
// Loop while receiving data...
// print data...
// end-while loop
// Close socket
close (sock);
// Stop program
exit (0);
} // End main
**//TCP Echo server program**
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFLEN 512 // Maximum length of buffer
#define PORT 9988 // Fixed server port number
int main (void)
{
struct sockaddr_in server_address; // Data structure for server address
struct sockaddr_in client_address; // Data structure for client address
int client_address_len = 0;
char buffer [512];
char* ptr = buffer;
int len;
int max_len = BUFLEN;
int sock_descrip;
// Populate socket address for the server
memset (&server_address, 0, sizeof (server_address)); // Initialize server address data structure
server_address.sin_family = AF_INET; // Populate family field - IPV4 protocol
server_address.sin_port = PORT; // Set port number
server_address.sin_addr.s_addr = INADDR_ANY; // Set IP address to IPv4 value for loacalhost
// Create a TCP socket; returns -1 on failure
int listen_sock;
if ((listen_sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
printf("Error: Listen socket failed!\n");
exit (1);
}
// Bind the socket to the server address; returns -1 on failure
if ((bind(listen_sock, (struct sockaddr *)&server_address, sizeof (server_address))) == -1) {
printf("Error: binding failed!\n");
exit (1);
}
printf("Server not fully implemented...\n");
// Listen for connections...
int wait_size;
if (listen(listen_sock, wait_size) == -1)
{
printf("Error: listening failed!\n");
exit(1);
}
for(;;)
{
if(sock_descrip=accept(listen_sock,(struct sockaddr *)&client_address, &client_address_len) == -1)
{
printf("Error: accepting failed!\n");
exit(1);
}
int x;
while ((x = recv(sock_descrip, ptr, max_len, 0)) > 0)
{
ptr += x;
max_len -= x;
len += x;
}
send(sock_descrip,buffer,len,0);
}
// Echo data back to the client...
close (listen_sock); // Close descriptor referencing server socket
} // End main
You need parentheses around the assignment:
if(
(sock_descrip=accept(listen_sock,(struct sockaddr *)&client_address, &client_address_len))
== -1)
I have to insert this code:
time_t ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
to change the message displayed when someone connect to the server, from "Hello student!\n" to the current time and date, but I don't know where copy those two lines of code in the program and what I have to modify in the code after copying those two lines.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
const char MESSAGE[] = "Hello student!\n";
int main(int argc, char *argv[]) {
int simpleSocket = 0;
int simplePort = 0;
int returnStatus = 0;
struct sockaddr_in simpleServer;
if (argc != 2) {
fprintf(stderr, "Usage: %s <port>\n", argv[0]);
exit(1);
}
simpleSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (simpleSocket == -1) {
fprintf(stderr, "Could not create a socket!\n");
exit(1);
}
else {
fprintf(stderr, "Socket created!\n");
}
/* retrieve the port number for listening */
simplePort = atoi(argv[1]);
/* setup the address structure */
/* use INADDR_ANY to bind to all local addresses */
memset(&simpleServer, '\0', sizeof(simpleServer));
simpleServer.sin_family = AF_INET;
simpleServer.sin_addr.s_addr = htonl(INADDR_ANY);
simpleServer.sin_port = htons(simplePort);
/* bind to the address and port with our socket */
returnStatus = bind(simpleSocket,(struct sockaddr *)&simpleServer,sizeof(simpleServer));
if (returnStatus == 0) {
fprintf(stderr, "Bind completed!\n");
}
else {
fprintf(stderr, "Could not bind to address!\n");
close(simpleSocket);
exit(1);
}
/* lets listen on the socket for connections */
returnStatus = listen(simpleSocket, 5);
if (returnStatus == -1) {
fprintf(stderr, "Cannot listen on socket!\n");
close(simpleSocket);
exit(1);
}
while (1)
{
struct sockaddr_in clientName = { 0 };
int simpleChildSocket = 0;
int clientNameLength = sizeof(clientName);
/* wait here */
simpleChildSocket = accept(simpleSocket,(struct sockaddr *)&clientName, &clientNameLength);
if (simpleChildSocket == -1) {
fprintf(stderr, "Cannot accept connections!\n");
close(simpleSocket);
exit(1);
}
/* handle the new connection request */
/* write out our message to the client */
write(simpleChildSocket, MESSAGE, strlen(MESSAGE));
close(simpleChildSocket);
}
close(simpleSocket);
return 0;
}
Thank you for your answers
A little introduction: about the original program
The program starts creating a socket and setting it to listen to a specific port, passed as an argument to your program with a command line such as programName <port>.
The port number is retrieved with the line simplePort = atoi(argv[1]);. There could have been a stricter check on the parameter (atoi() doesn't check if a number is actually provided), but I suppose it is ok for an entry level educational program.
After that, with the line
simpleChildSocket = accept(simpleSocket,(struct sockaddr *)&clientName, &clientNameLength);
the accept() function blocks until a connection request from a TCP client is received. As soon as the TCP handshake is completed (SYN / SYN-ACK / ACK, it is
called three-way handshake) a socket handle is returned (in your case simpleChildSocket) and that can be used to exchange data with the client.
The welcome message
After the accept is completed, and we are sure that all went fine, we soon come to our welcome message. With the lines
/* write out our message to the client */
write(simpleChildSocket, MESSAGE, strlen(MESSAGE));
the characters contained in the MESSAGE string (defined with the constant const char MESSAGE[] = "Hello student!\n";) are sent through write() function. Its parameters are
the socket descriptor
the pointer to the buffer to be sent
the number of bytes to be sent (in this case it corresponds to the length of MESSAGE, calculated with strlen(MESSAGE)
Note: write function can actually be used, but it is an unconventional choice. In fact it is a general function but when writing to sockets send() is actually used.
How to achieve you goal
All you have to do is to substitute MESSAGE in the write() call with the string containing the date:
{
char buff[30+1];
time_t ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.30s\r\n", ctime(&ticks));
/* write out our message to the client */
write( simpleChildSocket, buff, strlen(buff) );
}
Don't forget to define your buff character buffer. I defined it locally but you can also allocate it dynamically.
The code provided by your teacher copies the time calculate with ctime() in the buff array (I increased its size in order to make sure that the whole datetime can be contained in it).
Then we call write as we previously did, just substituting MESSAGE and strlen(MESSAGE) with the new string buff and strlen(buff).
I have solved
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
/*const*/ char MESSAGE[100] = "";
char buff[100];
int main(int argc, char *argv[]) {
int simpleSocket = 0;
int simplePort = 0;
int returnStatus = 0;
struct sockaddr_in simpleServer;
if (argc != 2) {
fprintf(stderr, "Usage: %s <port>\n", argv[0]);
exit(1);
}
simpleSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (simpleSocket == -1) {
fprintf(stderr, "Could not create a socket!\n");
exit(1);
}
else {
fprintf(stderr, "Socket created!\n");
}
/* retrieve the port number for listening */
simplePort = atoi(argv[1]);
/* setup the address structure */
/* use INADDR_ANY to bind to all local addresses */
memset(&simpleServer, '\0', sizeof(simpleServer));
simpleServer.sin_family = AF_INET;
simpleServer.sin_addr.s_addr = htonl(INADDR_ANY);
simpleServer.sin_port = htons(simplePort);
/* bind to the address and port with our socket */
returnStatus = bind(simpleSocket,(struct sockaddr *)&simpleServer,sizeof(simpleServer));
if (returnStatus == 0) {
fprintf(stderr, "Bind completed!\n");
}
else {
fprintf(stderr, "Could not bind to address!\n");
close(simpleSocket);
exit(1);
}
/* lets listen on the socket for connections */
returnStatus = listen(simpleSocket, 5);
if (returnStatus == -1) {
fprintf(stderr, "Cannot listen on socket!\n");
close(simpleSocket);
exit(1);
}
while (1)
{
struct sockaddr_in clientName = { 0 };
int simpleChildSocket = 0;
int clientNameLength = sizeof(clientName);
/* wait here */
simpleChildSocket = accept(simpleSocket,(struct sockaddr *)&clientName, &clientNameLength);
if (simpleChildSocket == -1) {
fprintf(stderr, "Cannot accept connections!\n");
close(simpleSocket);
exit(1);
}
/* handle the new connection request */
/* write out our message to the client */
time_t ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
strcpy(MESSAGE, buff);
write(simpleChildSocket, MESSAGE, strlen(MESSAGE));
close(simpleChildSocket);
}
close(simpleSocket);
return 0;
}
I am having a small problem when trying to implement a client-server program with multithreading on the side of the server. My idea is to have the server spin forever, accept a client when it can, and just send it to a client_handle() function using a thread.
Here is the problem: my server and client are using the code seen below. At the point of the initial response of the server, it fails in sending ALL_GOOD_CD. I'm not sure why this is happening, as I print out the socket fd of the client in a line before and it seems to match up with the file descriptor given to us at the time of acception.
One thought is that my socket id is not being passed to the thread correctly. My client never seems to receive the ALL_GOOD_CD (it is blocking on a recv() call after connecting with the server). I am new with threads and sockets, anything would help; thanks!
Here is the client code needed to run:
#include <stdio.h> /* for printf() and fprintf() */
#include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
#include <arpa/inet.h> /* for sockaddr_in and inet_addr() */
#include <stdlib.h> /* for atoi() and exit() */
#include <string.h> /* for memset() */
#include <unistd.h> /* for close() */
void DieWithError(char *errorMessage); /* Error handling function */
int main(int argc, char *argv[])
{
int sock; /* Socket descriptor */
struct sockaddr_in server_addr; /* Server address */
unsigned short server_port; /* Server port */
char *server_ip; /* Server IP address (dotted quad) */
char server_response[300]; /* Buffer to hold response from the server */
char* username;
/* Test for correct number of arguments */
if (argc != 4) {
fprintf(stderr, "Usage: %s <server_ip> <server_port> <username>\n", argv[0]);
exit(1);
}
server_ip = argv[1]; /* Second arg: server IP address (dotted quad) */
server_port = atoi(argv[2]); /* Third arg: server port number */
username = argv[3]; /* Fourth arg: username */
/* Create a reliable, stream socket using TCP */
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
DieWithError("socket() failed");
/* Construct the server address structure */
memset(&server_addr, 0, sizeof(server_addr)); /* Zero out structure */
server_addr.sin_family = AF_INET; /* Internet address family */
server_addr.sin_addr.s_addr = inet_addr(server_ip); /* Server IP address */
server_addr.sin_port = htons(server_port); /* Server port */
/* Establish the connection to the server */
if (connect(sock, (struct sockaddr*) &server_addr, sizeof(server_addr)) < 0)
DieWithError("connect() failed, could not find server.");
printf("connected\n");
memset(&server_response, 0, 300);
if (recv(sock, server_response, 300, 0) < 0)
DieWithError("recv() for initial response failed");
printf("received initial reponse\n");
}
void DieWithError(char* errorMessage) {
fprintf(stderr, "Dying with error sadface: %s\n", errorMessage);
exit(1);
}
Here is the server code as minified as possible:
#include <stdio.h> /* for printf() and fprintf() */
#include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
#include <arpa/inet.h> /* for sockaddr_in and inet_addr() */
#include <stdlib.h> /* for atoi() and exit() */
#include <string.h> /* for memset() */
#include <unistd.h> /* for close() */
#include <pthread.h> /* multithreading the clients! */
#define MAXMSGSIZE 150
#define MAXCLIENTS 5
#define TOO_MANY_CD 0
#define ALL_GOOD_CD 1
#define OTHER_BAD_CD 2
struct client {
char* username;
char** subs;
int socket;
char temp_msg[MAXMSGSIZE*2];
};
void DieWithError(char* errorMessage); /* Error handling function */
void handle_client(void* new_socket); /* Client handling function */
static struct client** clients;
static pthread_t* threads;
static pthread_mutex_t clients_mutex;
static pthread_mutex_t threads_mutex;
int main(int argc, char *argv[])
{
int server_sock; /* Server socket descriptor */
unsigned short server_port; /* Echo server port */
struct sockaddr_in server_addr; /* sockaddr_in struct to hold information about the server */
int server_addr_size; /* Size of server_addr struct in bytes */
int client_sock;
int empty_thread;
pthread_attr_t thread_attr;
if (argc != 2) { /* Test for correct number of arguments */
fprintf(stderr, "Usage: %s <server_port>\n", argv[0]);
exit(1);
}
clients = (struct client**) calloc(1, sizeof(struct client*) * MAXCLIENTS);
if (clients == NULL)
DieWithError("calloc() for clients failed");
threads = (pthread_t*) calloc(1, sizeof(pthread_t) * MAXCLIENTS);
if (clients == NULL)
DieWithError("calloc() for clients failed");
pthread_mutex_init(&clients_mutex, NULL);
pthread_mutex_init(&threads_mutex, NULL);
pthread_attr_init(&thread_attr);
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
server_port = atoi(argv[1]);
/* Create a reliable, stream socket using TCP */
if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
DieWithError("socket() failed");
// Zero out server_addr var and fill with information
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(server_port);
// Bind server with sock, IP, and port so that the clients can connect to us
if (bind(server_sock, (struct sockaddr*) &server_addr, sizeof(server_addr)) < 0)
DieWithError("bind() failed");
// Allow this server to accept 5 clients at a time (queue has 0 capacity because we multithread)
if (listen(server_sock, 0) < 0)
DieWithError("listen() failed");
// Display some information so we can connect with client
printf("Using\n\tport: %d\n\tIP: %s\n", server_port, inet_ntoa(server_addr.sin_addr));
server_addr_size = sizeof(server_addr);
for (;;) {
int* new_socket = (int*)malloc(sizeof(int));
if ((*new_socket = accept(server_sock,
(struct sockaddr*) &server_addr, &server_addr_size)) < 0) {
printf("accept() failed");
continue;
}
int free_spot = -1;
for (int i = 0; i < MAXCLIENTS; i++)
if (!threads[i]) free_spot = i;
if (free_spot == -1) {
printf("no empty threads (max clients handled currently)");
send(*new_socket,(void*) OTHER_BAD_CD, sizeof(OTHER_BAD_CD), 0);
close(*new_socket);
continue;
}
if (pthread_create(&threads[free_spot], &thread_attr,
(void*) &handle_client, (void*) new_socket)) {
printf("pthread_create failed");
close(*new_socket);
continue;
}
printf("sent new client %d to handle_client()\n", *new_socket);
}
}
void handle_client(void* new_socket) {
int socket = *(int*)new_socket;
free(new_socket);
printf("handling new client %d\n", socket);
struct client* curr_cl;
pthread_mutex_lock(&clients_mutex);
printf("locked mutex?\n");
if (send(socket, (void*)ALL_GOOD_CD, sizeof(ALL_GOOD_CD), 0) < 0) {
printf("inital all good resp failed");
send(socket, (void*)OTHER_BAD_CD, sizeof(OTHER_BAD_CD), 0);
return;
}
printf("sent stuff\n");
int free_spot = -1;
for (int i = 0; i < MAXCLIENTS; i++)
if (!clients[i]) free_spot = i;
printf("filtered through clients and got free spot %d\n", free_spot);
if (free_spot == -1) {
printf("didn't find free spot :(\n");
send(socket, (void*)TOO_MANY_CD, sizeof(TOO_MANY_CD), 0);
pthread_mutex_unlock(&clients_mutex);
return;
}
printf("found free spot %d for client %d", free_spot, socket);
clients[free_spot] = (struct client*) calloc(1, sizeof(struct client));
if (clients[free_spot] == NULL) {
send(socket, (void*)OTHER_BAD_CD, sizeof(OTHER_BAD_CD), 0);
pthread_mutex_unlock(&clients_mutex);
return;
}
curr_cl = clients[free_spot];
if (recv(socket, curr_cl->username, sizeof(curr_cl->username), 0) < 0) {
send(socket, (void*)OTHER_BAD_CD, sizeof(OTHER_BAD_CD), 0);
pthread_mutex_unlock(&clients_mutex);
return;
}
// Subscribe client to #ALL automatically
curr_cl->subs[0] = "#ALL";
if (send(socket, (void*)ALL_GOOD_CD, sizeof(ALL_GOOD_CD), 0) < 0) {
printf("send() for final all good failed\n");
send(socket, (void*)OTHER_BAD_CD, sizeof(OTHER_BAD_CD), 0);
pthread_mutex_unlock(&clients_mutex);
return;
}
printf("\taccepted new client %s and now listening\n", curr_cl->username);
pthread_mutex_unlock(&clients_mutex);
return;
}
void DieWithError(char* errorMessage) {
fprintf(stderr, "Dying with error sadface: %s\n", errorMessage);
exit(1);
}
Here is the Makefile
# the compiler: gcc for C
CC = gcc
# compiler flags
CFLAGS = -g
make: ttweetcl.c ttweetsrv.c
gcc -o ttweetcli ttweetcl.c && gcc -o ttweetsrv ttweetsrv.c -lpthread
.PHONY: clean
clean:
rm -f ./ttweetcli ./ttweetsrv
Solved! One comment (now removed) noticed that I was not ending my printf()'s with a \n and therefore was not flushing the buffer. Now that I have added all \n's then the code executes as it should.
I'm tyring to create a server client connection but for some reason when I run the server code, it'll just give me the prompt, nothing happens. I put a printf as the first thing in the main function but even that doesn't happen. What could be executed before the main? I don't think I have anything that could cause a problem.
#include "md5.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>
void *handle_connection (void *); //gets a connection identifier and handles that incoming connection
int verify_password (char s[]); // verifies if the input password is correct
int main(int argc, char *argv[])
{
pthread_t connected;
pthread_attr_t attributes;
int listenfd = 0, conn_id = 0; // the listening socket identifier and current connection identifier
//A: Creates the master socket
listenfd = socket (PF_INET, SOCK_STREAM, 0);
//B: the struct variable that keeps the socket address
struct sockaddr_in sad; /* structure to hold an IP address */
struct sockaddr_in cad;
struct hostent *ptrh; /* pointer to a host table entry */
socklen_t alen;
//B: initialization of socket address variable
memset ((char *)&sad, 0, sizeof(sad)); /* clear sockaddr structure */
sad.sin_family = AF_INET; /* set family to Internet */
sad.sin_addr.s_addr = INADDR_ANY; /* set the local IP address */
sad.sin_port = htons((u_short) 5000); /* set the port number */
//C: binds the socket to its address
bind (listenfd, (struct sockaddr *)&sad, sizeof(sad));
//D: starts listening on that socket
listen (listenfd, 100000);
while (1) // listens for incoming connections until program exits manually
{
int conn_id = 0;
//E: Wait for a connection request and accept it
conn_id = accept (listenfd, (struct sockaddr *)&cad, &alen);
//F: if the connection has been established it creates a thread that handles the connection
if (conn_id != -1)
{
pthread_attr_init (&attributes);
pthread_attr_setdetachstate (&attributes, PTHREAD_CREATE_DETACHED);
pthread_create (&connected, &attributes, (void *) handle_connection, &conn_id);
}
}
}
void *handle_connection(void *arg)
{
int conn_id = *(int *) arg;
char recv_buff[1024]; // the buffer that is used for keeping the data read from the socket
int n = 0; // number of characters read from socket
//G: read from connection into the buffer and checks if recieving data was successful
if (n = recv (conn_id, recv_buff, 1024, 0) <= 0)
{
printf ("Error Reading");
}
//H: verifies if the password is correct and sends T if the password is correct and F otherwise
if (verify_password (recv_buff) == 1)
{
if (send (conn_id, "T", 1, 0) < 1) {
printf ("Error Sending");
}
} else {
if (send (conn_id, "F", 1, 0) < 1) {
printf ("Error Sending");
}
}
//I: closes the connection
close (conn_id);
}
int verify_password(char s[])
{
char md5_hashed_password[33] ="24af484e92082ad450a63a69f924f10a"; // The password of server hashed by MD5
char md5_hashed_text[33]; // Keeps the MD5 hashed text of input text
size_t len = strlen(s);
md5 ((uint8_t*)s, len, md5_hashed_text); //Finds the MD5 hashed of input text
if (strncmp (md5_hashed_text, md5_hashed_password, 32) == 0) //If the hashed value of the input value and the
return 1; // password is same the password is accepted
return 0;
}