Related
There are two clients and a server in the application. The two clients communicate with each other via the server. When one client sends a message to the server, the server stores the message as a text file in the server, where file name is clientid_timestamp.txt. The server then sends the file to the other client that reads the file and displays its content on its terminal.
I have written the code for the client and the server given below.The issue that I am facing is that the client is not able to get the file from the server print it's contents.It doesn't show an output in the terminal.
Server side image in terminal
client side image in terminal
Server code:
#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 <pthread.h>
#include <sys/types.h>
#include <signal.h>
#define MAX_CLIENTS 3
#define BUFFER_SZ 2048
#define SIZE 1024
static _Atomic unsigned int cli_count = 0;
static int uid = 10;
/* Client structure */
typedef struct{
struct sockaddr_in address;
int sockfd;
int uid;
char name[32];
} client_t;
client_t *clients[MAX_CLIENTS];
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
void str_overwrite_stdout() {
printf("\r%s", "> ");
fflush(stdout);
}
void print_client_addr(struct sockaddr_in addr){
printf("%d.%d.%d.%d",
addr.sin_addr.s_addr & 0xff,
(addr.sin_addr.s_addr & 0xff00) >> 8,
(addr.sin_addr.s_addr & 0xff0000) >> 16,
(addr.sin_addr.s_addr & 0xff000000) >> 24);
}
/* Add clients to queue */
void queue_add(client_t *cl){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i < MAX_CLIENTS; ++i){
if(!clients[i]){
clients[i] = cl;
break;
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Remove clients to queue */
void queue_remove(int uid){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i < MAX_CLIENTS; ++i){
if(clients[i]){
if(clients[i]->uid == uid){
clients[i] = NULL;
break;
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
void send_file(int sockfd){
int n;
char data[SIZE] = {0};
FILE *fp = fopen("clientid_timestamp.txt", "r");
if (fp == NULL){
printf("Error opening file!\n");
exit(1);
}
while(fgets(data, SIZE, fp) != NULL) {
if (send(sockfd, data, sizeof(data), 0) == -1) {
perror("[-]Error in sending file.");
exit(1);
}
printf("hello");
bzero(data, SIZE);
}
}
/* Send message to all clients except sender */
void send_message(char *s, int uid){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i<MAX_CLIENTS; ++i){
if(clients[i]){
if(clients[i]->uid != uid){
printf("MESSAGE SENT TO %s:%d \n",inet_ntoa(clients[i]->address.sin_addr),ntohs(clients[i]->address.sin_port));
send_file(clients[i]->uid);
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Handle all communication with the client */
void *handle_client(void *arg){
char buff_out[BUFFER_SZ];
char name[32];
int leave_flag = 0;
cli_count++;
client_t *cli = (client_t *)arg;
// Name
bzero(buff_out, BUFFER_SZ);
while(1){
if (leave_flag) {
break;
}
int receive = recv(cli->sockfd, buff_out, BUFFER_SZ, 0);
if (receive > 0){
if(strlen(buff_out) > 0){
FILE *f = fopen("clientid_timestamp.txt", "w");
if (f == NULL){
printf("Error opening file!\n");
exit(1);
}
/* print some text */
fprintf(f, "%s", buff_out);
send_message(buff_out, cli->uid);
fclose(f);
printf("\n");
}
}
else {
printf("ERROR: -1\n");
leave_flag = 1;
}
bzero(buff_out, BUFFER_SZ);
}
/* Delete client from queue and yield thread */
close(cli->sockfd);
queue_remove(cli->uid);
free(cli);
cli_count--;
pthread_detach(pthread_self());
return NULL;
}
int main(int argc, char **argv){
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
return EXIT_FAILURE;
}
char *ip = "127.0.0.1";
int port = atoi(argv[1]);
int option = 1;
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
pthread_t tid;
/* Socket settings */
listenfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(ip);
serv_addr.sin_port = htons(port);
/* Ignore pipe signals */
signal(SIGPIPE, SIG_IGN);
if(setsockopt(listenfd, SOL_SOCKET,(SO_REUSEPORT | SO_REUSEADDR),(char*)&option,sizeof(option)) < 0){
perror("ERROR: setsockopt failed");
return EXIT_FAILURE;
}
/* Bind */
if(bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR: Socket binding failed");
return EXIT_FAILURE;
}
/* Listen */
if (listen(listenfd, 10) < 0) {
perror("ERROR: Socket listening failed");
return EXIT_FAILURE;
}
printf("=== WELCOME TO THE CHATROOM ===\n");
char exit[] = "exit_client";
char accepted[] = "accept_client";
while(1){
socklen_t clilen = sizeof(cli_addr);
connfd = accept(listenfd, (struct sockaddr*)&cli_addr, &clilen);
/* Check if max clients is reached */
if((cli_count) == MAX_CLIENTS){
printf("Max clients reached. Rejected: ");
print_client_addr(cli_addr);
printf(":%d\n", cli_addr.sin_port);
send(connfd, exit, strlen(exit), 0);
close(connfd);
continue;
}
else{
send(connfd, accepted, strlen(accepted), 0);
client_t *cli = (client_t *)malloc(sizeof(client_t));
cli->address = cli_addr;
cli->sockfd = connfd;
cli->uid = uid++;
printf("Client Connected : ");
printf("%s:%d \n",inet_ntoa(cli->address.sin_addr),ntohs(cli->address.sin_port));
/* Add client to the queue and fork thread */
queue_add(cli);
pthread_create(&tid, NULL, &handle_client, (void*)cli);
/* Reduce CPU usage */
sleep(1);
}
/* Client settings */
}
return EXIT_SUCCESS;
}
Client Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define LENGTH 2048
#define MAX 10000
// Global variables
volatile sig_atomic_t flag = 0;
int sockfd = 0;
char name[32];
void str_overwrite_stdout() {
printf("%s", "> ");
fflush(stdout);
}
void str_trim_lf (char* arr, int length) {
int i;
for (i = 0; i < length; i++) { // trim \n
if (arr[i] == '\n') {
arr[i] = '\0';
break;
}
}
}
void catch_ctrl_c_and_exit(int sig) {
flag = 1;
}
void send_msg_handler() {
char message[LENGTH] = {};
char buffer[LENGTH + 32] = {};
while(1) {
str_overwrite_stdout();
fgets(message, LENGTH, stdin);
//str_trim_lf(message, LENGTH);
if (strcmp(message, "exit") == 0) {
break;
} else {
sprintf(buffer, "%s\n", message);
send(sockfd, buffer, strlen(buffer), 0);
}
bzero(message, LENGTH);
bzero(buffer, LENGTH + 32);
}
catch_ctrl_c_and_exit(2);
}
void recv_msg_handler() {
while (1) {
int n;
FILE *fp;
char buff[1024]="",*ptr=buff;
int bytes=0,bytes_received;
while(bytes_received = recv(sockfd,ptr, 1, 0)){
printf("%s\n",ptr);
if(bytes_received==-1){
perror("recieve");
exit(3);
}
if(*ptr=='\n' ) break;
ptr++; //each times we increment it it points to the buffer element
}
*ptr=0;
ptr=buff;
printf("%s\n",ptr);
str_overwrite_stdout();
}
}
int main(int argc, char **argv){
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
return EXIT_FAILURE;
}
char *ip = "127.0.0.1";
int port = atoi(argv[1]);
signal(SIGINT, catch_ctrl_c_and_exit);
struct sockaddr_in server_addr;
/* Socket settings */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip);
server_addr.sin_port = htons(port);
// Connect to Server
int err = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err == -1) {
printf("ERROR: connect\n");
return EXIT_FAILURE;
}
char exit_client[] = "exit_client";
char buff[MAX];
bzero(buff, sizeof(buff));
recv(sockfd, buff, 1024, 0);
if((strcmp(buff, exit_client) == 0)){
printf("limit exceeded , closing the client \n");
close(sockfd);
return EXIT_SUCCESS;
}
else{
printf("client accepted by the server and limit not exceeded \n");
}
printf("=== WELCOME TO THE CHATROOM ===\n");
pthread_t send_msg_thread;
if(pthread_create(&send_msg_thread, NULL, (void *) send_msg_handler, NULL) != 0){
printf("ERROR: pthread\n");
return EXIT_FAILURE;
}
pthread_t recv_msg_thread;
if(pthread_create(&recv_msg_thread, NULL, (void *) recv_msg_handler, NULL) != 0){
printf("ERROR: pthread\n");
return EXIT_FAILURE;
}
while (1){
if(flag){
printf("\nBye\n");
break;
}
}
close(sockfd);
return EXIT_SUCCESS;
}
EDIT-1
I have made the changes suggested by the user stackinside and the code now works , but I have being facing a new issue for the past few hours and I am not able to wrap my head around it as what could the reason for it.
The issue that arises now is that the client is only able to send the message once to other client and vice versa and then it is not able to send any more messages except a new line.The updated code based on the changes I made
client side image in terminal
server side image in terminal
Server code :
#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 <pthread.h>
#include <sys/types.h>
#include <signal.h>
#define MAX_CLIENTS 3
#define BUFFER_SZ 2048
#define SIZE 1024
static _Atomic unsigned int cli_count = 0;
static int uid = 10;
/* Client structure */
typedef struct{
struct sockaddr_in address;
int sockfd;
int uid;
char name[32];
} client_t;
client_t *clients[MAX_CLIENTS];
void str_trim_lf (char* arr, int length) {
int i;
for (i = 0; i < length; i++) { // trim \n
if (arr[i] == '\n') {
arr[i] = '\0';
break;
}
}
}
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
void str_overwrite_stdout() {
printf("\r%s", "> ");
fflush(stdout);
}
void print_client_addr(struct sockaddr_in addr){
printf("%d.%d.%d.%d",
addr.sin_addr.s_addr & 0xff,
(addr.sin_addr.s_addr & 0xff00) >> 8,
(addr.sin_addr.s_addr & 0xff0000) >> 16,
(addr.sin_addr.s_addr & 0xff000000) >> 24);
}
/* Add clients to queue */
void queue_add(client_t *cl){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i < MAX_CLIENTS; ++i){
if(!clients[i]){
clients[i] = cl;
break;
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Remove clients to queue */
void queue_remove(int uid){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i < MAX_CLIENTS; ++i){
if(clients[i]){
if(clients[i]->uid == uid){
clients[i] = NULL;
break;
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
void send_file(int sockfd){
int n;
char data[SIZE] = {0};
FILE *fp = fopen("clientid_timestamp.txt", "r");
if (fp == NULL){
printf("Error opening file!\n");
exit(1);
}
while(fgets(data, SIZE, fp) != NULL) {
if (send(sockfd, data, sizeof(data), 0) == -1) {
perror("[-]Error in sending file.");
exit(1);
}
bzero(data, SIZE);
}
}
/* Send message to all clients except sender */
void send_message(char *s, int uid){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i<MAX_CLIENTS; ++i){
if(clients[i]){
if(clients[i]->uid != uid){
printf("MESSAGE SENT TO %s:%d \n",inet_ntoa(clients[i]->address.sin_addr),ntohs(clients[i]->address.sin_port));
send_file(clients[i]->sockfd);
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Handle all communication with the client */
void *handle_client(void *arg){
char buff_out[BUFFER_SZ];
char name[32];
int leave_flag = 0;
cli_count++;
client_t *cli = (client_t *)arg;
// Name
bzero(buff_out, BUFFER_SZ);
while(1){
if (leave_flag) {
break;
}
int receive = recv(cli->sockfd, buff_out, BUFFER_SZ, 0);
if (receive > 0){
if(strlen(buff_out) > 0){
FILE *f = fopen("clientid_timestamp.txt", "w");
if (f == NULL){
printf("Error opening file!\n");
exit(1);
}
/* print some text */
fprintf(f, "%s", buff_out);
fclose(f);
send_message(buff_out, cli->uid);
printf("\n");
}
}
else {
printf("ERROR: -1\n");
leave_flag = 1;
}
bzero(buff_out, BUFFER_SZ);
}
/* Delete client from queue and yield thread */
close(cli->sockfd);
queue_remove(cli->uid);
free(cli);
cli_count--;
pthread_detach(pthread_self());
return NULL;
}
int main(int argc, char **argv){
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
return EXIT_FAILURE;
}
char *ip = "127.0.0.1";
int port = atoi(argv[1]);
int option = 1;
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
pthread_t tid;
/* Socket settings */
listenfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(ip);
serv_addr.sin_port = htons(port);
/* Ignore pipe signals */
signal(SIGPIPE, SIG_IGN);
if(setsockopt(listenfd, SOL_SOCKET,(SO_REUSEPORT | SO_REUSEADDR),(char*)&option,sizeof(option)) < 0){
perror("ERROR: setsockopt failed");
return EXIT_FAILURE;
}
/* Bind */
if(bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR: Socket binding failed");
return EXIT_FAILURE;
}
/* Listen */
if (listen(listenfd, 10) < 0) {
perror("ERROR: Socket listening failed");
return EXIT_FAILURE;
}
printf("=== WELCOME TO THE CHATROOM ===\n");
char exit[] = "exit_client";
char accepted[] = "accept_client";
while(1){
socklen_t clilen = sizeof(cli_addr);
connfd = accept(listenfd, (struct sockaddr*)&cli_addr, &clilen);
/* Check if max clients is reached */
if((cli_count) == MAX_CLIENTS){
printf("Max clients reached. Rejected: ");
print_client_addr(cli_addr);
printf(":%d\n", cli_addr.sin_port);
send(connfd, exit, strlen(exit), 0);
close(connfd);
continue;
}
else{
send(connfd, accepted, strlen(accepted), 0);
client_t *cli = (client_t *)malloc(sizeof(client_t));
cli->address = cli_addr;
cli->sockfd = connfd;
cli->uid = uid++;
printf("Client Connected : ");
printf("%s:%d \n",inet_ntoa(cli->address.sin_addr),ntohs(cli->address.sin_port));
/* Add client to the queue and fork thread */
queue_add(cli);
pthread_create(&tid, NULL, &handle_client, (void*)cli);
/* Reduce CPU usage */
sleep(1);
}
/* Client settings */
}
return EXIT_SUCCESS;
}
Client code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define LENGTH 2048
#define MAX 10000
// Global variables
volatile sig_atomic_t flag = 0;
int sockfd = 0;
char name[32];
void str_overwrite_stdout() {
printf("%s", "> ");
fflush(stdout);
}
void str_trim_lf (char* arr, int length) {
int i;
for (i = 0; i < length; i++) { // trim \n
if (arr[i] == '\n') {
arr[i] = '\0';
break;
}
}
}
void catch_ctrl_c_and_exit(int sig) {
flag = 1;
}
void send_msg_handler() {
char message[LENGTH] = {};
char buffer[LENGTH + 32] = {};
while(1) {
str_overwrite_stdout();
fgets(message, LENGTH, stdin);
str_trim_lf(message, LENGTH);
if (strcmp(message, "exit") == 0) {
break;
} else {
sprintf(buffer, "%s\n", message);
send(sockfd, buffer, strlen(buffer), 0);
}
bzero(message, LENGTH);
bzero(buffer, LENGTH + 32);
}
catch_ctrl_c_and_exit(2);
}
void recv_msg_handler() {
int n;
FILE *fp;
char buff[1024]="",*ptr=buff;
int bytes=0,bytes_received;
while (1) {
while(bytes_received = recv(sockfd,ptr, 1, 0)){
if(bytes_received==-1){
perror("recieve");
exit(3);
}
if(*ptr=='\n' ) break;
ptr++; //each times we increment it it points to the buffer element
}
*ptr=0;
str_trim_lf(buff, strlen(buff));
ptr=buff;
printf("%s \n",ptr);
str_overwrite_stdout();
}
}
int main(int argc, char **argv){
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
return EXIT_FAILURE;
}
char *ip = "127.0.0.1";
int port = atoi(argv[1]);
signal(SIGINT, catch_ctrl_c_and_exit);
struct sockaddr_in server_addr;
/* Socket settings */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip);
server_addr.sin_port = htons(port);
// Connect to Server
int err = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err == -1) {
printf("ERROR: connect\n");
return EXIT_FAILURE;
}
char exit_client[] = "exit_client";
char buff[MAX];
bzero(buff, sizeof(buff));
recv(sockfd, buff, 1024, 0);
if((strcmp(buff, exit_client) == 0)){
printf("limit exceeded , closing the client \n");
close(sockfd);
return EXIT_SUCCESS;
}
else{
printf("client accepted by the server and limit not exceeded \n");
}
printf("=== WELCOME TO THE CHATROOM ===\n");
pthread_t send_msg_thread;
if(pthread_create(&send_msg_thread, NULL, (void *) send_msg_handler, NULL) != 0){
printf("ERROR: pthread\n");
return EXIT_FAILURE;
}
pthread_t recv_msg_thread;
if(pthread_create(&recv_msg_thread, NULL, (void *) recv_msg_handler, NULL) != 0){
printf("ERROR: pthread\n");
return EXIT_FAILURE;
}
while (1){
if(flag){
printf("\nBye\n");
break;
}
}
close(sockfd);
return EXIT_SUCCESS;
}
EDIT-2
So now I changed the fgets to freads and removed all the unnecessary bzero and changed the strlen to sizeof as suggested by the comments of user207421 , Martin James and stackinside .It finally works , I agree there is still a lot of room for optimisation , but it works for now and thanks for the effort.
client side image in terminal
Server side image in terminal
Client side code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define LENGTH 2048
#define MAX 10000
#define CHUNK_SIZE 512
// Global variables
volatile sig_atomic_t flag = 0;
int sockfd = 0;
char name[32];
void str_overwrite_stdout() {
printf("%s", "> ");
fflush(stdout);
}
void str_trim_lf (char* arr, int length) {
int i;
for (i = 0; i < length; i++) { // trim \n
if (arr[i] == '\n') {
arr[i] = '\0';
break;
}
}
}
void catch_ctrl_c_and_exit(int sig) {
flag = 1;
}
void send_msg_handler() {
char message[LENGTH] = {};
char buffer[LENGTH + 32] = {};
while(1) {
str_overwrite_stdout();
fgets(message, LENGTH, stdin);
str_trim_lf(message, LENGTH);
if (strcmp(message, "exit") == 0) {
break;
}
else {
sprintf(buffer, "%s\n", message);
send(sockfd, buffer, sizeof(buffer), 0);
}
bzero(message, LENGTH);
bzero(buffer, LENGTH + 32);
}
catch_ctrl_c_and_exit(2);
}
void recv_msg_handler() {
while (1) {
int n;
FILE *fp;
char buff[1024],*ptr=buff;
int bytes=0,bytes_received;
int file_size;
recv(sockfd, buff, CHUNK_SIZE, 0);
file_size = atoi(buff);
str_trim_lf(buff, sizeof(buff));
printf("%s \n",buff);
str_overwrite_stdout();
}
}
int main(int argc, char **argv){
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
return EXIT_FAILURE;
}
char *ip = "127.0.0.1";
int port = atoi(argv[1]);
signal(SIGINT, catch_ctrl_c_and_exit);
struct sockaddr_in server_addr;
/* Socket settings */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip);
server_addr.sin_port = htons(port);
// Connect to Server
int err = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err == -1) {
printf("ERROR: connect\n");
return EXIT_FAILURE;
}
char exit_client[] = "exit_client";
char buff[MAX];
bzero(buff, sizeof(buff));
recv(sockfd, buff, 1024, 0);
if((strcmp(buff, exit_client) == 0)){
printf("limit exceeded , closing the client \n");
close(sockfd);
return EXIT_SUCCESS;
}
else{
printf("client accepted by the server and limit not exceeded \n");
}
printf("=== WELCOME TO THE CHATROOM ===\n");
pthread_t send_msg_thread;
if(pthread_create(&send_msg_thread, NULL, (void *) send_msg_handler, NULL) != 0){
printf("ERROR: pthread\n");
return EXIT_FAILURE;
}
pthread_t recv_msg_thread;
if(pthread_create(&recv_msg_thread, NULL, (void *) recv_msg_handler, NULL) != 0){
printf("ERROR: pthread\n");
return EXIT_FAILURE;
}
while (1){
if(flag){
printf("\nBye\n");
break;
}
}
close(sockfd);
return EXIT_SUCCESS;
}
Server side:
#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 <pthread.h>
#include <sys/types.h>
#include <signal.h>
#define MAX_CLIENTS 3
#define BUFFER_SZ 2048
#define SIZE 1024
#define CHUNK_SIZE 512
static _Atomic unsigned int cli_count = 0;
static int uid = 10;
/* Client structure */
typedef struct{
struct sockaddr_in address;
int sockfd;
int uid;
char name[32];
} client_t;
client_t *clients[MAX_CLIENTS];
void str_trim_lf (char* arr, int length) {
int i;
for (i = 0; i < length; i++) { // trim \n
if (arr[i] == '\n') {
arr[i] = '\0';
break;
}
}
}
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
void str_overwrite_stdout() {
printf("\r%s", "> ");
fflush(stdout);
}
void print_client_addr(struct sockaddr_in addr){
printf("%d.%d.%d.%d",
addr.sin_addr.s_addr & 0xff,
(addr.sin_addr.s_addr & 0xff00) >> 8,
(addr.sin_addr.s_addr & 0xff0000) >> 16,
(addr.sin_addr.s_addr & 0xff000000) >> 24);
}
/* Add clients to queue */
void queue_add(client_t *cl){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i < MAX_CLIENTS; ++i){
if(!clients[i]){
clients[i] = cl;
break;
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Remove clients to queue */
void queue_remove(int uid){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i < MAX_CLIENTS; ++i){
if(clients[i]){
if(clients[i]->uid == uid){
clients[i] = NULL;
break;
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
void send_file(int sockfd){
int n;
char data[SIZE] = {0};
FILE *fp = fopen("clientid_timestamp.txt", "r");
if (fp == NULL){
printf("Error opening file!\n");
exit(1);
}
char file_data[CHUNK_SIZE];
size_t nbytes = 0;
while ( (nbytes = fread(data, sizeof(char), CHUNK_SIZE,fp)) > 0){
if (send(sockfd, data, nbytes, 0) == -1) {
perror("[-]Error in sending file.");
exit(1);
}
}
}
/* Send message to all clients except sender */
void send_message(char *s, int uid){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i<MAX_CLIENTS; ++i){
if(clients[i]){
if(clients[i]->uid != uid){
printf("MESSAGE SENT TO %s:%d \n",inet_ntoa(clients[i]->address.sin_addr),ntohs(clients[i]->address.sin_port));
send_file(clients[i]->sockfd);
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Handle all communication with the client */
void *handle_client(void *arg){
char buff_out[BUFFER_SZ];
char name[32];
int leave_flag = 0;
cli_count++;
client_t *cli = (client_t *)arg;
// Name
bzero(buff_out, BUFFER_SZ);
while(1){
if (leave_flag) {
break;
}
int receive = recv(cli->sockfd, buff_out, BUFFER_SZ, 0);
if (receive > 0){
if(strlen(buff_out) > 0){
FILE *f = fopen("clientid_timestamp.txt", "w");
if (f == NULL){
printf("Error opening file!\n");
exit(1);
}
/* print some text */
fprintf(f, "%s", buff_out);
fclose(f);
send_message(buff_out, cli->uid);
printf("\n");
}
}
else {
printf("ERROR: -1\n");
leave_flag = 1;
}
}
/* Delete client from queue and yield thread */
close(cli->sockfd);
queue_remove(cli->uid);
free(cli);
cli_count--;
pthread_detach(pthread_self());
return NULL;
}
int main(int argc, char **argv){
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
return EXIT_FAILURE;
}
char *ip = "127.0.0.1";
int port = atoi(argv[1]);
int option = 1;
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
pthread_t tid;
/* Socket settings */
listenfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(ip);
serv_addr.sin_port = htons(port);
/* Ignore pipe signals */
signal(SIGPIPE, SIG_IGN);
if(setsockopt(listenfd, SOL_SOCKET,(SO_REUSEPORT | SO_REUSEADDR),(char*)&option,sizeof(option)) < 0){
perror("ERROR: setsockopt failed");
return EXIT_FAILURE;
}
/* Bind */
if(bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR: Socket binding failed");
return EXIT_FAILURE;
}
/* Listen */
if (listen(listenfd, 10) < 0) {
perror("ERROR: Socket listening failed");
return EXIT_FAILURE;
}
printf("=== WELCOME TO THE CHATROOM ===\n");
char exit[] = "exit_client";
char accepted[] = "accept_client";
while(1){
socklen_t clilen = sizeof(cli_addr);
connfd = accept(listenfd, (struct sockaddr*)&cli_addr, &clilen);
/* Check if max clients is reached */
if((cli_count) == MAX_CLIENTS){
printf("Max clients reached. Rejected: ");
print_client_addr(cli_addr);
printf(":%d\n", cli_addr.sin_port);
send(connfd, exit, sizeof(exit), 0);
close(connfd);
continue;
}
else{
send(connfd, accepted, sizeof(accepted), 0);
client_t *cli = (client_t *)malloc(sizeof(client_t));
cli->address = cli_addr;
cli->sockfd = connfd;
cli->uid = uid++;
printf("Client Connected : ");
printf("%s:%d \n",inet_ntoa(cli->address.sin_addr),ntohs(cli->address.sin_port));
/* Add client to the queue and fork thread */
queue_add(cli);
pthread_create(&tid, NULL, &handle_client, (void*)cli);
/* Reduce CPU usage */
sleep(1);
}
/* Client settings */
}
return EXIT_SUCCESS;
}
Server side:
send_file(clients[i]->uid); ought to be send_file(clients[i]->sockfd);
fclose(f); should probably go before send_message(buff_out, cli->uid);
Client side:
printf("%s\n",ptr); (before if(bytes_received==-1)) ought to be removed
There are other inconsistencies, unused variables, etc. This reivew is very limited.
what I want to make is the multi-client,server file transfer with socket.
it compiled well and seems realistic.
But Problem is, Code won't act properly and client don't send file to server folder.
I don't know what is the problem.
Can anybody see code and tell what is the problem?
it would be grateful if you change my code also.
client
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
// NOTE/BUG: this didn't provide enough space for a 5 digit port + EOS char
#if 0
enum { PORTSIZE = 5 };
#else
enum { PORTSIZE = 6 };
#endif
void
sig_handler(int signo)
{
if (signo == SIGINT)
printf("!! OUCH, CTRL - C received on client !!\n");
}
int
main(int argc, char **argv)
{
struct addrinfo hints,
*res;
char *server_hostname = "127.0.0.1";
char file_path[BUFSIZ];
char *server_reply = NULL;
char *user_input = NULL;
char buffer[BUFSIZ];
int filefd;
int sockfd;
ssize_t read_return;
struct hostent *hostent;
unsigned short server_port = 12345;
char portNum[PORTSIZE];
char remote_file[BUFSIZ];
int select;
char *client_server_files[BUFSIZ];
int i = 0;
int j;
char protoname[] = "tcp";
struct protoent *protoent;
struct sockaddr_in sockaddr_in;
in_addr_t in_addr;
// char filename_to_send[BUFSIZ];
if (argc != 3) {
fprintf(stderr, "Usage ./client <ip> <port>\n");
exit(EXIT_FAILURE);
}
server_hostname = argv[1];
server_port = strtol(argv[2], NULL, 10);
/* Get socket. */
protoent = getprotobyname(protoname);
if (protoent == NULL) {
perror("getprotobyname");
exit(EXIT_FAILURE);
}
sockfd = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
/* Prepare sockaddr_in. */
hostent = gethostbyname(server_hostname);
if (hostent == NULL) {
fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
exit(EXIT_FAILURE);
}
in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
if (in_addr == (in_addr_t)-1) {
fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));
exit(EXIT_FAILURE);
}
sockaddr_in.sin_addr.s_addr = in_addr;
sockaddr_in.sin_family = AF_INET;
sockaddr_in.sin_port = htons(server_port);
/* Do the actual connection. */
if (connect(sockfd, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {
perror("connect");
return EXIT_FAILURE;
}
while (1) {
if (signal(SIGINT, sig_handler)) {
break;
}
puts("connected to the server");
puts("-----------------");
puts("|1 - listLocal| \n|2 - listServer| \n|3 - sendFile| \n|4 - help| \n|5 - exit| ");
puts("-----------------");
while (1) {
printf("------%d",select);
scanf("%d", &select);
while ( getchar() != '\n' );
switch (select) {
case 1: // list files of client's directory
system("find . -maxdepth 1 -type f | sort");
sprintf(remote_file, "%s", "listLocal");
send(sockfd, remote_file, sizeof(remote_file), 0);
break;
case 2: // listServer
sprintf(remote_file, "%s", "listServer");
send(sockfd, remote_file, sizeof(remote_file), 0);
puts("---- Files btw Server and the Client ----");
for (j = 0; j < i; ++j) {
puts(client_server_files[j]);
}
break;
case 3: // send file
memset(file_path, 0, sizeof file_path);
scanf("%s", file_path);
sprintf(remote_file, "%s", "sendFile");
send(sockfd, remote_file, sizeof(remote_file), 0);
memset(remote_file, 0, sizeof remote_file);
// send file name to server
sprintf(remote_file, "%s", file_path);
send(sockfd, remote_file, sizeof(remote_file), 0);
filefd = open(file_path, O_RDONLY);
if (filefd == -1) {
perror("open send file");
//exit(EXIT_FAILURE);
break;
}
while (1) {
read_return = read(filefd, buffer, BUFSIZ);
if (read_return == 0)
break;
if (read_return == -1) {
perror("read");
//exit(EXIT_FAILURE);
break;
}
if (write(sockfd, buffer, read_return) == -1) {
perror("write");
//exit(EXIT_FAILURE);
break;
}
}
// add files in char pointer array
client_server_files[i++] = file_path;
close(filefd);
break;
case 5:
sprintf(remote_file, "%s", "exit");
send(sockfd, remote_file, sizeof(remote_file), 0);
free(user_input);
free(server_reply);
exit(EXIT_SUCCESS);
default:
puts("Wrong selection!");
break;
}
}
}
free(user_input);
free(server_reply);
exit(EXIT_SUCCESS);
}
server
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
#include <pthread.h>
struct client {
socklen_t client_len;
struct sockaddr_in client_address;
int client_sockfd;
pthread_t thread;
};
// NOTE: provide enough space for a 5 digit port + EOS char
enum { PORTSIZE = 6 };
double cpu_time_used;
clock_t start, end;
void *forClient(void *ptr);
void portCleaner(const char* port_num) {
char temp[100] = "sudo lsof -t -i tcp:";
sprintf(temp, "%s%s%s", temp, port_num, " | xargs kill -9;");
system(temp);
//puts(temp);
}
void
sig_handler(int signo)
{
if (signo == SIGINT)
printf("!! OUCH, CTRL - C received by server !!\n");
}
int
main(int argc, char **argv)
{
struct addrinfo hints,
*res;
int enable = 1;
//int filefd; // NOTE: this is never initialized/used
int server_sockfd;
unsigned short server_port = 12345u;
char portNum[PORTSIZE];
struct sockaddr_in server_address;
struct protoent *protoent;
char protoname[] = "tcp";
#if 0
int socket_index = 0;
#else
struct client *ctl;
#endif
if (argc != 2) {
fprintf(stderr, "Usage ./server <port>\n");
exit(EXIT_FAILURE);
}
server_port = strtol(argv[1], NULL, 10);
/* Create a socket and listen to it.. */
protoent = getprotobyname(protoname);
if (protoent == NULL) {
perror("getprotobyname");
exit(EXIT_FAILURE);
}
server_sockfd = socket(
AF_INET,
SOCK_STREAM,
protoent->p_proto
);
if (server_sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
perror("setsockopt(SO_REUSEADDR) failed");
exit(EXIT_FAILURE);
}
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(server_port);
if (bind(
server_sockfd,
(struct sockaddr*)&server_address,
sizeof(server_address)
) == -1
) {
perror("bind");
portCleaner(argv[1]);
exit(EXIT_FAILURE);
}
if (listen(server_sockfd, 5) == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
fprintf(stderr, "listening on port %d\n", server_port);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,1);
start = clock();
while (1) {
ctl = malloc(sizeof(struct client));
if (ctl == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
ctl->client_len = sizeof(ctl->client_address);
puts("waiting for client");
ctl->client_sockfd = accept(server_sockfd,
(struct sockaddr *) &ctl->client_address, &ctl->client_len);
if (ctl->client_sockfd < 0) {
perror("Cannot accept connection\n");
close(server_sockfd);
exit(EXIT_FAILURE);
}
pthread_create(&ctl->thread, &attr, forClient, ctl);
}
return EXIT_SUCCESS;
}
void *
forClient(void *ptr)
{
end = clock();
cpu_time_used = 1000 * (((double) (end - start)) / CLOCKS_PER_SEC);
#if 0
int connect_socket = (int) ptr;
#else
struct client *ctl = ptr;
int connect_socket = ctl->client_sockfd;
#endif
int filefd;
ssize_t read_return;
char buffer[BUFSIZ];
char *file_path;
char receiveFileName[BUFSIZ];
char cmd[BUFSIZ];
// Thread number means client's id
printf("Connected time [%lf] --- Thread number [%ld]\n", cpu_time_used, pthread_self());
// until stop receiving go on taking information
while (recv(connect_socket, receiveFileName, sizeof(receiveFileName), 0)) {
if((strcmp(receiveFileName, "listServer") == 0
|| strcmp(receiveFileName, "listLocal") == 0 || strcmp(receiveFileName, "help") == 0
|| strcmp(receiveFileName, "exit") == 0 || strcmp(receiveFileName, "sendFile") == 0)) {
printf("--- Command <%s> ---\n", receiveFileName);
continue;
}
file_path = receiveFileName;
fprintf(stderr, "is the file name received? ? => %s\n", file_path);
filefd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (filefd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
do {
read_return = read(connect_socket, buffer, BUFSIZ);
if (read_return == -1) {
perror("read");
exit(EXIT_FAILURE);
}
if (write(filefd, buffer, read_return) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
} while (read_return > 0);
// NOTE/BUG: filefd was never closed
close(filefd);
}
fprintf(stderr, "Client dropped connection\n");
// NOTE: do all client related cleanup here
// previously, the main thread was doing the close, which is why it had
// to do the pthread_join
close(connect_socket);
free(ctl);
return (void *) 0;
}
while (recv(connect_socket, receiveFileName, sizeof(receiveFileName), 0)) {
if((strcmp(receiveFileName, "listServer") == 0
You throw away the return value of recv, which is the only way to know how many bytes of data you received. So the rest of your code has no idea what data you actually received.
Then you pass the chunk of data you read to strcmp. But it's just a chunk of arbitrary data. It's not a string. You cannot pass something to strcmp unless it's a string.
You are missing a message protocol. Your client is supposed to send messages and your server needs to process messages. To do this, you need a message protocol that defines what a message is and then you need to write code to send and receive messages.
The recv function has no idea what your messages are and has no way to know where the message ends.
Since you're not experienced at using TCP, you should always start by specifying the protocol the server and client will use on top of TCP. If it's a message protocol, define specifically how messages will be represented on the wire. It may be helpful to look at the specifications for existing protocols layered on top of TCP such as SMTP or HTTP.
Otherwise, use a library that provides functions to send and receive messages instead of trying to use TCP directly.
This is my client program that requests files from the server:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SERVER_PORT 5959
#define MAX_LINE 512
void setstring(char *str){
str[MAX_LINE-1]='\0';
}
int main(int argc, char * argv[]){
FILE *fp;
struct hostent *hp;
struct sockaddr_in sin;
char *host;
char filename[MAX_LINE],buf[MAX_LINE],reply[MAX_LINE],rec_line[MAX_LINE];
int s;
char msg[MAX_LINE];
int len,new_len,rec_file;
if (argc==2) {
host = argv[1];
}
else {
fprintf(stderr, "usage: simplex-talk host\n");
exit(1);
}
/* translate host name into peer's IP address */
hp = gethostbyname(host);
if (!hp) {
fprintf(stderr, "simplex-talk: unknown host: %s\n", host);
exit(1);
}
else
printf("Client's remote host: %s\n", argv[1]);
/* build address data structure */
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
sin.sin_port = htons(SERVER_PORT);
/* active open */
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("simplex-talk: socket");
exit(1);
}
else
printf("Client created socket.\n");
int send_file_name,rec_msg;
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
perror("simplex-talk: connect");
close(s);
exit(1);
}
else{
printf("Client connected.\n");
/* main loop: get and send lines of text */
printf("Hello from server\n");
while(!(strcmp(reply,"bye")==0)){
printf("Enter the file name:\n");
scanf("%s",filename);
setstring(filename);
send_file_name=send(s,filename,strlen(filename)+1,0);
if(send_file_name<0)
fputs("Error sending filename",stdout);
rec_msg=recv(s,msg,sizeof(msg),0);
if(strcmp(msg,"File not found")==0)
printf("File not found\n");
else{
printf("%s\n",msg);
fp=fopen(filename,"w");
printf("CP1\n");
if(rec_file=recv(s,rec_line,sizeof(rec_line),0)>0){
printf("CP2");
printf("String recieved:%s\n",rec_line);
if(len=fwrite(rec_line,1,rec_file+1,fp)>0)
printf("Recieved file\n");
else
printf("Error writing to file\n");
}
else
printf("Not recieved\n");
}
printf("Enter 'bye' to terminate requesting files\n");
scanf("%s",reply);
}
}
return 0;
}
This is my server program that accepts request for files from the client:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SERVER_PORT 5959
#define MAX_PENDING 5
#define MAX_LINE 256
void setstring(char* str){
str[MAX_LINE-1]='\0';
}
int main(){
FILE *fp;
struct sockaddr_in sin;
char buf[MAX_LINE],msg[MAX_LINE],*rec_line;
int len;
int s, new_s,count;
char str[INET_ADDRSTRLEN];
int error_file,send_msg,read_line,send_file;
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr("0.0.0.0");
sin.sin_port = htons(SERVER_PORT);
/* setup passive open */
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("simplex-talk: socket");
exit(1);
}
inet_ntop(AF_INET, &(sin.sin_addr), str, INET_ADDRSTRLEN);
printf("Server is using address %s and port %d.\n", str, SERVER_PORT);
if ((bind(s, (struct sockaddr *)&sin, sizeof(sin))) < 0) {
perror("simplex-talk: bind");
exit(1);
}
else
printf("Server bind done.\n");
listen(s, MAX_PENDING);
/* wait for connection, then receive and print text */
while(1) {
if ((new_s = accept(s, (struct sockaddr *)&sin, &len)) < 0) {
perror("simplex-talk: accept");
exit(1);
}
printf("Server Listening.\n");
printf("Greetings\n");
int rec_file_name=recv(new_s,buf,sizeof(buf),0);
if(rec_file_name>0)
printf("File requested:%s\n",buf);
fp=fopen(buf,"r");
if(fp==NULL)
{
fputs("File not found\n",stdout);
strcpy(buf,"File not found");
if(error_file=send(new_s,buf,strlen(buf)+1,0)>0)
fputs("Successfully send error message to client\n",stdout);
}
else{
bzero(buf,MAX_LINE);
printf("File found :) \n");
strcpy(buf,"OK");
if(send_msg=send(new_s,buf,strlen(buf)+1,0)>0)
fputs("File found message sent to client\n",stdout);
fseek(fp,0,SEEK_END);
int file_size=ftell(fp);
fseek(fp,0,SEEK_SET);
printf("File size:%d\n",file_size);
rec_line=(char *)malloc(sizeof(char)*(file_size));
read_line=fread(rec_line,1,file_size+1,fp);
printf("File read: %s\n",rec_line);
if(send_file=send(new_s,rec_line,strlen(rec_line)+1,0)>0)
printf("File string sent to client\n");
}
}
close(new_s);
}
The problem is that in the client, my second recv() call, where it is supposed to receive the contents of a file, shows nothing. The programs halts at that point, but the server programs displays that it has sent the file contents. The client doesn't receive it.
The basic problem is that you're not checking the return values to see how much data you actually sent and received. So when the client calls:
rec_msg=recv(s,msg,sizeof(msg),0);
it will receive up to sizeof(msg) (512) bytes, which is probably both the OK message the server is sending AND the file contents (after the NUL). Which means when it does a second recv call to get the contents, it blocks, because it already read the contents in the first call and there's no more data waiting in the receive buffer.
Your error-checking is haphazard, and consequently you're certainly missing a problem that occurs before the behavior you're observing. I recommend you follow RW Steven's idiom:
int n;
if( (n = recv(new_s, buf, sizeof(buf), 0)) < 0 ) {
err(EXIT_FAILURE, "recv %d", __LINE__);
}
Test every function call, and handle every error. For simple programs, just call err(3) on error. Do that consistently, and the program's behavior will be much less mysterious (if still occasionally surprising). And don't be afraid of the spacebar! It's easy to hit, and easier still to read.
My other bit of advice, if I may, concerns
int send_file_name,rec_msg;
Names like that are confusing. A name is almost never an integer. For I/O sizes, just use one simple name like n, len, or size. Even if you don't care for yourself, you want to care before publishing your question in an open forum. Otherwise, when people see
send_file_name=send(s,filename,strlen(filename)+1,0);
they may think send is some function other than send(2), or that the person asking the question was careless.
The main problem I see is that neither the client nor the server are handling socket I/O correctly in general. They are not handling the cases where reads and writes transfer fewer bytes then requested, you need to loop the I/O. And the client is reading too many bytes from the server anyway, which is why your second recv() is blocking. And you are relying on a disconnect to indicate the end of the file has been reached, but that does not allow the client to do adequate error checking to know if the full file was actually received or not.
Also, when sending the content of a file, the server is attempting to read the entire file into memory (bad!), not doing adequate error checking on the file I/O, and it is treating the file content as text instead of as binary (don't use strlen() on binary data!).
Try something more like this instead:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SERVER_PORT 5959
#define MAX_LINE 512
int sendstring(int sock, const char *str) {
if (!str) str = "";
int len = strlen(str) + 1;
do {
int ret = send(sock, str, len, 0);
if (ret <= 0) return -1;
str += ret;
len -= ret;
}
while (len > 0);
return 0;
}
int readbuf(int sock, void *buf, int buflen) {
char *pbuf = (char*) buf;
while (buflen > 0) {
int len = recv(sock, pbuf, buflen, 0);
if (len <= 0) return -1;
pbuf += len;
buflen -= len;
}
return 0;
}
int readstring(int sock, char *str, int maxlen) {
while (maxlen > 0) {
if (recv(sock, str, 1, 0) <= 0) return -1;
if (*str == '\0') return 0;
++str;
--maxlen;
}
return -2;
}
int readfile(int sock, int fd) {
int filesize;
char buf[MAX_LINE];
if (readbuf(sock, &filesize, sizeof(filesize)) < 0) return -1;
filesize = ntohl(filesize);
while (filesize > 0) {
int len = readbuf(sock, buf, min(sizeof(buf), filesize));
if (len < 0) return -1;
if (fwrite(buf, len, 1, fp) != 1) return -2;
filesize -= len;
}
return 0;
}
int main(int argc, char * argv[]) {
char filename[MAX_LINE], reply[MAX_LINE];
if (argc != 2) {
fprintf(stderr, "usage: simplex-talk host\n");
exit(1);
}
char *host = argv[1];
/* translate host name into peer's IP address */
struct hostent *hp = gethostbyname(host);
if (!hp) {
fprintf(stderr, "simplex-talk: unknown host: %s\n", host);
exit(1);
}
if (hp->h_addrtype != AF_INET) {
fprintf(stderr, "simplex-talk: unsupported address type %d for host: %s\n", hp->h_addrtype, host);
exit(1);
}
/* build address data structure */
struct sockaddr_in sin;
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);
sin.sin_port = htons(SERVER_PORT);
printf("Host's remote IP: %s\n", inet_ntoa(&sin.sin_addr));
/* active open */
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror("simplex-talk: socket");
exit(1);
}
printf("Client created socket.\n");
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
perror("simplex-talk: connect");
close(s);
exit(1);
}
printf("Client connected.\n");
/* main loop: get and send lines of text */
do {
printf("Enter the file name ('bye' to quit):\n");
if (scanf("%512s", filename) != 1) {
printf("Error reading filename\n");
break;
}
if (strcmp(filename, "bye") == 0) {
sendstring(s, "bye");
break;
}
if (sendstring(s, filename) < 0) {
printf("Error sending filename\n");
break;
}
if (readstring(s, reply, sizeof(reply)) < 0) {
printf("Error reading reply\n");
break;
}
if (strcmp(reply, "OK") != 0) {
printf("%s\n", reply);
if (strcmp(reply, "bye") == 0) break;
continue;
}
FILE *fp = fopen(filename, "wb");
if (!fp) {
printf("Error opening file\n");
break;
}
printf("Receiving file\n");
int ret = readfile(s, fd);
fclose(fp);
if (ret < 0) {
if (ret == -2)
printf("Error writing file\n");
else
printf("Error reading file\n");
break;
}
printf("Received file\n");
}
while (1);
close(s);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SERVER_PORT 5959
#define MAX_PENDING 5
#define MAX_LINE 512
int sendbuf(int sock, void *buf, int buflen) {
char *pbuf = (char*) buf;
while (len > 0) {
int len = send(sock, pbuf, buflen, 0);
if (len <= 0) return -1;
pbuf += len;
buflen -= len;
}
return 0;
}
int sendstring(int sock, const char *str) {
if (!str) str = "";
return sendbuf(sock, str, strlen(str) + 1);
}
int sendfile(int sock, int fd) {
char buf[MAX_LINE];
struct stat s;
if (fstat(fd, &s) < 0) return -2;
int pos = ftell(fp);
if (pos == -1) return -2;
int file_size = s.st_size - pos;
int tmp_file_size = htonl(file_size);
if (sendbuf(sock, &tmp_file_size, sizeof(tmp_file_size)) < 0) return -1;
while (file_size > 0) {
int len = fread(buf, 1, min(sizeof(buf), file_size), fp);
if (len < 1) return -2;
if (sendbuf(sock, buf, len) < 0) return -1;
file_size -= len;
}
return 0;
}
int readstring(int sock, char *str, int maxlen) {
while (maxlen > 0) {
if (recv(sock, str, 1, 0) <= 0) return -1;
if (*str == '\0') return 0;
++str;
--maxlen;
}
return -2;
}
int main() {
char msg[MAX_LINE];
struct sockaddr_in sin;
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(SERVER_PORT);
/* setup passive open */
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror("simplex-talk: socket");
exit(1);
}
printf("Server is using address %s and port %d.\n", inet_ntoa(&(sin.sin_addr)), SERVER_PORT);
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
perror("simplex-talk: bind");
close(s);
exit(1);
}
printf("Server bind done.\n");
if (listen(s, MAX_PENDING) < 0) {
perror("simplex-talk: listen");
close(s);
exit(1);
}
printf("Server Listening.\n");
/* wait for connection, then receive and print text */
do {
int len = sizeof(sin);
int cli_s = accept(s, (struct sockaddr *)&sin, &len);
if (cli_s < 0) {
perror("simplex-talk: accept");
close(s);
exit(1);
}
printf("Client connected\n");
do {
if (readstring(cli_s, msg, sizeof(msg)) < 0) {
printf("Error reading request\n");
break;
}
if (strcmp(msg, "bye") == 0) break;
printf("File requested: %s\n", msg);
FILE *fp = fopen(msg, "rb");
if (!fp)
{
printf("Cannot open file\n");
if (sendstring(cli_s, "Cannot open file") < 0) {
printf("Error sending reply\n");
break;
}
continue;
}
printf("File found :) \n");
if (sendstring(cli_s, "OK") < 0) {
printf("Error sending reply\n");
fclose(fp);
break;
}
ret = sendfile(cli_s, fp);
fclose(fp);
if (ret < 0) {
printf("Error sending file\n");
break;
}
printf("File sent to client\n");
}
while (1);
close(cli_s);
}
while (1);
close(s);
return 0;
}
I'm baffled by an issue i'm having when trying to get UTF-8 and swedish characters ÅÄÖ to print correctly in a UNIX-talk clone.
I've set the locale with setLocale() to sv_SE and i'm using wide characters to try to display the characters correctly, lowercase åäö works just fine but somehow the capital variant does not work.
Below is the code in its entirety, i suspect that there is something i'm missing with the character sizes in reader() sender() or putch().
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ncurses.h>
#include <pthread.h>
#include <signal.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sched.h>
#include <pthread.h>
#include <locale.h>
#include <wchar.h>
#define MATRIXSIZE 1000
#define STACKSIZE 10000
#define CHATLEN 2048
#include <syslog.h>
int r = 0, i = 0, mode = -1;
wchar_t mybuf[CHATLEN], tmbuf[CHATLEN];
WINDOW *me;
WINDOW *them;
struct stuff {
unsigned int col, row, size, realsize;
pid_t childpid;
pid_t mainpid;
char matrix[MATRIXSIZE];
char nukeline[1024];
int nukesize;
int terminate;
struct massaskit {
int writechan;
int readchan;
int sockfd;
struct sockaddr_in server;
struct sockaddr_in writeclient;
struct sockaddr_in readclient;
int c;
struct hostent *serverhost;
char hostname[256];
uint16_t port;
} bertil;
};
pthread_mutex_t scr_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_t sendthread; // Thread that listens to user's typing, and
// puts the characters on the screen, and
// transmits them over the network.
pthread_t readthread; // Thread that reads characters from the network
// and shows them on the screen.
int srv1(void *ptr)
{
struct massaskit *sockstuff = (struct massaskit *)ptr;
int opt = 1;
if ((sockstuff->sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket();");
exit(-1);
}
sockstuff->server.sin_family = AF_INET;
sockstuff->server.sin_addr.s_addr = INADDR_ANY;
sockstuff->server.sin_port = htons(sockstuff->port);
setsockopt(sockstuff->sockfd, SOCK_STREAM, SO_REUSEADDR, &opt,
sizeof(opt));
if (bind
(sockstuff->sockfd, (struct sockaddr *)&sockstuff->server,
sizeof(sockstuff->server)) < 0) {
perror("bind failed");
exit(-1);
}
if ((listen(sockstuff->sockfd, 3)) < 0)
{
perror("listen");
exit(-1);
}
sockstuff->c = sizeof(struct sockaddr_in);
printf("waiting for readchan on %i sockstuff->port..\n",
sockstuff->port);
sockstuff->readchan =
accept(sockstuff->sockfd,
(struct sockaddr *)&sockstuff->readclient,
(socklen_t *) & sockstuff->c);
printf("got a connection! now need a connection on writechan (p:%i)\n",
sockstuff->port);
sockstuff->writechan =
accept(sockstuff->sockfd,
(struct sockaddr *)&sockstuff->writeclient,
(socklen_t *) & sockstuff->c);
printf("got a connection! both read/write (p:%i)\n", sockstuff->port);
shutdown(sockstuff->writechan, SHUT_RD);
shutdown(sockstuff->readchan, SHUT_WR);
if (close(sockstuff->sockfd) != 0)
{
exit(1);
}
return 0;
}
int cli1(void *ptr)
{
/* srv1 starts with readchan, we start with writechan :-) */
struct massaskit *sockstuff = (struct massaskit *)ptr;
int opt = 1;
sockstuff->sockfd = -1; /* make it broken so other function understand */
if ((sockstuff->writechan = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("writechan->socket()");
exit(-1);
}
else
{
printf("socket init is ok.got fd[%i] [writechan]\n",
sockstuff->writechan);
}
setsockopt(sockstuff->writechan, SOCK_STREAM, SO_REUSEADDR, &opt,
sizeof(opt));
if ((sockstuff->serverhost =
gethostbyname(sockstuff->hostname)) == NULL) {
perror("error in resolving hostname :/\n");
exit(-1);
}
else
{
printf("ok, resolved host, now making connection [writechan]!\n");
}
memset(&sockstuff->server, '\0', sizeof(struct in_addr));
sockstuff->server.sin_family = AF_INET;
memcpy(&sockstuff->server.sin_addr.s_addr,
sockstuff->serverhost->h_addr,
(size_t) sockstuff->serverhost->h_length);
sockstuff->server.sin_port = htons(sockstuff->port);
if (connect
(sockstuff->writechan, (struct sockaddr *)&sockstuff->server,
sizeof(sockstuff->server)) == -1) {
perror("connection");
exit(-1);
}
else
{
printf("%s%s", "writechan established,starting readchan and", "sleeping 2s so other end can initalize the readchan.\n");
}
if ((sockstuff->readchan = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket();");
exit(-1);
}
else
{
printf("readchan socket init ok.\n");
fflush(stdout);
}
setsockopt(sockstuff->readchan, SOCK_STREAM, SO_REUSEADDR, &opt,
sizeof(opt));
if ((sockstuff->serverhost = gethostbyname(sockstuff->hostname)) == NULL)
{
perror("error in recieve channel, could'nt resolve host bailing.\n");
exit(-1);
}
else
{
printf("readchan could resolve host nice shit alabama\n");
fflush(stdout);
}
memset(&sockstuff->server, '\0', sizeof(struct in_addr));
sockstuff->server.sin_family = AF_INET;
memcpy(&sockstuff->server.sin_addr.s_addr,
sockstuff->serverhost->h_addr,
(size_t) sockstuff->serverhost->h_length);
sockstuff->server.sin_port = htons(sockstuff->port);
if (connect
(sockstuff->readchan, (struct sockaddr *)&sockstuff->server,
sizeof(sockstuff->server)) == -1) {
perror("connect()");
exit(-1);
}
else
{
printf("read chan estabiled. starting program!\n");
fflush(stdout);
}
return 0;
}
void putch(WINDOW * win, wchar_t ch)
{
syslog(LOG_INFO, "%04x", ch);
if (ch == 4 || ch == 7) // Translate left-arrow, backspace to CTL-H
ch = '\b';
if(ch < ' ' && ch != '\t' &&
ch != '\n' && ch != '\b'
)
{
return;
}
pthread_mutex_lock(&scr_mutex); // Get exclusive access to screen.
wechochar(win, ch);
if (ch == '\b')
{
wdelch(win);
refresh();
}
pthread_mutex_unlock(&scr_mutex);
}
void setupscreen()
{
int rows, cols;
initscr();
cbreak();
noecho();
intrflush(stdscr, FALSE);
rows = (LINES - 3) / 2;
cols = COLS - 2;
me = newwin(rows, cols, 1, 1);
them = newwin(rows, cols, rows + 2, 1);
idlok(me, TRUE);
scrollok(me, TRUE);
keypad(me, TRUE);
idlok(them, TRUE);
scrollok(them, TRUE);
border(0, 0, 0, 0, 0, 0, 0, 0);
move(rows + 1, 1);
hline(0, cols);
refresh();
}
void* sender(void *ptr) {
struct stuff *s = (struct stuff *)ptr;
setupscreen();
int ch;
while (1)
{
if (i > CHATLEN - 1)
{
i = 0;
}
ch = wgetch(me);
mybuf[i] = ch;
if (ch == KEY_RESIZE)
{
clear();
endwin();
setupscreen();
wchar_t *p = &mybuf[0];
while (&(*p) < &mybuf[CHATLEN - 1])
{
putch(me, (*p));
p++;
}
p = &tmbuf[0];
while (&(*p) < &tmbuf[CHATLEN - 1])
{
putch(them, (*p));
p++;
}
refresh();
}
else
{
putch(me, mybuf[i]);
int writefd = s->bertil.writechan;
write(writefd, &mybuf[i], sizeof(mybuf[i]));
}
i++;
}
pthread_cancel(sendthread);
return NULL;
}
void* reader(void *ptr) {
struct stuff *s = (struct stuff *)ptr;
int ch;
while(1)
{
if(r> CHATLEN - 1)
{
r = 0;
}
int readfd=s->bertil.readchan;
if((read(readfd,&ch,sizeof(ch))) == 0)
{
endwin();
refresh();
return 0;
}
tmbuf[r] = ch;
putch(them, tmbuf[r]);
r++;
}
pthread_cancel(readthread);
return NULL;
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "sv_SE");
memset(mybuf, 0, CHATLEN);
memset(tmbuf, 0, CHATLEN);
struct stuff s;
memset(&s, 0, sizeof(struct stuff));
if (argc == 1)
{
printf("usage %s port [host server on port]\n", argv[0]);
printf("usage %s port host [connecto host:port]\n", argv[0]);
exit(1);
}
else if (argc == 3)
{
s.bertil.port = (uint16_t) atoi(argv[1]);
memset(s.bertil.hostname, 0, 256);
memcpy(s.bertil.hostname, argv[2], strlen(argv[2]));
cli1(&s.bertil);
}
else if (argc == 2)
{
s.bertil.port = (uint16_t) atoi(argv[1]);
srv1(&s.bertil);
}
pthread_create(&readthread, NULL, reader, &s);
pthread_create(&sendthread, NULL, sender, &s);
pthread_join(sendthread, NULL);
pthread_join(readthread,NULL);
}
If you want to try out the program compile it as follows:
gcc file.c -lpthread -lncurses
To serve:
./a.out 1234
To connect:
./a.out 1234 localhost
Any help would be greatly appreciated!
Actually, wchar_t is enough bits, and sign-extension problems do not appear likely.
However, the application using ncurses in two threads (sender and reader), and unless you have compiled it specially and allowed for mutexes, it won't work well. ncurses (like any implementation of curses) uses global variables for maintaining the screen. Multiple threads will exercise the library in unexpected ways.
Further reading:
Official releases (ncurses FAQ):
5.7 (2 November 2008). This provides rudimentary support for threaded applications. It also distributes tack separately.
curs_threads - curses thread support (ncurses manual page)
Try using unsigned char instead of int in your functions.
In putch, you do not need wchar_t, unsigned char (one byte) is fine in UTF-8 (http://www.science.co.il/language/Character-code.asp?s=1252).
I wrote a man-in-the-middle/proxy server initially on my mac. Essentially the proxy creates a socket and waits for a connection, then connects to another application. This works flawlessly on in OS X and in OpenBSD; however, when porting the proxy to Ubuntu, it doesn't work as intended.
There is two instances of this proxy running, listening on two separate ports. When this is ran on Ubuntu, all the traffic goes through a single port. I also run into a problem when setting the socket to nonblocking (through fcntl) that sometimes it fails with "Invalid Argument"
I am using sys/socket.
Any pitfalls during this port that I am missing?
EDIT:
I believe there are two problems. One being the Invalid Argument, the other being that traffic is being pushed to different ports.
Service 1 binds to Proxy instance 1 which then binds back to the appropriate service on the black box, which kicks off service 2. However, for some reason on Ubuntu it connects to the Instance 1 which is listening on the incorrect port.
EDIT Solution to Invalid Argument for fcntl:
Found out why i was getting the invalid argument, sadly i'm still having the other issue.
fcntl(fd, cmd, arg)
cmd - F_SETFL(long)
I was passing in a pointer to an int instead of the long primitive.
EDIT:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/ftp.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
void cleanup(int sig)
{
syslog(LOG_INFO, "Cleaning up...");
exit(0);
}
void sigreap(int sig)
{
int status;
pid_t p;
while ((p = waitpid(-1, &status, WNOHANG)) > 0) {
syslog(LOG_INFO, "sigreap: pid=%d, status=%d\n", (int) p, status);
}
/* doh! */
signal(SIGCHLD, sigreap);
}
void set_nonblock(int fd)
{
long fl;
int x;
fl = fcntl(fd, F_GETFL);
if (fl < 0) {
syslog(LOG_ERR, "fcntl F_GETFL: FD %d: %s", fd, strerror(errno));
exit(1);
}
fl |= O_NONBLOCK;
x = fcntl(fd, F_SETFL, fl);
if (x < 0) {
syslog(LOG_ERR, "fcntl F_SETFL: FD %d: %s", fd, strerror(errno));
exit(1);
}
}
int create_server_sock(char *addr, int port)
{
int addrlen, s, on = 1, x;
static struct sockaddr_in client_addr;
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
perror("socket"), exit(1);
addrlen = sizeof(client_addr);
memset(&client_addr, '\0', addrlen);
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = INADDR_ANY; //inet_addr(addr);
client_addr.sin_port = htons(port);
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4);
x = bind(s, (struct sockaddr *) &client_addr, addrlen);
if (x < 0)
perror("bind"), exit(1);
x = listen(s, 5);
if (x < 0)
perror("listen"), exit(1);
return s;
}
int open_remote_host(char *host, int port)
{
struct sockaddr_in rem_addr;
int len, s, x;
struct hostent *H;
int on = 1;
H = gethostbyname(host);
if (!H)
return (-2);
len = sizeof(rem_addr);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
return s;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4);
len = sizeof(rem_addr);
memset(&rem_addr, '\0', len);
rem_addr.sin_family = AF_INET;
memcpy(&rem_addr.sin_addr, H->h_addr, H->h_length);
rem_addr.sin_port = htons(port);
x = connect(s, (struct sockaddr *) &rem_addr, len);
if (x < 0) {
close(s);
return x;
}
set_nonblock(s);
return s;
}
int get_hinfo_from_sockaddr(struct sockaddr_in addr, int len, char *fqdn)
{
struct hostent *hostinfo;
hostinfo = gethostbyaddr((char *) &addr.sin_addr.s_addr, len, AF_INET);
if (!hostinfo) {
sprintf(fqdn, "%s", inet_ntoa(addr.sin_addr));
return 0;
}
if (hostinfo && fqdn)
sprintf(fqdn, "%s [%s]", hostinfo->h_name, inet_ntoa(addr.sin_addr));
return 0;
}
int wait_for_connection(int s)
{
int newsock;
socklen_t len;
static struct sockaddr_in peer;
len = sizeof(struct sockaddr);
newsock = accept(s, (struct sockaddr *) &peer, &len);
/* dump_sockaddr (peer, len); */
if (newsock < 0) {
if (errno != EINTR)
perror("accept");
}
get_hinfo_from_sockaddr(peer, len, client_hostname);
set_nonblock(newsock);
return (newsock);
}
static int print_bytes(char * buf, ssize_t length)
{
int i = 0, ascii_off = 0, hex_off = 0;
char * hex_bytes = (char *) calloc(32*2,1);
char * ascii_bytes = (char *) calloc(32*2,1);
for( i = 0; i < length; i++)
{
hex_off += sprintf(hex_bytes+hex_off,"%02X ",(unsigned char)buf[i]);
if(buf[i] >= '!' && buf[i] <= '~')
ascii_off += sprintf(ascii_bytes+ascii_off,"%c ",buf[i]);
else
ascii_off += sprintf(ascii_bytes+ascii_off,". ");
if( ((i+1) % 16 == 0) || i == length-1 )
{
fprintf(stderr,"%-48s\t%s\n",hex_bytes,ascii_bytes);
free(hex_bytes);
free(ascii_bytes);
hex_bytes = (char *) calloc(32*2,1);
ascii_bytes = (char *) calloc(32*2,1);
ascii_off = 0;
hex_off = 0;
if(i != length-1)
fprintf(stderr,"\t");
}
}
free(hex_bytes);
free(ascii_bytes);
return 0;
}
int mywrite(int fd, char *buf, int *len)
{
int x = write(fd, buf, *len);
print_bytes(buf,*len);
if (x < 0)
return x;
if (x == 0)
return x;
if (x != *len)
memmove(buf, buf+x, (*len)-x);
*len -= x;
return x;
}
void service_client(int fd1, int fd2, int injfd)
{
int maxfd;
cfd = fd1;
sfd = fd2;
char *sbuf;
char *cbuf;
int x, n;
int cbo = 0;
int sbo = 0;
int ibo = 0;
fd_set R;
int max_clients = 30;
int i = 0,s = 0, addrlen;
struct sockaddr_in address;
sbuf = malloc(BUF_SIZE);
cbuf = malloc(BUF_SIZE);
cntrlbuf = calloc(1,BUF_SIZE);
char * injbuf = malloc(BUF_SIZE);
maxfd = cfd > sfd ? cfd : sfd;
maxfd = injfd > maxfd ? injfd : maxfd;
maxfd++;
maxfd++;
struct inj_con * ptr;
while (1) {
struct timeval to;
if (cbo) {
process_packet(cbuf,&cbo,sfd);
}
if (sbo) {
process_packet(sbuf,&sbo,cfd);
}
if (ibo) {
process_packet(injbuf,&ibo,cfd);
}
if (cntrlo) {
fprintf(stderr,"\033[33;1mControl->(%d), len = 0x%x (%d):\033[0m\n\t",cfd,cntrlo,cntrlo);
if (mywrite(cfd, cntrlbuf, &cntrlo) < 0 && errno != EWOULDBLOCK) {
syslog(LOG_ERR, "write %d: %s", cfd, strerror(errno));
exit(1);
}
}
FD_ZERO(&R);
if (cbo < BUF_SIZE)
FD_SET(cfd, &R);
if (sbo < BUF_SIZE)
FD_SET(sfd, &R);
if (ibo < BUF_SIZE)
FD_SET(injfd, &R);
to.tv_sec = 0;
to.tv_usec = 1000;
x = select(max_clients+3, &R, 0, 0, &to);
if (x > 0 || cntrl_q->item_count > 0) {
if (FD_ISSET(injfd, &R)) {
int new_socket;
if((new_socket = accept(injfd, (struct sockaddr *) &address, (socklen_t *) &addrlen)) < 0)
{
perror("accept");
exit(1);
}
// Truncated
//
}
char * temp_pkt;
if (FD_ISSET(cfd, &R)) {
temp_pkt = (char *) calloc(BUF_SIZE,1);
n = read(cfd, temp_pkt, BUF_SIZE);
syslog(LOG_INFO, "read %d bytes from CLIENT (%d)", n, cfd);
if (n > 0) {
push_msg(s_q,temp_pkt,n);
} else {
free(temp_pkt);
close(cfd);
close(sfd);
close_injection_sockets();
close(injfd);
_exit(0);
}
}
if (FD_ISSET(sfd, &R)) {
temp_pkt = (char *) calloc(BUF_SIZE,1);
n = read(sfd, temp_pkt, BUF_SIZE);
syslog(LOG_INFO, "read %d bytes from SERVER (%d)\n", n, sfd);
if (n > 0) {
push_msg(c_q,temp_pkt,n);
} else {
free(temp_pkt);
close(sfd);
close(cfd);
close_injection_sockets();
close(injfd);
_exit(0);
}
}
if(cntrlo == 0 && cntrl_q->front != NULL)
{
struct msg * tmp = next_msg(cntrl_q);
if(tmp != NULL)
{
memcpy(cntrlbuf,tmp->msg,tmp->len);
cntrlo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
if(sbo == 0 && c_q->front != NULL)
{
struct msg * tmp = next_msg(c_q);
if(tmp != NULL)
{
memcpy(sbuf,tmp->msg,tmp->len);
sbo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
if(cbo == 0 && s_q->front != NULL)
{
struct msg * tmp = next_msg(s_q);
if(tmp != NULL)
{
memcpy(cbuf,tmp->msg,tmp->len);
cbo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
if(ibo == 0 && inj_q->front != NULL)
{
struct msg * tmp = next_msg(inj_q);
if(tmp != NULL)
{
memcpy(injbuf,tmp->msg,tmp->len);
ibo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
} else if (x < 0 && errno != EINTR) {
close(sfd);
close(cfd);
_exit(0);
}
}
}
static int create_injection_sock(int injectionport)
{
struct sockaddr_in serv_addr;
int portno = injectionport;
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd <0)
{
perror("ERROR: opening socket");
exit(1);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
{
perror("ERROR: on bind");
exit(1);
}
if (listen(sockfd,5) < 0 )
{
perror("listen injection");
exit(1);
}
return sockfd;
}
int main(int argc, char *argv[])
{
if (!(5 == argc || 6 == argc)) {
fprintf(stderr, "usage: %s laddr lport rhost rport [injectionport]\n", argv[0]);
exit(1);
}
char *localaddr = strdup(argv[1]);
int localport = atoi(argv[2]);
char *remoteaddr = strdup(argv[3]);
int remoteport = atoi(argv[4]);
int injectionport;
if(argc == 6)
injectionport = atoi(argv[5]);
int client, server;
int master_sock;
int injection_sock = -1;
cntrl_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
inj_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
s_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
c_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
identities = (struct item_queue *) calloc(1,sizeof(struct item_queue));
assert(localaddr);
assert(localport > 0);
assert(remoteaddr);
assert(remoteport > 0);
if(argc == 6)
assert(injectionport > 0);
openlog(argv[0], LOG_PID, LOG_LOCAL4);
signal(SIGINT, cleanup);
signal(SIGCHLD, sigreap);
if(argc == 6)
injection_sock = create_injection_sock(injectionport);
master_sock = create_server_sock(localaddr, localport);
for (;;) {
if ((client = wait_for_connection(master_sock)) < 0)
continue;
if ((server = open_remote_host(remoteaddr, remoteport)) < 0)
continue;
if (!fork()) {
service_client(client, server, injection_sock);
}
close(client);
close(server);
}
}
Just a guess, but I think your problem is the way you're handling the injection socket. I don't know what you deleted in the // TRUNCATED code block, but basically what you're doing is this:
// Parent process -- bind & listen on the injection socket
injfd = socket(...);
bind(injfd, ...);
listen(injfd, ...);
...
while (1)
{
client = wait_for_connection();
...
if (!fork())
{
// Each child process
...
if (stuff && FD_ISSET(injfd, &R)) {
new_socket = accept(injfd);
...
}
...
}
...
}
In other words, all of your child processes are listening on the same injection socket and trying to accept connections. I'm not sure if this even well-defined behavior, but even in the best case, when a new connection arrives on the injection port, the process that is going to accept the connection could be random and uncontrollable. It could be any of the child processes or even the parent process.
In order to control that behavior, you need to decide who should be listening for connections on the injection socket. If only the parent should be listening, then only the parent should call accept() on it or pass it as an argument to select(). Likewise, if only a particular child should be listening, then only that child should call accept() or pass it to select(). All other processes that are ignoring that socket should close() it at their earliest convenience (e.g. immediately after fork() returns) to avoid leaking file descriptors.
Turns out that SO_REUSEADDR socket option was the issue. I removed me setting that socket option and everything worked out.
This Lead me to the solution.