Passing chars from structs through sockets in c - c

I am trying to send/receive messages between my server and client. I currently have a stuct in my server that holds a char value. I am trying to pass this value to my client. See the following:
tileInfo->tiles[user_cords_x][user_cords_y].identifier = '+'; // Char i want to pass
write_client_msg(cli_sockfd, &tileInfo->tiles[user_cords_x][user_cords_y].identifier);
/* Writes a message to a client socket. */
void write_client_msg(int cli_sockfd, char * msg)
{
int n = write(cli_sockfd, msg, strlen(msg));
if (n < 0)
error("ERROR writing msg to client socket");
}
On my client side i am receiving it with the following:
char *msg;
char identity = read(sockfd, msg, 1);
printf("This is the value: %d \n", identity);
Currently the output i am getting is This is the value: 1. I am new to sockets and dont fully understand passing chars. Could someone please explain and show me how to pass the '+' to my client side?

You are making several mistakes with your code.
On the server side, in this code:
write_client_msg(cli_sockfd, &tileInfo->tiles[user_cords_x][user_cords_y].identifier);
It is clear that identifier is a single char, or else the code would not compile. In which case, using strlen() inside of write_client_msg() is wrong, since msg will not have a null terminator when passing in a pointer to a single char. You would end up transmitting garbage to the other party, if not just crash altogether from accessing invalid memory.
Then, on the client side, you are passing an uninitialized msg pointer to read(). But also, you are displaying read()'s return value, which is the number of bytes actually received. That is why your display is showing 1, since you are asking for 1 byte to be received.
To send and receive the identifier correctly, you would need something more like this instead:
void write_client_msg(int sockfd, char *msg, size_t msg_len)
{
int n = write(sockfd, msg, msg_len);
if (n < 0)
error("ERROR writing msg to client socket");
}
...
write_client_msg(cli_sockfd, &tileInfo->tiles[user_cords_x][user_cords_y].identifier, 1);
char identity;
int n = read(sockfd, &identity, 1);
if (n < 0)
error("ERROR reading identity from client socket");
else if (n == 0)
error("DISCONNECTED while reading identity from client socket");
else
printf("This is the value: %c \n", identity);
That being said, a better solution is to make write_client_msg() send the msg_len before sending the msg. Especially since write_client_msg() doesn't know what kind of data it is sending. Then the client can read the msg_len to know how many bytes to read for the msg, and then process the msg as needed.
Also, write() and read() can return fewer bytes than requested, so you need to call them in loops to make sure you actually send/receive everything.
For example:
int write_all(int sockfd, void *data, size_t data_len)
{
char *d = (char*) data;
while (data_len > 0)
{
int n = write(sockfd, d, data_len);
if (n < 0)
return n;
d += n;
data_len -= n;
}
return 1;
}
void write_client_msg(int sockfd, char *msg, size_t msg_len)
{
uint32_t len = htonl(msg_len);
int n = write_all(sockfd, &len, sizeof(len));
if (n == 1)
n = write_all(sockfd, msg, msg_len);
if (n < 0)
error("ERROR writing msg to client socket");
}
...
write_client_msg(cli_sockfd, &tileInfo->tiles[user_cords_x][user_cords_y].identifier, 1);
int read_all(int sockfd, void *data, size_t data_len)
{
char *d = (char*) data;
while (data_len > 0)
{
int n = read(sockfd, d, data_len);
if (n <= 0)
return n;
d += n;
data_len -= n;
}
return 1;
}
char *read_server_msg(int sockfd)
{
uint32_t len;
int n = read_all(sockfd, &len, sizeof(len));
if (n <= 0)
return NULL;
len = ntohl(len);
char *msg = malloc(len+1);
if (!msg)
return NULL;
n = read_all(sockfd, msg, len);
if (n <= 0) {
free(msg);
return NULL;
}
msg[len] = '\0';
return msg;
}
...
char *msg = read_server_msg(sockfd);
if (!msg)
error("ERROR reading msg from client socket");
else {
printf("This is the value: %s \n", msg);
free(msg);
}

The proper solution (based on your code) would be:
char *msg;
msg = malloc(2);
if (read(sockfd, msg, 1)!=1) {
printf("Something wrong\n");
return 0;
}
msg[1]= '\0';
printf("Sent received: %s \n", msg);

Solved:
char *msg;
msg = (char*) malloc (1);
read(sockfd, msg, 1);
printf("Sent received: %s \n", msg);

Related

How to receive long array of integers with TCP socket in C?

How can I send and receive a long array of integers using TCP sockets?
In the case of short array, the reception is possible using the function recv(.., 4*size of array) one time, however when the size of array is too long, I can't receive data correctly.
int main(void)
{
int listenfd = 0, connfd = 0;
int i,j,x;
int fd;
unsigned int *pic;
struct sockaddr_in serv_addr;
char *recvBuff;
clock_t t;
//Allocate memory for a 24-bit 640x480 rgb image
pic = (int*)malloc(10*sizeof(int));
recvBuff = (char*)malloc(1*sizeof(char));
for(i = 0; i < 10 ; i++){
pic[i] = 20;
}
//Create the TCP socket
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
memset(pic, '0', sizeof(pic));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(7); // la valeur du port
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(listenfd, 10);
//fprintf(stdout,"End Creating Socket4\n");
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
while(1)
{
recv(connfd,recvBuff, sizeof(char),MSG_WAITALL);
//printf("BUFF: %s\n",recvBuff);
//Wait for client request
if(strcmp(recvBuff,"A")){
printf("Error in input\n");
}else
write(connfd, pic, 921600);
}
close(connfd);
}
In the case of short array, the reception is possible using the function recv(.., 4*size of array) one time
That is not guaranteed. Any call to recv() can return fewer bytes than requested, even as few as just 1 byte. So you always need to call recv() in a loop until you have actually received as many bytes as are you expecting, eg:
ssize_t recv_all(int skt, void *buf, size_t bufsize)
{
char *ptr = (char*) buf;
while (bufsize > 0)
{
ssize_t recvd = recv(skt, ptr, bufsize, 0);
if (recvd <= 0) return recvd;
ptr += recvd;
bufsize -= recvd;
}
return 1;
}
And then you can call it like this:
int32_t *arr = (int32_t*) malloc(sizeof(int32_t) * count);
...
if (recv_all(..., arr, sizeof(int32_t) * count) <= 0)
{
// error, or peer disconnected...
}
else
{
// use arr as needed...
}
You should do the same thing for send() too, eg:
ssize_t send_all(int skt, const void *buf, size_t bufsize)
{
const char *ptr = (const char*) buf;
while (bufsize > 0)
{
ssize_t sent = send(skt, ptr, bufsize, 0);
if (sent < 0) return sent;
ptr += sent;
bufsize -= sent;
}
return 0;
}
int32_t *arr = (int32_t*) malloc(sizeof(int32_t) * count);
...
if (send_all(..., arr, sizeof(int32_t) * count) < 0)
{
// error...
}
else
{
// arr sent in full...
}
First of all, it's worth noting that recv() doesn't care at all about the type of data it receives, only about its size.
So all you need is a function that calls recv() in a loop until it receives all of the requested data
// Returns the number of bytes read, or -1 in the case of an error
ssize_t recv_all(const int sock, void * const buf, const size_t n)
{
ssize_t len = 0;
ssize_t total_read = 0;
while (n > (size_t)total_read)
{
len = recv(sock, (char *)buf + total_read, n - total_read, MSG_WAITALL);
switch (len)
{
case -1:
return -1;
case 0:
break;
default:
total_read += len;
}
}
return total_read;
}

read() not reading the remaining bytes on a socket buffer

I have created two programs, a client and a server. They communicate via sockets sending char arrays of a fixed size of 115 bytes.
The data I want to transfer is stored in the following struct:
typedef struct {
char origin[14];
char type;
char data[100];
} socket_data;
But in order to send the data serialized, I want to send that information in a single string concatenating al the fields in the struct so I send a 115 bytes string. If any of those fields does not reach it's max size, I will manually fill the extra array positions with \0.
I have created two functions implemented in both client and server that send data through the socket or receive data from the socket.
The two functions are the following:
void socket_send(int socket, char *origin, char type, char *data) {
char info[115]; //data to be sent
socket_data aux;
strcpy(aux.origin, origin);
aux.type = type;
strcpy(aux.data, data);
//Filling up the remaining positions of origin and data variables
for (int i = (int) strlen(aux.origin); i<14; i++) aux.origin[i] = '\0';
for (int i = (int) strlen(aux.data); i<100; i++) aux.data[i] = '\0';
//Building up the 115 byte string I want to send via socket
for (int i=0; i<14; i++) info[i] = aux.origin[i];
info[14] = type;
for (int i=0; i<100; i++) info[i+15] = aux.data[i];
ssize_t total_bytes = 115;
ssize_t bytes_written = 0;
//Here I send all the bytes through the socket
do {
bytes_written = write(socket, info + (115 - total_bytes), total_bytes);
total_bytes -= bytes_written;
} while (total_bytes > 0);
}
socket_data socket_rcv(int socket) {
socket_data info;
char sequence[115];
ssize_t total_bytes = 115;
ssize_t bytes_read = 0;
//Here I receive all the bytes from the socket (till I fill up the 115 byte string called sequence)
do {
bytes_read = read(socket, sequence + (115 - total_bytes), total_bytes);
total_bytes -= bytes_read;
} while (total_bytes > 0);
//Then I return a stuct
for (int i=0; i<14; i++) info.origin[i] = sequence[i];
info.type = sequence[14];
for (int i=0; i<100; i++) info.data[i] = sequence[i+15];
return info;
}
As you can see, I loop both read() and write() to make sure all bytes are sent as I'm aware sometimes those functions read or write less bytes than demanded.
The issue is that, testing the functionality of the program, I have seen that in the case that less bytes are read (it loops), the program blocks (maybe waiting for another write() from the server side) instead of reading the remaining bytes in the socket buffer (because all 115 bytes where sent and only 111 received, so there should be still 4 bytes in the socket buffer). Sometimes also, instead of blocking waiting for a possible write(), the program terminates when it shouldn't...
I can't find the issue here and I'd appreciate some help
EDIT
I created this functions to set up the sockets...
Server:
int socketConfig (connection_info cinfo) {
int socketfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socketfd < 0) {
write(1, "Socket error\n", strlen("Socket error\n"));
return -1;
}
struct sockaddr_in s_addr;
memset (&s_addr, 0, sizeof (s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(cinfo.port);
s_addr.sin_addr.s_addr = INADDR_ANY;
if (bind (socketfd, (void *) &s_addr, sizeof (s_addr)) < 0) {
write(1, "Bind error\n", strlen("Bind error\n"));
return -1;
}
listen(socketfd, 3);
return socketfd;
}
int receiveClient(int serverfd) {
struct sockaddr_in client;
socklen_t len = sizeof(client);
return accept(serverfd, (void *) &client, &len);
}
Client:
int connect_to_server(Config config) {
struct sockaddr_in client;
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
write(1, "Connecting Jack...\n", strlen("Connecting Jack...\n"));
if (sockfd < 0) {
write(1, "Error creating the socket\n", strlen("Error creating the socket\n"));
return -1;
}
memset(&client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(config.port_jack);
if (inet_aton(config.ip_jack, &client.sin_addr) == 0) {
write(1, "Invalid IP address\n", strlen("Invalid IP address\n"));
return -1;
}
if (connect(sockfd, (void *) &client, sizeof(client)) < 0) {
write(1, "Error connecting to Jack\n", strlen("Error connecting to Jack\n"));
return -1;
}
return sockfd;
}
I can guarantee the connection works
You are not checking the return values of write() and read() for failures.
Try something more like this:
int socket_send_all(int socket, const void *data, size_t size) {
const char *pdata = (const char*) data;
ssize_t bytes_written;
while (size > 0) {
bytes_written = write(socket, pdata, size);
if (bytes_written < 0) return bytes_written;
pdata += bytes_written;
size -= bytes_written;
}
return 0;
}
int socket_rcv_all(int socket, void *data, size_t size) {
char *pdata = (char*) data;
ssize_t bytes_read;
while (size > 0) {
bytes_read = read(socket, pdata, size);
if (bytes_read <= 0) return bytes_read;
pdata += bytes_read;
size -= bytes_read;
}
return 1;
}
int socket_send2(int socket, const socket_data *sd) {
char bytes[115];
memcpy(bytes, sd->origin, 14);
bytes[14] = sd->type;
memcpy(bytes+15, sd->data, 100);
return socket_send_all(socket, bytes, 115);
/* alternatively:
int ret = socket_send_all(socket, sd->origin, 14);
if (ret == 0) ret = socket_send_all(socket, &(sd->type), 1);
if (ret == 0) ret = socket_send_all(socket, sd->data, 100);
return ret;
*/
}
int socket_send(int socket, char *origin, char type, char *data) {
socket_data aux;
strncpy(aux.origin, origin, 14);
aux.type = type;
strncpy(aux.data, data, 100);
return socket_send2(socket, &aux);
}
int socket_rcv2(int socket, socket_data *sd) {
char bytes[115];
int ret = socket_rcv_all(socket, bytes, 115);
if (ret > 0) {
memcpy(sd->origin, bytes, 14);
sd->type = bytes[14];
memcpy(sd->data, bytes+15, 100);
}
return ret;
/* alternatively:
int ret = socket_rcv_all(socket, sd->origin, 14);
if (ret > 0) ret = socket_rcv_all(socket, &(sd->type), 1);
if (ret > 0) ret = socket_rcv_all(socket, sd->data, 100);
return ret;
*/
}
socket_data socket_rcv(int socket) {
socket_data aux;
int ret = socket_rcv2(socket, &aux);
if (ret <= 0) {
// error handling ...
}
return aux;
}
read() returns as many bytes as it wants. Perchance the sent output went out in two packets. Perhaps something else (memory alignment comes to mind). Always handle short reads by trying to read more or have a headache. In addition, write() only writes as many byte as it wants. A short write is usually a full buffer or split by a signal, but stranger things have been observed.
You need to check for errors every time around the loop. Your program as written will trash memory otherwise.

Why is my client not receiving any bytes?

I am writing a simple client-server program. My server works fine with telnet, but my client can only send bytes, not receive them. Is there something obvious I'm missing? I think it may be because of my usage of the recv function in some way but it's not evident to me. The recv function consistently gets 0 bytes. I should add that the recv function doesn't block, yet the received number of bytes is always zero.
A snippet of the code in the client's main function is below. The function changeIPOrPort sets the servers ip address and port and works fine since the server receives the clients message. I can post the server code as well if necessary.
int main() {
int client_fd, numbytes = 0;
//int quit = 0;
struct sockaddr_in their_addr;
char response[MAXDATASIZE] = "";
char buf[MAXDATASIZE] = "";
size_t len = 0;
/* Server config*/
their_addr.sin_family = AF_INET;
memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
printf("Configuring the server\n");
changeIPOrPort(&their_addr);
printf("Hello! please input letters to know things about me.\n");
printf("Each character will give you a piece of information about me\n");
printf("%s\n", serviceInfo); //info string "UI"...
/*create client socket*/
client_fd = socket(AF_INET, SOCK_STREAM, 0);
if(connect(client_fd, (struct sockaddr *)&their_addr, sizeof(their_addr)) < 0) {
printf("\n Error : Connect Failed \n");
return 1;
}
if(!fgets(response, sizeof (response), stdin)) {
printf("Error reading line.\n");
}
else {
len = strlen(response);
if(response[len - 1] == '\n') {
response[len - 1] = '\0';
}
printf("You entered the characters:%s\n", response);
printf("sending...\n");
send(client_fd, response, len, 0);
numbytes = recv(client_fd, buf, strlen(buf), 0) ;
if(numbytes < 0) {
printf("Error receiving from server, quitting\n");
exit(1);
}
else {
printf("Number of bytes received: %d\n", numbytes);
buf[numbytes] = '\0';
printf("%s\n", buf);
printf("This is errno\n", errno);
}
}
close(client_fd);
return 0;
}
Replace 'strlen(buf)' with 'sizeof(buf)-1'

Parsing Chunked HTTP/1.1 Response

I just implemented a HTTP/1.1 client to parse the chunked transferring coding. However, it works for some websites but fails for others. I assume I need to read chunkSize + 2 bytes including \r\n for each chunk data, am I right?
Here is my code:
while(chunked)//if detecting chunked in the header before, this is true
{
//getLine is a function can read a line separated by \r\n
//sockfd is a socket created before and file position is at the start of HTTP body (after that blank line between header and body)
line = getLine(sockfd);
printf("%s", line);//print the chunk size line in hex
int chunkSize = strtol(line, NULL, 16);
if(chunkSize == 0)
{
printf("##### Read chunk size of 0, reading until we hit end of stream.\n");
break;
}
printf("##### Chunk size (in hex above) is %d in decimal and is printed here:\n", chunkSize);
char* chunkBuf = (char *)malloc(chunkSize + 2 + 1);//2 for \r\n, 1 for \0
bzero(chunkBuf, chunkSize + 3);
if(read(sockfd, chunkBuf, chunkSize + 2) == 0)//sockfd is a socket created before
{
perror("Read Error: ");
exit(EXIT_FAILURE);
}
printf("%s", chunkBuf);//print the chunk content
free(chunkBuf);
}
Actually I can print out the whole content without parsing, i.e. print line by line, so I think I may make some mistakes in the code above, could anyone give me some hint?
Below is the whole code for your reference:
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#define HTTP_VERSION "HTTP/1.1"
#define PAGE "/"
int createSokect()
{
int socketfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(socketfd < 0)
{
perror("Cannot create socket\n");
exit(EXIT_FAILURE);
}
return socketfd;
}
char* getIP(char* host)
{
struct hostent* hent;
int len = 15;//xxx.xxx.xxx.xxx
char *ipaddr = (char *)malloc(len + 1);//one more \0
bzero(ipaddr, len + 1);
if((hent = gethostbyname(host)) == NULL)
{
printf("Cannot get IP for this host: %s\n", host);
exit(EXIT_FAILURE);
}
if(inet_ntop(AF_INET, (void*)hent->h_addr_list[0], ipaddr, len) == NULL)
{
printf("Cannot resolve IP for this host: %s\n", host);
exit(EXIT_FAILURE);
}
return ipaddr;
}
char* createQuery(char* host, char* page)
{
char* msg = "GET %s %s\r\nHost: %s\r\nConnection: close\r\n\r\n";
char* query = (char *)malloc(strlen(host) + strlen(page) + strlen(msg) + strlen(HTTP_VERSION) - 6 + 1);//-6: %s %s %s
sprintf(query, msg, page, HTTP_VERSION, host);
return query;
}
char* getLine(int fd)
{
char c = 0, pre = 0;
char* line = 0;
int size = 1;
int pos = 0;
while(read(fd, &c, 1)!=0)
{
if(pos + 1 == size)
{
size *= 2;
line = realloc(line, size);
}
line[pos++] = c;
//printf("%c", c);
if(pre == '\r' && c == '\n')//this is a new line
{
break;
}
pre = c;
}
if(line)
{
line[pos++] = 0;
}
return line;
}
int main(int argc, char** argv)
{
if(argc < 3)
{
perror("Need more arguments");
exit(EXIT_FAILURE);
}
int sockfd = createSokect();
char* ip = getIP(argv[1]);
printf("Host: %s\n", argv[1]);
printf("IP: %s\n", ip);
struct sockaddr_in server;
server.sin_family = AF_INET;
int err = inet_pton(server.sin_family, ip, (void *)(&(server.sin_addr.s_addr)));
if(err != 1)
{
perror("Cannot convert IP to binary address\n");
exit(EXIT_FAILURE);
}
server.sin_port = htons(atoi(argv[2]));
printf("port: %d\n", server.sin_port);
//connect to the server
if(connect(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
{
printf("Cannot connect: %d\n", err);
exit(EXIT_FAILURE);
}
char* query = createQuery(argv[1], PAGE);
printf("##### CLIENT IS SENDING THE FOLLOWING TO SERVER:\n");
printf("%s", query);
int offset = 0;
//send query to the server
err = send(sockfd, query + offset, strlen(query) - offset, 0);
if(err < 0)
{
perror("Cannot send query");
exit(EXIT_FAILURE);
}
printf("##### CLIENT RECEIVED THE FOLLOWING FROM SERVER:\n");
//receive message line by line
bool chunked = false;
char* line;
while((line = getLine(sockfd)) != NULL)
{
printf("%s", line);
if(!strcasecmp(line, "transfer-encoding: chunked\r\n"))
{
chunked = true;
//printf("Chunked here\n");
}
if(!strcmp(line, "\r\n"))
{
printf("##### Just read blank line, now reading body.\n");
if(chunked)//chunked, we print those in another way, otherwise line by line
{
free(line);
break;
}
}
free(line);
}
while(chunked)
{
line = getLine(sockfd);
printf("%s", line);
int chunkSize = strtol(line, NULL, 16);
if(chunkSize == 0)
{
printf("##### Read chunk size of 0, reading until we hit end of stream.\n");
break;
}
printf("##### Chunk size (in hex above) is %d in decimal and is printed here:\n", chunkSize);
char* chunkBuf = (char *)malloc(chunkSize + 2 + 1);//2 for \r\n, 1 for \0
bzero(chunkBuf, chunkSize + 3);
if(read(sockfd, chunkBuf, chunkSize + 2) == 0)
{
perror("Read Error: ");
exit(EXIT_FAILURE);
}
printf("%s", chunkBuf);
free(chunkBuf);
}
//receive message from the server
/*
char buf[2048];
bzero(buf, sizeof(buf));
err = recv(sockfd, buf, sizeof(buf), 0);
if(err < 0)
{
perror("Receive error");
exit(EXIT_FAILURE);
}
char *content = buf;
fprintf(stdout, content);*/
free(query);
free(ip);
close(sockfd);
printf("##### Connection closed by server.\n");
exit(EXIT_SUCCESS);
}
The line:
if(read(sockfd, chunkBuf, chunkSize + 2) == 0) ...
will read up to chunkSize+2, i.e. it can read less. See the manual page of read. Your code shall look something like:
int n = 0;
while (n<chunkSize) {
r = read(sockfd, chunkBuf+n, chunkSize - n);
if (r <= 0) { error or closed conection ... }
n += r;
}
Since I know the chunk size, so I read character one by one counting up to the chunk size. This way can work. But I still don't understand why I failed when trying to use read or recv by the whole chunk size at one time.

Error in C program ‘struct client_thread *’ but argument is of type ‘int’

I'm playing around with C although I am fairly new to it. to find out how threads and locks work, connections work, within an IRC.
However I have come across and error, and I'm not sure how to fix it.
Bellow are the errors I get.
sample.c: In function ‘clientThreadEntry’:
sample.c:343:5: warning: passing argument 1 of ‘connectionMain’ makes pointer from integer without a cast [enabled by default]
connectionMain(t->sock);
^
sample.c:216:5: note: expected ‘struct client_thread *’ but argument is of type ‘int’
int connectionMain(struct client_thread *t) {
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <netdb.h>
#include <time.h>
#include <errno.h>
#include <pthread.h>
#include <ctype.h>
struct client_thread {
pthread_t thread;
int thread_id;
int sock;
char nickname[32];
int state;
#define DEAD 1
#define ALIVE 2
int user_command_seen;
int user_has_registered;
time_t timeout;
char line[1024];
int line_len;
int next_message;
};
/**
Allocate static structure for all client connections.
*/
#define MAX_CLIENTS 50
struct client_thread threads[MAX_CLIENTS];
/**
Number of connections we have open right now.
*/
int clientCount = 0;
pthread_rwlock_t message_log_lock = PTHREAD_RWLOCK_INITIALIZER;
#define MAX_MESSAGES 10000
char messageLogRecipients[MAX_MESSAGES];
char *messageLog[MAX_MESSAGES];
int messageCount = 0;
int messageAppend(char *recipient, char *message) {
/*
If used up all message space, exit.
*/
if (messageCount >= MAX_MESSAGES) return -1;
// Append message.
pthread_rwlock_wrlock(&message_log_lock);
messageLogRecipients[messageCount] = strdup(recipient);
message[messageCount] = strdup(message);
messageCount++;
pthread_rwlock_unlock(&message_log_lock);
return 0;
}
int messageRead(struct client_thread *t) {
pthread_rwlock_rdlock(&message_log_lock);
int i;
for (i = t->next_message + 1; i < messageCount; i++) {
}
t -> next_message = messageCount;
pthread_rwlock_unlock(&message_log_lock);
return 0;
}
int create_listen_socket(int port) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) return -1;
int on = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) == -1) {
close(sock);
return -1;
}
if (ioctl(sock, FIONBIO, (char *) &on) == -1) {
close(sock);
return -1;
}
/* Bind it to the next port we want to try. */
struct sockaddr_in address;
bzero((char *) &address, sizeof (address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
if (bind(sock, (struct sockaddr *) &address, sizeof (address)) == -1) {
close(sock);
return -1;
}
if (listen(sock, 20) != -1) return sock;
close(sock);
return -1;
}
int accept_incoming(int sock) {
struct sockaddr addr;
unsigned int addr_len = sizeof addr;
int asock;
if ((asock = accept(sock, &addr, &addr_len)) != -1) {
return asock;
}
return -1;
}
int read_from_socket(int sock, unsigned char *buffer, int *count, int buffer_size,
int timeout) {
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, NULL) | O_NONBLOCK);
int t = time(0) + timeout;
if (*count >= buffer_size) return 0;
int r = read(sock, &buffer[*count], buffer_size - *count);
while (r != 0) {
if (r > 0) {
(*count) += r;
break;
}
r = read(sock, &buffer[*count], buffer_size - *count);
if (r == -1 && errno != EAGAIN) {
perror("read() returned error. Stopping reading from socket.");
return -1;
} else usleep(100000);
// timeout after a few seconds of nothing
if (time(0) >= t) break;
}
buffer[*count] = 0;
return 0;
}
/**
Function to check is a user has registered yet or not.
*/
int registrationCheck(struct client_thread *t) {
if (t->user_has_registered)
return -1;
if (t->user_command_seen && t->nickname[0]) {
// User has met registration requirements.
t->user_has_registered = 1;
t->timeout = 60;
char msg[8192];
snprintf(msg, 1024, ":myircServer.com 001 %s : You have registered.\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 002 %s : You have registered.\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 003 %s : You have registered.\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 004 %s : You have registered.\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 253 %s : Unknown connections\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 254 %s : Channels formed\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 255 %s : I have ??? clients and ??? servers.\n", t->nickname);
write(t->sock, msg, strlen(msg));
return 0;
}
return -1;
}
int connectionMain(struct client_thread *t) {
int sock = t->sock;
char nickname[8192];
char msg[1024];
snprintf(msg, 1024, ":myircserver.com 020 * :Greetings, from the IRC server\n");
write(sock, msg, strlen(msg));
unsigned char buffer[8192];
int length = 0;
t->timeout = 5;
int timeOfLastData = time(0);
while (1) {
length = 0;
messageRead(t);
read_from_socket(sock, buffer, &length, 8192, 1);
if (length > 0)
{
timeOfLastData = time(0);
}
if (length == 0 && ((time(0) - timeOfLastData) >= t->timeout)) {
snprintf(msg, 1024, "ERROR :Closing Link: Connection timed out.. See ya!\n");
write(sock, msg, strlen(msg));
close(sock);
return 0;
}
buffer[length] = 0;
char channel[8192];
int r = sscanf((char *) buffer, "JOIN %s", channel);
if (r == 1) {
if (!t->user_has_registered) {
snprintf(msg, 1024, ":myircserver.com 241 * :JOIN command sent before registration\n");
write(sock, msg, strlen(msg));
}
}
r = sscanf((char *) buffer, "NICK %s", nickname);
if (r == 1) {
/**
check and saw a nickname from the client.
Need to handle what to do next.
Checks if the nickname provided is less than 1 or greater than 30.
if it is, then send and error since it is invalid and we cannot deal with nicknames of these sizes.
if this isn't in place we would be trying to pass a string to big or too small to our nickname variable.
*/
if (strlen(nickname) > 30 || strlen(nickname) < 1) {
snprintf(msg, 1024, "ERROR :Inviliad nickname: Nickname too short or too long.\n");
write(sock, msg, strlen(msg));
} else {
/**
Nickname is a valid length!
copy nickname to thread variable nickname.
*/
strcpy(t->nickname, nickname);
registrationCheck(t);
}
}
/**
Saw USER command if a NICK has been provided. Then mark connection.
As registered. and send client greeting messages.
If this isnt here, then we will not beable to handle USER command or the like.
*/
if (!strncasecmp("USER ", (char *) buffer, 5)) {
if (t->nickname) {
/**
Nickname has been provided and user has been registered.
*/
t->user_command_seen = 1;
registrationCheck(t);
}
}
/**
Checks if the messaged parsed is correct.
Checks nickname, length and the response are correct and send the correct respose code.
Checks of the PRIVMSG has at least 10 bytes of data to be used.
Needed to see if there is data, and if it is sent before registration.
*/
if (!strncasecmp("PRIVMSG", (char *) buffer, 7)) {
if (!t->user_has_registered){
snprintf(msg, 1024, ":myircserver.com 241 * :PRIVMSG command sent before registration\n");
write(sock, msg, strlen(msg));
} else {
// Client is registered, so handle the message.
char recipient[1024];
char message [1024];
if (sscanf((char *) buffer, "PRIVMSG %s :%[^\n]", recipient, message) == 2){
messageAppend(recipient, message);
} else {
// msg is wrongly formed, error.
snprintf(msg, 1024, ":myircserver.com 461 %s :Wrongly formed PRIVMSG command sent.\n", t->nickname);
write(sock, msg, strlen(msg));
}
}
}
/**
* Client left, we must check and close this or we will get a SIGPIPE error that will kill program.
* Send an error statement back to user so they know what is going on.
*/
if (!strncasecmp("QUIT", (char *) buffer, 4)) {
snprintf(msg, 1024, "ERROR :Closing Link: Connection timed out (see ya!)\n");
write(sock, msg, strlen(msg));
close(sock);
return 0;
}}
close(sock);
return 0;
}
void *clientThreadEntry(void *arg) {
struct client_thread *t = arg;
/**
Run the threads connection handling code through threads.
*/
connectionMain(t->sock);
t->state = DEAD;
return NULL;
}
int handleConnection(int sock) {
printf("WE GET HERE\n");
int i;
for (i = 0; i < clientCount; i++) {
if (threads[i].state == DEAD) {
break;
}}
if (i > MAX_CLIENTS) {
close(sock);
return 1;
}
// clear out client structure, set up for threads.
bzero(&threads[i], sizeof (struct client_thread));
// store file descriptor into thread array.
threads[i].sock = sock;
threads[i].state = ALIVE;
threads[i].thread_id = i;
if (pthread_create(&threads[i].thread, NULL, clientThreadEntry, &threads[i]))
{
close(sock);
return 1;
}
if (i == clientCount) clientCount++;
return 0;
}
int main(int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
if (argc != 2) {
fprintf(stderr, "usage: sample <tcp port>\n");
exit(-1);
}
int master_socket = create_listen_socket(atoi(argv[1]));
fcntl(master_socket, F_SETFL, fcntl(master_socket, F_GETFL, NULL)&(~O_NONBLOCK));
while (1) {
int client_sock = accept_incoming(master_socket);
if (client_sock != -1) {
// Got connection -- do something with it.
handleConnection(client_sock);
} else {usleep(10000);}
}
}
You have a compilation warning. Your function connectionMain takes a client_thread pointer:
int connectionMain(struct client_thread *t)
{
But you are calling it with a sock integer:
void *clientThreadEntry(void *arg) {
struct client_thread *t = arg;
/**
Run the threads connection handling code through threads.
*/
connectionMain(t->sock);
t->state = DEAD;
return NULL;
}
Presumably you want to call it like so:
connectionMain(t);
That would at least fix the compilation warning.

Resources