Related
I have a client program and a server program. There could be multiple servers and multiple
clients that can connect to multiple servers of there choice
The client program lists a menu
connect 4000 // connects to server on port 4000
bid 1000 4000 // send a bid value of 1000 to the server at port 4000
Now a server may recieve bids from several clients connected to it and keeps track of the highest
bid till now. Whenever a new bid is placed the server sends a broadcast to each client connected
to it one by one like - write(users[i].sock_fd, msg, size).
How do I listen to this message on the client side ?
There are two things here
The client needs to listen to the message sent by server.
The client is also reading the text or menu items (connect and bid) from command line from the user.
I have coded the part 2) But confused how to code 1) into client and simultaneously make the 2) also working
Client code :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define BUF_SIZE 128
#define MAX_AUCTIONS 5
#ifndef VERBOSE
#define VERBOSE 0
#endif
#define ADD 0
#define SHOW 1
#define BID 2
#define QUIT 3
/* Auction struct - this is different than the struct in the server program
*/
typedef struct auction_data
{
int sock_fd;
char item[BUF_SIZE];
int current_bid;
} auction_data;
auction_data *auction_data_ptr;
/* Displays the command options available for the user.
* The user will type these commands on stdin.
*/
void print_menu()
{
printf("The following operations are available:\n");
printf(" show\n");
printf(" add <server address> <port number>\n");
printf(" bid <item index> <bid value>\n");
printf(" quit\n");
}
/* Prompt the user for the next command
*/
void print_prompt()
{
printf("Enter new command: ");
fflush(stdout);
}
/* Unpack buf which contains the input entered by the user.
* Return the command that is found as the first word in the line, or -1
* for an invalid command.
* If the command has arguments (add and bid), then copy these values to
* arg1 and arg2.
*/
int parse_command(char *buf, int size, char *arg1, char *arg2)
{
int result = -1;
char *ptr = NULL;
if (strncmp(buf, "show", strlen("show")) == 0)
{
return SHOW;
}
else if (strncmp(buf, "quit", strlen("quit")) == 0)
{
return QUIT;
}
else if (strncmp(buf, "add", strlen("add")) == 0)
{
result = ADD;
}
else if (strncmp(buf, "bid", strlen("bid")) == 0)
{
result = BID;
}
ptr = strtok(buf, " "); // first word in buf
ptr = strtok(NULL, " "); // second word in buf
if (ptr != NULL)
{
strncpy(arg1, ptr, BUF_SIZE);
}
else
{
return -1;
}
ptr = strtok(NULL, " "); // third word in buf
if (ptr != NULL)
{
strncpy(arg2, ptr, BUF_SIZE);
return result;
}
else
{
return -1;
}
return -1;
}
/* Connect to a server given a hostname and port number.
* Return the socket for this server
*/
int add_server(char *hostname, int port)
{
// Create the socket FD.
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0)
{
perror("client: socket");
exit(1);
}
// Set the IP and port of the server to connect to.
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
struct addrinfo *ai;
/* this call declares memory and populates ailist */
if (getaddrinfo(hostname, NULL, NULL, &ai) != 0)
{
close(sock_fd);
return -1;
}
/* we only make use of the first element in the list */
server.sin_addr = ((struct sockaddr_in *)ai->ai_addr)->sin_addr;
// free the memory that was allocated by getaddrinfo for this list
freeaddrinfo(ai);
// Connect to the server.
if (connect(sock_fd, (struct sockaddr *)&server, sizeof(server)) == -1)
{
perror("client: connect");
close(sock_fd);
return -1;
}
if (VERBOSE)
{
fprintf(stderr, "\nDebug: New server connected on socket %d. Awaiting item\n", sock_fd);
}
return sock_fd;
}
/* ========================= Add helper functions below ========================
* Please add helper functions below to make it easier for the TAs to find the
* work that you have done. Helper functions that you need to complete are also
* given below.
*/
/* Print to standard output information about the auction
*/
void print_auctions(struct auction_data *a, int size)
{
printf("Current Auctions:\n");
for (int i = 0; i < size; i++)
{
struct auction_data auction_data = a[i];
printf("(%d) %s bid = %d\n", i, auction_data.item, auction_data.current_bid);
}
/* TODO Print the auction data for each currently connected
* server. Use the follosing format string:
* "(%d) %s bid = %d\n", index, item, current bid
* The array may have some elements where the auction has closed and
* should not be printed.
*/
}
/* Process the input that was sent from the auction server at a[index].
* If it is the first message from the server, then copy the item name
* to the item field. (Note that an item cannot have a space character in it.)
*/
void update_auction(char *buf, int size, struct auction_data *a, int index)
{
// TODO: Complete this function
// fprintf(stderr, "ERROR malformed bid: %s", buf);
// printf("\nNew bid for %s [%d] is %d (%d seconds left)\n", );
}
int main(void)
{
char name[BUF_SIZE];
int size = 0;
// Declare and initialize necessary variables
// TODO
// Get the user to provide a name.
printf("Please enter a username: ");
fflush(stdout);
int num_read = read(STDIN_FILENO, name, BUF_SIZE);
printf("%s-name\n", name);
if (num_read <= 0)
{
fprintf(stderr, "ERROR: read from stdin failed\n");
exit(1);
}
print_menu();
// TODO
char server_reply[2000];
while (1)
{
print_prompt();
char *command;
scanf("%m[^\n]s", &command);
getchar();
char arg1[100];
char arg2[100];
int commandNumber = parse_command(command, 1000, arg1, arg2);
char dest[100] = "";
strcpy(dest, name);
dest[strlen(dest) - 1] = '\0';
if (commandNumber == ADD)
{
printf("%s-name4\n", dest);
int port = atoi(arg2);
int sock_fd = add_server(arg1, port);
printf("%s-server\n", server_reply);
write(sock_fd, dest, strlen(dest));
auction_data_ptr = (auction_data *)realloc(auction_data_ptr, (size + 1) * sizeof(auction_data_ptr));
auction_data_ptr[size].sock_fd = sock_fd;
size++;
}
else if (commandNumber == SHOW)
{
print_auctions(auction_data_ptr, size);
}
else if (commandNumber == BID)
{
int itemIndex = atoi(arg1);
int bidValue = atoi(arg2);
printf("%d-test\n", auction_data_ptr[itemIndex].sock_fd);
send(auction_data_ptr[itemIndex].sock_fd, arg2, strlen(arg2), 0);
}
else if (commandNumber == QUIT)
{
}
// TODO
}
return 0; // Shoud never get here
}
Server Code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef PORT
#define PORT 30000
#endif
#define MAX_BACKLOG 5
#define MAX_CONNECTIONS 20
#define BUF_SIZE 128
#define MAX_NAME 56
int verbose = 0;
struct user
{
int sock_fd;
char name[MAX_NAME];
int bid;
};
typedef struct
{
char *item;
int highest_bid; // value of the highest bid so far
int client; // index into the users array of the top bidder
} Auction;
/*
* Accept a connection. Note that a new file descriptor is created for
* communication with the client. The initial socket descriptor is used
* to accept connections, but the new socket is used to communicate.
* Return the new client's file descriptor or -1 on error.
*/
int accept_connection(int fd, struct user *users)
{
int user_index = 0;
while (user_index < MAX_CONNECTIONS && users[user_index].sock_fd != -1)
{
user_index++;
}
if (user_index == MAX_CONNECTIONS)
{
fprintf(stderr, "server: max concurrent connections\n");
return -1;
}
int client_fd = accept(fd, NULL, NULL);
if (client_fd < 0)
{
perror("server: accept");
close(fd);
exit(1);
}
users[user_index].sock_fd = client_fd;
users[user_index].name[0] = '\0';
return client_fd;
}
/* Remove \r\n from str if the characters are at the end of the string.
* Defensively assuming that \r could be the last or second last character.
*/
void strip_newline(char *str)
{
if (str[strlen(str) - 1] == '\n' || str[strlen(str) - 1] == '\r')
{
if (str[strlen(str) - 2] == '\r')
{
str[strlen(str) - 2] = '\0';
}
else
{
str[strlen(str) - 1] = '\0';
}
}
}
/*
* Read a name from a client and store in users.
* Return the fd if it has been closed or 0 otherwise.
*/
int read_name(int client_index, struct user *users)
{
int fd = users[client_index].sock_fd;
/* Note: This is not the best way to do this. We are counting
* on the client not to send more than BUF_SIZE bytes for the
* name.
*/
int num_read = read(fd, users[client_index].name, MAX_NAME);
if (num_read == 0)
{
users[client_index].sock_fd = -1;
return fd;
}
users[client_index].name[num_read] = '\0';
strip_newline(users[client_index].name);
if (verbose)
{
fprintf(stderr, "[%d] Name: %s\n", fd, users[client_index].name);
}
/*
if (num_read == 0 || write(fd, buf, strlen(buf)) != strlen(buf)) {
users[client_index].sock_fd = -1;
return fd;
}
*/
return 0;
}
/* Read a bid from a client and store it in bid.
* If the client does not send a number, bid will be set to -1
* Return fd if the socket is closed, or 0 otherwise.
*/
int read_bid(int client_index, struct user *users, int *bid)
{
printf("inside bid\n");
int fd = users[client_index].sock_fd;
char buf[BUF_SIZE];
char *endptr;
int num_read = read(fd, buf, BUF_SIZE);
if (num_read == 0)
{
return fd;
}
buf[num_read] = '\0';
if (verbose)
{
fprintf(stderr, "[%d] bid: %s", fd, buf);
}
// Check if the client sent a valid number
// (We are not checking for a good bid here.)
errno = 0;
*bid = strtol(buf, &endptr, 10);
if (errno != 0 || endptr == buf)
{
*bid = -1;
}
return 0;
}
void broadcast(struct user *users, char *msg, int size)
{
for (int i = 0; i < MAX_CONNECTIONS; i++)
{
if (users[i].sock_fd != -1)
{
if (write(users[i].sock_fd, msg, size) == -1)
{
// Design flaw: can't remove this socket from select set
close(users[i].sock_fd);
users[i].sock_fd = -1;
}
}
}
}
int prep_bid(char *buf, Auction *a, struct timeval *t)
{
// send item, current bid, time left in seconds
printf("robin2-%s-%d\n", a->item, a->highest_bid);
printf("robin-%ld\n", t->tv_sec);
sprintf(buf, "%s %d %ld", a->item, a->highest_bid, t->tv_sec);
printf("robin-bid2\n");
return 0;
}
/* Update auction if new_bid is higher than current bid.
* Write to the client who made the bid if it is lower
* Broadcast to all clients if the bid is higher
*/
int update_bids(int client_index, struct user *users,
int new_bid, Auction *auction, struct timeval *t)
{
char buf[BUF_SIZE];
if (new_bid > auction->highest_bid)
{
auction->highest_bid = new_bid;
auction->client = client_index;
prep_bid(buf, auction, t);
if (verbose)
{
fprintf(stderr, "[%d] Sending to %d:\n %s\n",
getpid(), users[client_index].sock_fd, buf);
}
broadcast(users, buf, strlen(buf) + 1);
}
else
{
fprintf(stderr, "Client %d sent bid that was too low. Ignored\n",
client_index);
}
return 0;
}
int main(int argc, char **argv)
{
argc = 7;
argv[1] = "-v";
argv[2] = "-t";
argv[3] = "5";
argv[4] = "-p";
argv[5] = "4000";
argv[6] = "robin";
Auction auction;
int opt;
int port = PORT;
struct timeval timeout;
struct timeval *time_ptr = NULL;
int minutes = 0;
while ((opt = getopt(argc, argv, "vt:p:")) != -1)
{
switch (opt)
{
case 'v':
verbose = 1;
break;
case 't':
minutes = atoi(optarg);
timeout.tv_sec = minutes * 60;
timeout.tv_usec = 0;
time_ptr = &timeout;
break;
case 'p':
port = atoi(optarg);
break;
default:
fprintf(stderr, "Usage: auction_server [-v] [-t timeout] [-p port] item\n");
exit(1);
}
}
if (optind >= argc)
{
fprintf(stderr, "Expected argument after options\n");
exit(1);
}
auction.item = argv[optind];
auction.client = -1;
auction.highest_bid = -1;
struct user users[MAX_CONNECTIONS];
for (int index = 0; index < MAX_CONNECTIONS; index++)
{
users[index].sock_fd = -1;
users[index].name[0] = '\0';
}
// Create the socket FD.
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0)
{
perror("server: socket");
exit(1);
}
// Set information about the port (and IP) we want to be connected to.
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = INADDR_ANY;
// This sets an option on the socket so that its port can be reused right
// away. Since you are likely to run, stop, edit, compile and rerun your
// server fairly quickly, this will mean you can reuse the same port.
int on = 1;
int status = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
(const char *)&on, sizeof(on));
if (status == -1)
{
perror("setsockopt -- REUSEADDR");
}
// This should always be zero. On some systems, it won't error if you
// forget, but on others, you'll get mysterious errors. So zero it.
memset(&server.sin_zero, 0, 8);
// Bind the selected port to the socket.
if (bind(sock_fd, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("server: bind");
close(sock_fd);
exit(1);
}
// Announce willingness to accept connections on this socket.
if (listen(sock_fd, MAX_BACKLOG) < 0)
{
perror("server: listen");
close(sock_fd);
exit(1);
}
if (verbose)
{
fprintf(stderr, "[%d] Ready to accept connections on %d\n",
getpid(), port);
}
// The client accept - message accept loop. First, we prepare to listen
// to multiple file descriptors by initializing a set of file descriptors.
int max_fd = sock_fd;
fd_set all_fds;
FD_ZERO(&all_fds);
FD_SET(sock_fd, &all_fds);
while (1)
{
// select updates the fd_set it receives, so we always use a copy
// and retain the original.
fd_set listen_fds = all_fds;
int nready;
if ((nready = select(max_fd + 1, &listen_fds, NULL, NULL, time_ptr)) == -1)
{
perror("server: select");
exit(1);
}
if (nready == 0)
{
char buf[BUF_SIZE];
sprintf(buf, "Auction closed: %s wins with a bid of %d\r\n",
users[auction.client].name, auction.highest_bid);
printf("%s", buf);
broadcast(users, buf, BUF_SIZE);
exit(0);
}
// Is it the original socket? Create a new connection ...
if (FD_ISSET(sock_fd, &listen_fds))
{
int client_fd = accept_connection(sock_fd, users);
if (client_fd != -1)
{
if (client_fd > max_fd)
{
max_fd = client_fd;
}
FD_SET(client_fd, &all_fds);
if (verbose)
{
fprintf(stderr, "[%d] Accepted connection on %d\n",
getpid(), client_fd);
}
}
}
// Next, check the clients.
for (int index = 0; index < MAX_CONNECTIONS; index++)
{
if (users[index].sock_fd > -1 && FD_ISSET(users[index].sock_fd, &listen_fds))
{
int client_closed = 0;
int new_bid = 0;
if (users[index].name[0] == '\0')
{
client_closed = read_name(index, users);
if (client_closed == 0)
{
char buf[BUF_SIZE];
prep_bid(buf, &auction, time_ptr);
if (verbose)
{
fprintf(stderr, "[%d] Sending to %d:\n %s\n",
getpid(), users[index].sock_fd, buf);
}
if (write(users[index].sock_fd, buf, strlen(buf) + 1) == -1)
{
fprintf(stderr, "Write to %d failed\n", sock_fd);
close(sock_fd);
}
}
}
else
{ // read a bid
client_closed = read_bid(index, users, &new_bid);
if (client_closed == 0)
{
update_bids(index, users, new_bid, &auction, time_ptr);
}
}
if (client_closed > 0)
{
FD_CLR(client_closed, &all_fds);
printf("Client %d disconnected\n", client_closed);
}
}
}
}
// Should never get here.
return 1;
}
Caveat: Because you've only posted partial code for server and client, this will be some suggestions.
Your client can attach/connect to multiple bid servers simultaneously. As such, it must be able to keep track of the multiple connections in a manner similar to a server.
Your main [stated] issue is that you're blocking the client on a user prompt (e.g. from stdin via scanf et. al.). Presently, this means that the client is "stuck" at user input prompt and can not field messages from the servers it is connected to. More on how to fix this below.
So, you'll have a bunch of code from the server that needs to be in the client with some minor differences. You may wish to generalize some of the server code a bit, so it can work both in server and client (e.g. you may want to move it to common.c).
You already have code in the server to handle multiple connections. The server needs a select mask that is the OR of the listen fd and all active client fds.
Likewise, your client needs a select mask that is the OR of the fd for user input (e.g. 0) and all active server connections.
Doing select on fd 0 and using stdio.h streams won't work too well. So, replace access to stdin with (e.g.) read(0,line_buffer,sizeof(line_buffer)). You do this if fd 0 is set in the select mask. The role is very similar to what your server does for the accept on sock_fd.
You'll need to allow for partial reads and append to the buffer until you see a newline. So, you'll have to do the work that fgets would normally do in assembling a whole line. Then, you can call parse_command.
Because read doesn't understand newline demarcations, the user could enter more than one line before you can do a read.
So, for user input of:
connect 4000\n
bid 100 4000\n
connect 5000\n
You may get partial reads of:
conn
ect
4000\nbid 100 4000
\nconnect
5000\n
You may also need to use the FIONREAD ioctl on the fd 0 to prevent blocking. And, you may need to set the kernel TTY layer into raw mode via termios calls.
The client now becomes very similar to your server code. It will handle [asynchronously] actions by any connected servers and user input.
A tip: Under the DRY principle ["don't repeat yourself"] ...
You already have a struct user in the server. The client will need something similar/identical, such as struct server. When generalizing the code, rather than having two distinct structs that do essentially the same thing, consider renaming the existing struct to (e.g.) struct connection
I have to implement two processes that have to interact through TCP, basically a file transfer between a server and a client (school homework). Everything work for the first file transmission but when the client ends its job the server crashes because of error - accept() failed: Bad file descriptor.
server_main.c
int main (int argc, char *argv[]) {
char cwd[100];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("App[starting]: " ANSI_COLOR_CYAN "%s" ANSI_COLOR_RESET "\n", cwd);
} else {
printf("App[quitting]: " ANSI_COLOR_RED "UNABLE TO LOCATE WDIR" ANSI_COLOR_RESET "\n");
return 1;
}
// procedo solo se vengono passati esattamente due parametri allo script
// 1. il nome dello script (default)
// 2. la porta
if (argc == 2) {
int passiveSocket = startTcpServer(argv[1]);
runIterativeTcpInstance(passiveSocket);
close(passiveSocket);
return 0;
}
// se arrivo qui ho app crash
printf("App[quitting]: " ANSI_COLOR_RED "USAGE: <PORT>" ANSI_COLOR_RESET "\n");
return 1;
}
gj_server.c
int startTcpServer(const char* port) {
uint16_t i_port;
int sockfd;
sockfd = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
parsePort(port, &i_port);
bindToAny(sockfd, i_port);
Listen(sockfd, 516);
printf("Server[listening]: " ANSI_COLOR_GREEN "PORT = %d, BACKLOG = 516" ANSI_COLOR_RESET "\n", (int) i_port);
return sockfd;
}
void runIterativeTcpInstance(int passiveSock) {
struct sockaddr_in cli_addr;
socklen_t addr_len = sizeof(struct sockaddr_in);
while(1) {
printf("Server[accepting]: " ANSI_COLOR_YELLOW "WAITING FOR A CONNECTION..." ANSI_COLOR_RESET "\n");
int connSock = Accept(passiveSock , (struct sockaddr *) &cli_addr, &addr_len);
doTcpJob(connSock);
close(connSock);
}
}
void doTcpJob(int connSock) {
//TODO: uscire in caso di errore di una delle due fasi
printf("Server[connection]:" ANSI_COLOR_GREEN "STARTED A NEW ON CONNECTION (PID=%d)" ANSI_COLOR_RESET "\n", getpid());
char request[256];
initStr(request, 256);
if(doTcpReceive(connSock, request) == 0)
doTcpSend(connSock, request);
else {
printf("Server[error]: " ANSI_COLOR_RED "INVALID REQUEST FROM CLIENT: %s" ANSI_COLOR_RESET "\n", request);
char err_buff[7] = "-ERR\r\n";
send(connSock, err_buff, 6, 0);
//close(connSock);
}
//close(connSock);
}
int doTcpReceive(int connSock, char *request) {
struct timeval tval;
fd_set cset;
FD_ZERO(&cset);
FD_SET(connSock, &cset);
tval.tv_sec = 15;
tval.tv_usec = 0;
if(Select(FD_SETSIZE, &cset, NULL, NULL, &tval) == 1) {
// TODO: INSERIRE LOGICA RECEIVE QUI
ssize_t read = 0;
while (reqCompleted(request) == -1 ) {
ssize_t received = Recv(connSock, request, 256, 0);
read += received;
}
printf("Server[receive]: " ANSI_COLOR_CYAN "RECEIVED %s" ANSI_COLOR_RESET "\n", request);
return checkRequest(request);
}
// esco per timeout
//close(connSock);
return -1;
}
void doTcpSend(int connSock, char *request) {
// TODO: INSERIRE LOGICA SEND QUI
// 1. send ok message
char ok_msg[6] = "+OK\r\n";
send(connSock, ok_msg, 5, 0);
// 2. send file size
FILE *fp = fopen(request, "rb+");
// qui il file dovrebbe esistere, ma potrebbe essere inacessibile o
// l'apertura potrebbe fallire
if (fp == NULL) {
printf("SERVER[READING]" ANSI_COLOR_RED "CANNOT OPEN FILE %s " ANSI_COLOR_RESET "\n", request);
char err_buff[7] = "-ERR\r\n";
send(connSock, err_buff, 6, 0);
//close(connSock);
return;
}
struct stat stat_buf;
if (fstat(fileno(fp), &stat_buf) == 0) {
long f_size = stat_buf.st_size;
long f_time = stat_buf.st_mtime;
uint32_t net_f_size = htonl(f_size);
send(connSock, &net_f_size, 4, 0);
// 3. send file content
ssize_t sent = 0;
printf("fsize: %d", (int)f_size);
while (sent < f_size) {
sent += sendfile(connSock, fileno(fp), NULL, f_size);
showProgress((int)sent, (int)f_size, "Server[sending]: ");
}
fclose(fp);
// 4. send file timestamp
uint32_t net_f_time = htonl(f_time);
send(connSock, &net_f_time, 4, 0);
//close(connSock);
} else {
char err_buff[7] = "-ERR\r\n";
send(connSock, err_buff, 6, 0);
//close(connSock);
return;
}
}
int reqCompleted(char *request) {
int len = strlen(request);
return len > 6 && request[len - 2] == '\r' && request[len - 1] == '\n' ? 0 : -1;
}
int checkRequest(char *request) {
int len = strlen(request);
if (len > 6 && request[0] == 'G' && request[1] == 'E' && request[2] == 'T'
&& request[3] == ' ' && request[len - 2] == '\r' && request[len - 1] == '\n') {
memcpy(request, request + 4, (len - 6)); // estraggo il nome del file dalla richiesta
request[len - 6] = '\0';
return access(request, F_OK); // verifico che il file esista nella cartella di lavoro
}
return -1;
}
the parameter called passiveSock causes the problem, at the second iteration of the while loop, I'm quite new in C and I think it's relative to life cycle of a variable. I've already tried to print passiveSock before the accept call and gives me an error. This is the valgrind output:
==1== Syscall param accept(s) contains uninitialised byte(s)
==1== at 0x4F21990: __accept_nocancel (syscall-template.S:84)
==1== by 0x10A7D4: Accept (sockwrap.c:97)
==1== by 0x109AA5: runIterativeTcpInstance (gj_server.c:28)
==1==
((null)) error - accept() failed: Bad file descriptor
==1==
==1== HEAP SUMMARY:
==1== in use at exit: 0 bytes in 0 blocks
==1== total heap usage: 2 allocs, 2 frees, 1,576 bytes allocated
==1==
==1== All heap blocks were freed -- no leaks are possible
==1==
==1== For counts of detected and suppressed errors, rerun with: -v
==1== Use --track-origins=yes to see where uninitialised values come from
==1== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
EDIT sockwrap.c Accept definition
int Accept (int listen_sockfd, SA *cliaddr, socklen_t *addrlenp)
{
int n;
again:
if ( (n = accept(listen_sockfd, cliaddr, addrlenp)) < 0)
{
if (INTERRUPTED_BY_SIGNAL ||
errno == EPROTO || errno == ECONNABORTED ||
errno == EMFILE || errno == ENFILE ||
errno == ENOBUFS || errno == ENOMEM
)
goto again;
else
err_sys ("(%s) error - accept() failed", prog_name);
}
return n;
}
initStr and showProgress
void initStr(char* string, int length) {
memset(string, '\0', length);
string[length] = '\0';
}
void showProgress(int done, int tot, char * progMsg) {
int progress = ((double) done / (double) tot) * 100;
printf("\r%s " ANSI_COLOR_CYAN "%d bytes (%d%%)" ANSI_COLOR_RESET, progMsg, done, progress);
fflush(stdout);
}
Of course the bug was in the first function I wanted to check. Look closely:
void initStr(char* string, int length) {
memset(string, '\0', length);
string[length] = '\0'; // <-- BOOM!
}
But:
char request[256];
initStr(request, 256);
So request is an array with 256 entries. But initStr writes into the 257th entry, which does not exist.
You should get rid of this function and never use anything even remotely like it. Filling an entire buffer with zeroes serves no purpose. Instead, properly track the length of the data in the buffer and stop calling strlen all the time. There are other bugs in your code because of this, much more subtle ones.
For example, you try to read 256 bytes from the connection. This could fill your entire buffer, replacing all the zero bytes. You then pass the buffer to strlen -- but there is no guarantee there is a zero byte anywhere in the buffer.
Data received over a network connection is not a string. It's raw data with a known length that is returned from the read or recv function. Don't pretend it is a string. It's a bad habit that will bite you again and again and again.
I am trying to create a client that communicate with a server by sending 2 types of messages:
The word QUIT that communicate to the server to close the connection.
An operation with the following syntax: operator first_operand second_operand. For example: + 3 3, - 5 6 etc. (the operands must be positive integers, and there must be only 2 operands).
If the server receive an operation, it executes it and returns the result to the client. The problem is that the first operation I send returns the right result, while the following ones work randomly (sometimes they return the right result, other times the function strtok() doesn't get the second operand and returns NULL...).
This is code of the client that process the message written by the user in the prompt and that scan the message to check if the operation is written with the correct syntax (WARNING: the code is written in an extremely unprofessional and unclean way).
The code part that creates the problem is inside the while(1).
#define MAXLENGTH 256
int main (int argc, char *argv[]) {
int simpleSocket = 0;
int simplePort = 0;
int returnStatus = 0;
char first[10], second[10];
char* operator;
char buffer[MAXLENGTH] = "";
char message[50];
char terminationCommand[] = "QUIT\n";
char space[2] = " ";
struct sockaddr_in simpleServer;
if (3 != argc) {
fprintf(stderr, "Usage: %s <server> <port>\n", argv[0]);
exit(1);
}
/* create a streaming socket */
simpleSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (simpleSocket == -1) {
fprintf(stderr, "Could not create a socket!\n");
exit(1);
} else {
fprintf(stderr, "Socket created!\n");
}
/* retrieve the port number for connecting */
simplePort = atoi(argv[2]);
/* setup the address structure */
/* use the IP address sent as an argument for the server address */
//bzero(&simpleServer, sizeof(simpleServer));
memset(&simpleServer, '\0', sizeof(simpleServer));
simpleServer.sin_family = AF_INET;
//inet_addr(argv[2], &simpleServer.sin_addr.s_addr);
simpleServer.sin_addr.s_addr=inet_addr(argv[1]);
simpleServer.sin_port = htons(simplePort);
/* connect to the address and port with our socket */
returnStatus = connect(simpleSocket, (struct sockaddr *)&simpleServer, sizeof(simpleServer));
if (returnStatus == 0) {
fprintf(stderr, "Connect successful!\n\n");
} else {
fprintf(stderr, "Could not connect to address!\n");
close(simpleSocket);
exit(1);
}
/* get the message from the server */
returnStatus = read(simpleSocket, buffer, sizeof(buffer));
if (returnStatus > 0) {
printf("%s\n", &buffer[3]);
} else {
fprintf(stderr, "Return Status = %d \n", returnStatus);
}
memset(&buffer, '\0', sizeof(buffer));
printf("You can execute 2 commands:\n");
printf("1. Operations ( +, -, *, /, % ) with the following syntax: operator + first operand + second operand.\n");
printf("Example: + 5 2 \n");
printf("2. Termination of the connection with the following syntax: QUIT + press Enter.\n");
while(1) {
printf("\nEnter a command:\n");
fgets(message, 1000, stdin);
// the if with the termination command works fine
if (strcmp(message, terminationCommand) == 0) {
if (send(simpleSocket, message, strlen(message), 0) < 0) {
printf("Send failed.");
return 1;
}
returnStatus = read(simpleSocket, buffer, sizeof(buffer));
if (returnStatus > 0) {
printf("%s\n", &buffer[4]);
} else {
fprintf(stderr, "Return Status = %d \n", returnStatus);
}
close(simpleSocket);
exit(1);
}
operator = strtok(message, space);
if (strcmp(operator, "+") == 0 || strcmp(operator, "-") == 0 || strcmp(operator, "/") == 0 || strcmp(operator, "%") == 0 || strcmp(operator, "*") == 0) {
char *first_operand = strtok(NULL, space);
if (first_operand != NULL) {
if (strcmp(first_operand, "ANS") == 0)
strcpy(first, "ANS");
else
strcpy(first, first_operand);
printf("%s\n", operator);
printf("%s\n", first);
char *second_operand = strtok(NULL, space);
printf("%s\n", second_operand);
if (second_operand != NULL && strtok(NULL, space) == NULL && (atoi(first) > 0 || strcmp(first, "ANS") == 0)) {
if (strcmp(second_operand, "ANS\n") == 0)
strcpy(second, "ANS");
else {
strcpy(second, second_operand);
}
if (atoi(second) > 0 || strcmp(second, "ANS") == 0) {
printf("OK\n");
char operation[] = "";
strcat(operation, operator);
strcat(operation, " ");
strcat(operation, first);
strcat(operation, " ");
strcat(operation, second);
if (send(simpleSocket, operation, strlen(operation), 0) < 0) {
printf("Send failed.");
return 1;
}
returnStatus = read(simpleSocket, buffer, sizeof(buffer));
if (returnStatus > 0) {
printf("%s\n", buffer);
} else {
fprintf(stderr, "Return Status = %d \n", returnStatus);
}
}
}
}
}
// after everything I reset the buffers I use to memorize the message and the elements of the message
memset(&buffer, '\0', sizeof(buffer));
memset(&first, '\0', sizeof(first));
memset(&second, '\0', sizeof(second));
memset(&message, '\0', sizeof(message));
memset(operator, '\0', sizeof(operator));
}
}
Can someone tell me why the second strtok() acts weird 90% of the times? What am I doing wrong?
There are multiple issues in you program:
You send newline terminated messages and you assume on the other end the read will return exactly the bytes sent by the other party, which is an incorrect assumption for the TCP/IP communications, only the order of bytes received is guaranteed, but the messages can be split on the way and received in chunks different from the sending sequence. You should instead read the socket into a buffer and only handle it once you receive a newline.
In your case, there is another problem which is more pressing: the buffer into which you read the data is not null terminated, so you should not pass it to standard C functions such as strtok().
I have looked at the similar threads but can't seem to find anything that could solve my problem.
I am programming a server that could send an image(jpg) file from the path sent to it from the client. I am using send/recv functions in C for that.
I read files one chunk of data at a time and send it to the client that receives the contents and writes them at some location to build the file.
Problem is 'recv' doesn't receive the number of bytes sent by the 'send'.
As part of debugging, I have tried different buffer sizes and '128' buffer size doesn't give me any problem, and the file is successfully transferred and built.
However, for '32' and '64' bit buffers, 'recv' receives '32' bit or '64' bit data at the last chunk, even though the data sent by the server is less than either '32' bit or '64' bit. And, for '256', '512', '1024' so on, 'recv' returns ONLY '128' bits at EXACTLY one of the responses, even though the server sends complete chunk, i.e. '256' or'512', depending on the buffer size.
I'll appreciate any advise for debugging. Following code is for the relevant parts only, but I can provide more, should someone requires it.
//Client Code
#define BUFFER_SIZE 4096
#define HEADER_LEN 512
const char * const scheme = "GETFILE";
const char* const method = "GET";
const char * const end_marker = "\\r\\n\\r\\n";
struct gfcrequest_t
{
int filelen;
char cport[12];
char servIP[50];
gfstatus_t ret_status;
char spath[1024];
int tot_bytes;
char filecontent[BUFFER_SIZE];
void (*fl_handler)(void *, size_t, void *);
void * fDesc;
void (*head_handler)(void *, size_t, void *);
void * headarg;
};
static pthread_mutex_t counter_mutex;
gfcrequest_t *gfc;
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET)
{
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
static char *stringFromError(gfstatus_t stat)
{
static const char *strings[] = {"GF_OK", "GF_FILE_NOT_FOUND", "GF_ERROR", "GF_INVALID"};
return strings[stat];
}
int getFileRequestHeader(char * req_header)
{
return snprintf(req_header,HEADER_LEN, "%s%s%s%s", scheme, method,gfc->spath, end_marker);
// return snprintf(req_header,HEADER_LEN, "%s%s%s%s", scheme, method,"/courses/ud923/filecorpus/yellowstone.jpg", end_marker);
}
gfcrequest_t *gfc_create()
{
gfc = (gfcrequest_t*)malloc(1* sizeof(gfcrequest_t));
return gfc;
}
void gfc_set_server(gfcrequest_t *gfr, char* server)
{
strcpy(gfr->servIP, server);
}
void gfc_set_path(gfcrequest_t *gfr, char* path)
{
strcpy(gfr->spath, path);
}
void gfc_set_port(gfcrequest_t *gfr, unsigned short port)
{
snprintf(gfr->cport,12, "%u",port);
}
void gfc_set_headerfunc(gfcrequest_t *gfr, void (*headerfunc)(void*, size_t, void *))
{
gfr->head_handler = headerfunc;
}
void gfc_set_headerarg(gfcrequest_t *gfr, void *headerarg)
{
/*have to change this...*/
gfr->headarg = headerarg;
}
int isEndMarker(char *iheader, int start)
{
char *marker = "\\r\\n\\r\\n";
int i = 0; int ind=0;
while (ind <= 7)
{
if (iheader[start++] == marker[ind++])
{
continue;
}
return 0;
}
return 1;
}
int getFileLen(char *resp_header, gfcrequest_t *gfr)
{
char scheme[8];
char status[4];
int istatus;
char filelen[12];
int contentlen = 0;
int fileindex = 0;
char end_marker[12];
int fexit=0;
int end=0;
sscanf(resp_header, "%7s%3s", scheme, status);
istatus = atoi(status);
if (istatus == 200)
{
gfr->ret_status = GF_OK;
}
else if (istatus == 400)
{
gfr->ret_status = GF_FILE_NOT_FOUND;
}
else if (istatus == 500)
{
gfr->ret_status = GF_ERROR;
}
if (!strcmp(scheme, "GETFILE") && (istatus == 200 || istatus == 400 || istatus == 500))
{
int index = 10;
while(1)
{
if (resp_header[index] == '\\')
{
end = isEndMarker(resp_header, index);
}
if (end)
break;
filelen[fileindex++] = resp_header[index++];
}
filelen[fileindex] = '\0';
}
int head_len = strlen(scheme) + strlen(status) + strlen(filelen) + 8;
return atoi(filelen);
}
void gfc_set_writefunc(gfcrequest_t *gfr, void (*writefunc)(void*, size_t, void *))
{
gfr->fl_handler = writefunc;
}
void gfc_set_writearg(gfcrequest_t *gfr, void *writearg)
{
gfr->fDesc = writearg;
}
int gfc_perform(gfcrequest_t *gfr){
struct addrinfo hints, *servinfo, *p;
int sockfd, rv, totalBytesRcvd;
char req_header[HEADER_LEN];
int bytesRcvd;
int header_len;
char s[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof(hints));
memset(req_header,0,sizeof(req_header));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; //use my IP
if ((rv = getaddrinfo(NULL, gfr->cport, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next)
{
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror("client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
{
close(sockfd);
perror("client: connect");
continue;
}
break;
}
if (p == NULL)
{
fprintf(stderr, "client: failed to connect\n");
return 2;
}
//printf("connected...\n");
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof(s));
//Ahsan
// printf("Before getFileRequestHeader...\n");
header_len = getFileRequestHeader(req_header);
//printf("Header Description:%s, Header Len: %u\n", req_header, header_len);
if (send(sockfd, req_header, header_len, 0) != header_len)
perror("send() sent a different number of bytes than expected");
if ((bytesRcvd = recv(sockfd, gfr->filecontent, BUFFER_SIZE, 0)) <= 0)
perror("recv() failed or connection closed prematurely");
//printf("Header Received: %s\n", gfr->filecontent);
gfr->filelen = getFileLen(gfr->filecontent, gfr);
//printf("File Length: %d\n", gfr->filelen);
/* Receive the same string back from the server */
int req_no=1;
gfr->tot_bytes = 0;
while ( 1 )
{
printf("Request: %d ", req_no++);
ssize_t nb = recv( sockfd, gfr->filecontent, BUFFER_SIZE, 0 );
if ( nb == -1 ) err( "recv failed" );
if ( nb == 0 ) {printf("zero bytes received...breaking");break;} /* got end-of-stream */
gfr->fl_handler(gfr->filecontent, nb, gfr->fDesc);
gfr->tot_bytes += nb;
printf("Received Bytes: %zd Total Received Bytes: %d\n", nb, gfr->tot_bytes);
}
return 0;
}
/*
* Returns the string associated with the input status
*/
char* gfc_strstatus(gfstatus_t status)
{
return stringFromError(status);
}
gfstatus_t gfc_get_status(gfcrequest_t *gfr){
return gfr->ret_status;
}
size_t gfc_get_filelen(gfcrequest_t *gfr)
{
return gfr->filelen;
}
size_t gfc_get_bytesreceived(gfcrequest_t *gfr)
{
return gfr->tot_bytes;
}
void gfc_cleanup(gfcrequest_t *gfr)
{
free(gfr);
gfr=NULL;
}
void gfc_global_init()
{
;
// pthread_mutex_lock(&counter_mutex);
//
// gfc = (gfcrequest_t*)malloc(1* sizeof(gfcrequest_t));
//
// pthread_mutex_unlock(&counter_mutex);
}
void gfc_global_cleanup()
{
;
// pthread_mutex_lock(&counter_mutex);
//
// free(gfc);
//
// pthread_mutex_unlock(&counter_mutex);
}
//Server Code
struct gfcontext_t
{
int sockfd;
int clntSock;
};
struct gfserver_t
{
char port[12];
unsigned short max_npending;
char fpath[256];
ssize_t (*fp_handler)(gfcontext_t *ctx, char *, void*);
int *handler_arg;
};
/*Variable decalation*/
static gfserver_t *gfserv;
static gfcontext_t *gfcontext;
int isEndMarker(char *iheader, int start)
{
char *marker = "\\r\\n\\r\\n";
int i = 0; int ind=0;
while (ind <= 7)
{
if (iheader[start++] == marker[ind++])
{
//printf("Header Char:%c Marker:%c\n", iheader[start], marker[ind]);
//start++;
continue;
}
return 0;
}
//printf("Its a marker!!!\n");
return 1;
}
int parseHeader(char *iheader, gfserver_t *gfs, int hlen)
{
//"GETFILEGET/courses/ud923/filecorpus/road.jpg\r\n\r\n"
char scheme[8];
char method[4];
// char path[256];
int pathindex = 0;
char end_marker[12];
int end = 0;
int fexit=0;
sscanf(iheader, "%7s%3s", scheme, method);
int i=10;
if (iheader[i] == '/')
{
// printf("Path has started...\n");
if (!strcmp(scheme, "GETFILE") && !strcmp(method, "GET"))
{
while(1)
{
if (iheader[i] == '\\')
{
end = isEndMarker(iheader, i);
}
if (end)
break;
gfs->fpath[pathindex++] = iheader[i++];
}
gfs->fpath[pathindex] = '\0';
}
}
printf("Scheme: %s Method:%s Path:%s\n", scheme, method, gfs->fpath);
return 0;
}
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET)
{
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
ssize_t gfs_sendheader(gfcontext_t *ctx, gfstatus_t stat, size_t file_len)
{
char resp_header[MAX_REQUEST_LEN];
char end_marker[12] = "\\r\\n\\r\\n";
sprintf(resp_header, "GETFILE%d%zd%s",stat, file_len, end_marker);
printf("Response: %s\n", resp_header);
if (send(ctx->clntSock, resp_header, MAX_REQUEST_LEN, 0) != MAX_REQUEST_LEN)
perror("send() failed");
return 0;
}
ssize_t gfs_send(gfcontext_t *ctx, void *data, size_t len)
{
size_t total = 0;
size_t bytesLeft = len;
size_t n;
int debug_req=1;
while (total < len)
{
n = send(ctx->clntSock, data+total, bytesLeft, 0);
if (n == -1) { printf("Nothing to send...\n"); break; }
fprintf(stderr, "Tries: %d Bytes Sent: %zu\n", debug_req++, n);
total += n;
bytesLeft -= n;
}
// if ( shutdown( ctx->clntSock, SHUT_WR ) == -1 ) err( "socket shutdown failed" );
return total;
}
void gfs_abort(gfcontext_t *ctx){
close(ctx->clntSock);
close(ctx->sockfd);
free(ctx);
free(gfserv);
perror("aborting...");
exit(1);
}
gfserver_t* gfserver_create()
{
gfserv = (gfserver_t*) malloc(1 * sizeof(gfserver_t));
gfcontext = (gfcontext_t*) malloc(1*sizeof(gfcontext_t));
return gfserv;
}
void gfserver_set_port(gfserver_t *gfs, unsigned short port)
{
//set port number in gfs structure
snprintf(gfs->port,12, "%u",port);
}
void gfserver_set_maxpending(gfserver_t *gfs, int max_npending)
{
//set maxpending connections
gfs->max_npending = max_npending;
}
void gfserver_set_handler(gfserver_t *gfs, ssize_t (*handler)(gfcontext_t *, char *, void*))
{
gfs->fp_handler = handler;
}
void gfserver_set_handlerarg(gfserver_t *gfs, void* arg)
{
gfs->handler_arg = (int *)arg;
}
void gfserver_serve(gfserver_t *gfs)
{
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage clntAddr; //connectors address information
socklen_t clntSize;
int yes = 1;
char s[INET6_ADDRSTRLEN];
int rv;
int rcvMsg = 0;
char recvBuff[MAX_REQUEST_LEN];
char *req_path;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; //using stream socket instead of datagrams
hints.ai_flags = AI_PASSIVE; //use my IP
if ((rv = getaddrinfo(NULL, gfs->port, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next)
{
if ((gfcontext->sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror("server: socket");
continue;
}
//get rid of 'address already in use' error.
if (setsockopt(gfcontext->sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}
if (bind(gfcontext->sockfd, p->ai_addr, p->ai_addrlen) == -1)
{
close(gfcontext->sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL)
{
fprintf(stderr, "server: failed to bind.\n");
return 2;
}
freeaddrinfo(servinfo); // no need of servinfo structure anymore
if (listen(gfcontext->sockfd, gfs->max_npending) == -1)
{
perror("listen");
exit(1);
}
//printf("server: waiting for connetions...\n");
while(1)
{
clntSize = sizeof(clntAddr);
gfcontext->clntSock = accept(gfcontext->sockfd, (struct sockaddr *)&clntAddr, &clntSize);
if (gfcontext->clntSock == -1)
{
perror("accept");
continue;
}
inet_ntop(clntAddr.ss_family, get_in_addr((struct sockaddr *)&clntAddr), s, sizeof(s));
//printf("server: got connection from %s\n", s);
if (!fork())
{ // this is the child process
if ((rcvMsg = recv(gfcontext->clntSock, recvBuff, MAX_REQUEST_LEN, 0)) < 0)
{
perror("recv() failed");
exit(1);
}
/*Still to parse received request...*/
//printf("Recd Header: %s, Recd %d bytes\n",recvBuff, rcvMsg);
/*Parse the received header...*/
int len = parseHeader(recvBuff, gfs, rcvMsg);
//printf("Requested Path: %s\n", gfs->fpath);
if (gfs->fp_handler(gfcontext, gfs->fpath, NULL) < 0)
{
printf("some problem...\n");
}
if ( shutdown( gfcontext->clntSock, SHUT_WR ) == -1 ) err( "socket shutdown failed" );
//close(gfcontext->clntSock);
}
}
}
//Server gf_send is being called from following function handler function:
Handler:
ssize_t handler_get(gfcontext_t *ctx, char *path, void* arg){
int fildes;
size_t file_len, bytes_transferred;
ssize_t read_len, write_len;
char buffer[BUFFER_SIZE];
printf("Path: %s\n", path);
if( 0 > (fildes = content_get(path)))
return gfs_sendheader(ctx, GF_FILE_NOT_FOUND, 0);
/* Calculating the file size */
file_len = lseek(fildes, 0, SEEK_END);
gfs_sendheader(ctx, GF_OK, file_len);
/* Sending the file contents chunk by chunk. */
int req=1;
bytes_transferred = 0;
while(bytes_transferred < file_len){
read_len = pread(fildes, buffer, BUFFER_SIZE, bytes_transferred);
if (read_len <= 0){
fprintf(stderr, "handle_with_file read error, %zd, %zu, %zu", read_len, bytes_transferred, file_len );
gfs_abort(ctx);
return -1;
}
printf("Request No: %d ", req++);
write_len = gfs_send(ctx, buffer, read_len);
if (write_len != read_len){
fprintf(stderr, "handle_with_file write error");
gfs_abort(ctx);
return -1;
}
bytes_transferred += write_len;
}
printf("Total Bytes sent to client: %zu\n", bytes_transferred);
return bytes_transferred;
}
You didn't specify, so I am assuming you are using TCP here (send/receive semantics is different with UDP),
You are suffering from a very common misconception that one send on one end of a TCP socket corresponds to one receive of sent number of bytes on the other end. This is wrong.
In fact, TCP socket is a bi-directional stream of bytes with no notion of messages. One write can correspond to many reads on the other end, and vise versa. Treat it as a stream.
You need to keep number of bytes sent and received as returned from sending and receiving system calls.
It is also important to let the other side know how much data you are sending, so it will know when, say, an image is fully transferred. This is the job of an application-level protocol that you have to either come up with or use an existing one.
Edit 0:
Here is what looks needed even before setting up any meaningful protocol between client and the server.
First the sending code:
size_t total = 0;
while ( total != len ) {
ssize_t nb = send( s, data + total, len - total, 0 );
if ( nb == -1 ) err( "send failed" );
total += nb;
}
if ( shutdown( s, SHUT_WR ) == -1 ) err( "socket shutdown failed" );
/* also need to close client socket, see below */
Then the receiving code:
char buffer[BUFFER_SIZE]; /* somewhere, might be static */
size_t total = 0; /* everything received */
while ( 1 ) {
ssize_t nb = recv( s, buffer, BUFFER_SIZE, 0 );
if ( nb == -1 ) err( "recv failed" );
if ( nb == 0 ) break; /* got end-of-stream */
if ( write( file_fd, buffer, nb ) == -1 ) err( "file write failed" );
total += nb;
}
/* send an ack here */
if ( close( s ) == -1 ) err( "socket close failed" );
if ( close( file_fd )) err( "file close failed" );
printf( "received and saved total of %zu bytes\n", total );
Then your application-level protocol might be as simple as server sending, say, 64-bit file length immediately after accepting new client connection (you need to decide what endianness to use for that), then after sending that many bytes to the client and shutting down writing on the socket, waiting for the client to acknowledge successful receipt of data. That might be that same number back, or just a single byte - up to you, then finally closing the socket. That way the client knows upfront how many bytes to expect, and the server knows that transfer was successful.
After getting this simple version working you can extend it to allow multiple file transfers per connection, and/or dive into IO multiplexing with select(2)/poll(2).
Hope this helps.
First of all: recv() does not always receive the data in the chunks that it was sent by send(), as a matter of fact - it rarely does - because of buffering (for example, you send 256-bytes receive two buffers of 128-bytes each)
Now to your error: I think the problem is that you are not be calling select() with a FD_SET to reset your socket to a "ready to receive" state before calling recv() a second time.
I have a metric-ton of winsock/c-sockets code on my site if you want to dig through it.
Let me know if I can expand on this answer, I'd be happy to provide additional assistance!
gfr->fl_handler(gfr->filecontent, BUFFER_SIZE, gfr->fDesc);
Usual problem. You're assuming the read filled the buffer. It should be:
gfr->fl_handler(gfr->filecontent, bytesRcvd, gfr->fDesc);
#include <winsock2.h>
#include <stdio.h>
const int PORT = 6667;
const char *SERVER = "irc.freenode.org";
const char *CHAN = "#channela";
const char *NICK = "loveMilk";
const int MAX_BUFF_SIZE = 512;
int sock_conn(SOCKET *socketn, const char *HOST, int portn);
int sock_send(SOCKET *socketn, char* msg, ...);
int main(int argc, char *argv[])
{
WSADATA wsadata;
char buff[MAX_BUFF_SIZE];
char oBuff[MAX_BUFF_SIZE];
int buffRec;
if (WSAStartup(MAKEWORD(2,2), &wsadata) != 0)
return 0;
SOCKET sock;
if(sock_conn(&sock, SERVER, PORT) != 0)
{
WSACleanup();
return 0;
}
printf("connected.\n");
sock_send(&sock, "USER %s \"\" \"127.0.0.1\" :%s\r\n", NICK, NICK);
sock_send(&sock, "NICK %s\r\n", NICK);
Sleep(100);
sock_send(&sock, "JOIN %s\r\n", CHAN);
printf("Joined channel.\n");
while(1)
{
memset(buff, 0, MAX_BUFF_SIZE);
memset(oBuff, 0, MAX_BUFF_SIZE);
buffRec = recv(sock, buff, MAX_BUFF_SIZE, 0);
if((buffRec == 0) || (buffRec == SOCKET_ERROR)) break;
if(buff[0] != ':')
{
strcpy(oBuff, "PONG :");
printf("PONG");
sock_send(&sock, oBuff);
}
else
{
if(strstr(buff, "PRIVMSG"))
{
int i, num = 0;
for(i = 0; i < strlen(buff); ++i) if(buff[i] = ' ') ++num;
char** parts = malloc(sizeof(char*) * num);
char *p;
p = strtok(buff, " ");
int j = 0;
while(p != NULL)
{
parts[j] = p;
j++;
p = strtok(NULL, " ");
}
free(parts);
}
}
}
closesocket(sock);
return 1;
}
int sock_conn(SOCKET *socketn, const char *HOST, int portn)
{
WSADATA wsadata;
SOCKADDR_IN sockA;
LPHOSTENT hostE;
if(WSAStartup(MAKEWORD(2,2), &wsadata) == -1) return -1;
if(!(hostE = gethostbyname(HOST)))
{
WSACleanup();
return -1;
}
if ((*socketn = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
WSACleanup();
return -1;
}
sockA.sin_family = AF_INET;
sockA.sin_port = htons(portn);
sockA.sin_addr = *((LPIN_ADDR)*hostE->h_addr_list);
if(connect(*socketn, (LPSOCKADDR)&sockA, sizeof(struct sockaddr)) == SOCKET_ERROR)
{
WSACleanup();
return -1;
}
}
int sock_send(SOCKET *socketn, char* msg, ...)
{
char buff[MAX_BUFF_SIZE];
va_list va;
va_start(va, msg);
vsprintf(buff, msg, va);
va_end(va);
send(*socketn, buff, strlen(buff), 0);
return 1;
}
If I try to print buff after the if(strstr(buff, "PRIVMSG")) it crashes.
The while with the strtok won't work, if I try to reach parts[0] it crashes.
I tried to print parts[0] but shows nothing, tried to print during the while loop, shows nothing.
why?
You don't terminate your strings!
Edit the receiving part as this:
buffRec = recv(sock, buff, MAX_BUFF_SIZE, 0);
if((buffRec == 0) || (buffRec == SOCKET_ERROR)) break;
/* New line: Terminate buffer as a string */
buff[buffRec] = '\0';
As the other answer points out, a character array must end with '\0' to be considered a string. I think that C doesn't distinguish between the two, but you need the '\0' to signify the end of string. This could be why strstr(buff, "PRIVMSG")) isn't returning anything. It may default to null (thus not satisfying your if) because it doesn't think it has been passed a string.
'strtok(string, delimiter)' breaks an input string into tokens by using the delimiter. Here, you have passed it NULL as its string and " " as its delimiter. I am unfamiliar with many string functions (still learning C myself), but I think this is incorrect usage in your code.
parts[] does not seem to be defined in the code you've given. Its first use is where you try to store data in it for the inner while loop. Are there any other parts to the program that are not shown?