I am trying to create two programs a client and server, where the client opens a socket connection and then writes data to the server who on accepting the connection spawns a new threads and then detaches it, to handle the rest of the read/writes. The problem is that when I make multiple writes then reads from the client the reads aren't getting the correct data, however on the server side it prints that it sent the correct data.
This is what my code looks like to generate new threads, and how I handle those threads.
while(1){
listen(sockfd,5);
// determine the size of a clientAddressInfo struct
clilen = sizeof(clientAddressInfo);
int *newsockfd = malloc(sizeof(int));
// block until a client connects, when it does, create a client socket
*newsockfd = accept(sockfd, (struct sockaddr *) &clientAddressInfo, &clilen);
// if the connection blew up for some reason, complain and exit
if (*newsockfd < 0){
error("ERROR on accept");
}
connection_args *args = malloc(sizeof(connection_args));
args->file_descrp = newsockfd;
pthread_t tid;
pthread_create(&tid,NULL, handle_connect, args);
}
void * handle_connect(void* args){
connection_args* connect_arg = (connection_args*)args;
pthread_detach(pthread_self());
int n = -1;
char buffer[256];
bzero(buffer,256);
//while not close;
while(1){
// try to read from the client socket
n = read(*connect_arg->file_descrp,buffer,255);
printf("input: %s\n", buffer);
// if the read from the client blew up, complain and exit
if (n < 0){
error("ERROR reading from socket");
}
int fd;
if(strcmp("open",buffer) == 0){
fd = open("file.txt",0);
bzero(buffer,256);
sprintf(buffer,"%d",fd);
}else if(strcmp("read",buffer) == 0){
char *read_buffer = malloc(sizeof(char)*256);
bzero(read_buffer,256);
fd = read(get_filedescrp(),read_buffer,30);
bzero(buffer,256);
sprintf(buffer,"%s,%d",read_buffer,fd);
}else if(strcmp("close",buffer) == 0){
break;
}
printf("buffer_send: %s\n",buffer);
// try to write to the client socket
n = write(*connect_arg->file_descrp,buffer,sizeof(buffer));
// if the write to the client below up, complain and exit
if (n < 0){
printf("here!!\n");
error("ERROR writing to socket");
}
bzero(buffer,256);
}
printf("Left thread\n");
return NULL;
}
You cannot implement client server communication over TCP/IP without some sort of protocol. The data written by the sender can be sliced and diced along the way and come in different chunk lengths to the reader side. You must have a way to tell if you have received a full frame before trying to interpret the data.
For example, you can use a very simple line based protocol: read data upto and including the '\n' byte. Reading one byte at a time into a lien buffer is somewhat inefficient but easy to implement.
A socket read call may or may not return the entire data sent by client in a single call.
Each read call returns number of bytes are that are read in that call. So the application should call read in a loop till expected number of bytes are read.
Related
The objective of my program is to use select to manage multiple sockets. However, I thought of trying it with one socket first. Now, the problem that I am facing is that initially client sends data to server, and server receives it and displays it, but then when client again sends some data, the server code remains still at select command.
here are some snippets that will give you an idea of how I am initializing the socket.
if((master_socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
exit(1);
}
if((bind(master_socket, (struct sockaddr *)&req, sizeof(req))) < 0)
{
exit(1);
}
listen(master_socket, 5);
FD_SET(master_socket,&listening);
/* wait for connection, then receive and print text */
len = sizeof(struct sockaddr);
while(1)
{
FD_ZERO(&listening); //Flush out everything in socket
FD_SET(master_socket,&listening); // Add master
if(f_client>0) // Add client if any
{
FD_SET(f_client,&listening);
}
printf("Checking for new connection \n");
//Timeout is null, so waiting indefinitely
rc = select(FD_SETSIZE, &listening, NULL, NULL, NULL);
if (FD_ISSET(master_socket, &listening))
{
printf("Master side invoked\n");
if((f_client = accept(master_socket, (struct sockaddr *)&req, &len)) < 0)
{
exit(1);
}
}
else if (FD_ISSET(f_client,&listening))
{
if ((valread = read( f_client , buf, 1024)) == 0)
{
close(f_client);
f_client=0;
}
else
{
fputs(buf, stdout);
}
}
}
Basically in above program, it connects to the server, maintains a file descriptor for client f_client and add it. And in every round, it clears the socket, add master socket, and client socket if any, and then checks. Problem here is, first time it works, but second time when client sends some data. it gets hang to rc = select(FD_SETSIZE, &listening, NULL, NULL, NULL);
I am not to understand what is wrong here. Can anyone help?
if ((valread = read( f_client , buf, 1024)) == 0)
{
close(f_client);
f_client=0;
}
else
{
fputs(buf, stdout);
}
This code is broken. The fputs function can only be used with a C-style string. You just have arbitrary data with no particular structure. Since you ignore valread, you also have no idea how many bytes you read. (Think about it, how could fputs possibly know how many bytes to output? That information is only in valread, and you don't pass it that information.)
You've already received the data, this broken code just threw it away. If you log valread, you'll see that you actually already read it in your last call to read before the call to select that hung.
instead of fputs, you could use something like this:
for (int i = 0; i < valread; ++i)
putchar(buf[i]);
I'm writing a client server application and I'm using poll to multiplex between several client sockets and stdin, where I can insert commands (example: stop the server). I believe the structure (the "logic") of my code is correct, however it's not behaving the way I expect it to:
struct pollfd pfd[NSERVER]; //defined as 10
pfd[0].fd = fileno(stdin);
pfd[0].events = POLLIN;
pfd[1].fd = socktfd; //server bind, listen socket
pfd[1].events = POLLIN;
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char remoteIP[INET6_ADDRSTRLEN];
addrlen = sizeof remoteaddr;
char buf[1024]; // buffer
int pos=2;
while(poll(pfd,1,0) >= 0)
{
if(pfd[0].revents & POLLIN) { //stdin
//process input and perform command
}
if(pfd[1].revents & POLLIN) {
/* new connection */
int connsockfd = accept(socktfd, (struct sockaddr *)&remoteaddr,&addrlen);
pfd[pos].fd=connsockfd;
}
int i=2;
//Loop through the fd in pfd for events
while (i<=NSERVER)
{
if (pfd[i].revents & POLLIN) {
int c=recv(pfd[i].fd, buf, sizeof buf, 0);
if(c<=0) {
if (c==0)
{
/* Client closed socket */
close(pfd[i].fd);
}
}else
{//Client sent some data
c=send(pfd[i].fd,sbuff,z,0);
if (c<=0)
{
Error;
}
free(sbuff);
}
}
i++;
}
}
I've removed some code inside the recv and send to make the code easier to read.
It fails to behave (it just hangs, doesn't accept connections or reacts to input from stdin).
Note: I would prefer to use poll over select, so please don't point to select :-).
Thanks in advance for any assistance.
you should set every pfd[i].fd = -1, so they get ignored initially by poll().
poll(pfd, 1, 0) is wrong and should at least be poll(pfd, 2, 0) or even poll(pfd, NSERVER, 0).
while(i<=NSERVER) should be while(i<NSERVER)
Your program probably hangs, because you loop through the pfd array, which is not initialized and containes random values for .fd and .revents, so it wants to send() or recv() on some random FD which might block. Do if(pdf[i].fd < 0) {i++; continue;} in the i<NSERVER loop.
You also don't set pfd[pos].events = POLLIN on newly accepted sockets. Don't set POLLOUT unless you have something to send, because it will trigger almost every time.
I'm working on an assignment for my Distributed Systems class. I'm a master's student in C.S., but my specialty in programming is .NET and I'm working on a project that requires some fairly involved Unix knowledge, which is tripping me up.
The assignment is implementing a flush channel protocol API. So I'm coding a small function library that other apps can implement to use flush channel communication. I've set it up so that when the init function is called, it forks a child process to act as the server for incoming messages. The child communicates with the parent process by sending incoming data to the parent through a pipe.
This works OK if messages are sent and received one at a time; e.g.,
send -> receive -> send -> receive -> etc.
However, if multiple messages are sent before doing any receives; e.g.,
send -> send -> send -> receive
then it gets messed up. Specifically, the first message is received correctly, but when I go to receive the second message, the program hangs and needs to be killed. I've done a lot of searching online and been plugging away at this for hours but haven't made much progress.
The program as a whole is far too large to show here, but here are the most relevant bits. Here's the part where I get the server going and receive messages. Note the line
write(fd[1], buffer, (strlen(buffer)+1));
-- I think that's a good candidate for being the source of the problem here, but not sure what to do differently. (Tried fwrite() and that didn't work at all.)
fd = malloc(2 * sizeof(int));
int nbytes;
if (pipe(fd) < 0) {
perror("Could not create pipe");
return -1;
}
pID = fork();
if (pID < 0) {
perror("Failed to fork");
return -1;
} else if (pID == 0) { // child
close(fd[0]); // close input side of pipe
int cc;
int fsize;
struct sockaddr_in from;
int serials[500];
int i;
for (i = 0; i < 500; i++) serials[i] = 0;
char buffer[2048];
while (1) {
fsize = sizeof(from);
cc = recvfrom(socketNo, buffer, 2048, 0, (struct sockaddr*)&from, &fsize);
if (cc < 0) perror("Receive error");
datagram data = decodeDatagram(buffer);
if (serials[data.serial] == 0) {
write(fd[1], buffer, (strlen(buffer)+1));
serials[data.serial] = 1;
}
}
} else { // parent
close(fd[1]); // close output side of pipe
return 0;
}
(The "serials" array is for not forwarding repeated messages, as messages are sent multiple times to improve reliability. I know a fixed size for this array is not good practice, but the tests for this assignment don't send that many messages so it's OK in this context.)
The beginning of the receive function looks like this:
int fRecv(int* id, char* buf, int nbytes) {
checkDatagramTable(*id);
char* tbuf = malloc((nbytes + 9) * sizeof(char));
int rbytes = read(fd[0], tbuf, nbytes + 9);
The "+9" is to accommodate additional information that gets packaged along with the message to be sent, for flush channel ordering. This is also a pretty sketchy area, but allocating more space to be extra sure has not helped the issue.
I know there's quite a bit of extraneous stuff in here, references to other functions etc. But the problem surely lies in how I'm piping the data through, so the source of my issue should lie there somewhere.
Thanks in advance for your assistance; it is truly appreciated.
This looks suspicious. (what is in the packets? They could be binary) Where is the typedefinition for datagram ?
fsize = sizeof(from);
cc = recvfrom(socketNo, buffer, 2048, 0, (struct sockaddr*)&from, &fsize);
if (cc < 0) perror("Receive error");
datagram data = decodeDatagram(buffer);
if (serials[data.serial] == 0) {
write(fd[1], buffer, (strlen(buffer)+1)); // <-- ????
serials[data.serial] = 1;
}
I'd try instead:
write(fd[1], buffer, cc);
UPDATE:
If the message is not null terminated, you'll have to terminate it explicitly:
(if cc == 2048) cc -= 1;
buffer [cc] = '\0'; // <<--
datagram data = decodedatagram(buffer);
...
Also, it is advisable to use "sizeof buffer" instead of "2048".
UPDATE2:
You could test if the strings in the packets are really null-terminated by:
unsigned pos;
cc = recvfrom(socketNo, buffer, 2048, 0, (struct sockaddr*)&from, &fsize);
if (cc < 0) perror("Receive error");
for pos=0; pos < cc; pos++) {
if (buff[pos] == 0) break;
}
switch (cc-pos) {
case 0: fprintf (stderr, "No nul byte found in packet: I lose!\n" ); break;
default: fprintf (stderr, "Spurious nul byte found in the middle of packet\n" );
case 1: break;
}
datagram data = decodeDatagram(buffer);
if (serials[data.serial] == 0) {
write(fd[1], buffer, cc);
serials[data.serial] = 1;
}
I am wanting my TCP server and client to "talk" to each other depending on what the other says. But when I run, it seems to go through one iteration, but then both the server and client seem to just "hang" there. Once I kill it on the client side, I end up getting a broken pipe error.
Can anyone interpret what I am doing wrong here?
SERVER code snippet:
while(feof(file_ptr) == 0)
{
fscanf(file_ptr, "%s", supplies);
printf("%s\n", supplies);
//SEND supplies to all clients (currently just one client)
n = write(newsockfd, supplies, strlen(supplies));
if (n < 0)
{
error("ERROR writing to socket");
}
//Receive a request from client that they can use supply
bzero(buffer,2);
n = read(newsockfd,buffer,2);
if (n < 0)
{
error("ERROR reading from socket");
}
printf("Who is using supply? %s\n", buffer);
if(strcmp(buffer, "A"))
{
aRounds++;
}
while(done != 1)
{
//WAIT loop until message CO is received from client
bzero(buffer,2);
n = read(newsockfd,buffer,2);
if (n < 0)
{
error("ERROR reading from socket");
}
printf("Done? %s\n", buffer);
if(strcmp(buffer, "CO"))
{
done = 1;
}
}
done = 0;
}
fclose(file_ptr);
n = write(newsockfd, "DN", 2);
CLIENT code snippet:
while(noSupplies != 1)
{
//RECEIVE MSG from server about supplies
bzero(buffer,2);
n = read(sockfd,buffer,2);
if (n < 0)
{
error("ERROR reading from socket");
}
printf("%s\n",buffer);
if(strcmp(buffer, "BC") == 0 || strcmp(buffer, "CB") == 0)
{
//SEND server msg that you are using supply
n = write(sockfd,"A", 1);
if (n < 0)
{
error("ERROR writing to socket");
}
printf("Client A has received components %s during round %d.\n", buffer, round);
n = write(sockfd,"CO", 2);
if (n < 0)
{
error("ERROR writing to socket");
}
}
else if(strcmp(buffer, "DN"))
{
noSupplies = 1;
}
round++;
}
I get the following from the server (before killing):
BC
Who is smoking? A
Done smoking? CO
And the following from the client (before killing):
BC
Client A has received components BC during round 1.
Then after killing from the client.. I get the following from the server:
BC
Who is using supply? A
Done? CO
Done?
CB
Who is using supply?
Done?
CB
Who is using supply?
Done?
BC
Broken pipe
Does anyone understand what I am doing wrong? (looking for code fix!)
Just an FYI.. I will be changing the server code eventually to handle listening to mulitple clients (TCP) but one hurdle at a time right? :S
Thanks!
Not sure if this is all of your problem, but
bzero(buffer, 2);
n = read(socket, buffer, 2);
...
printf("%s", buffer);
is not correct, since if the read actually did get 2 bytes and neither of them were 0 then buffer may not be a null terminated string (unless there is something earlier that made that so).
Several calls to strcmp should also always fail for the same reason -- the read in string is not 0 terminated so strcmp thinks that it keeps on going and therefore is not the same as the compared to string, even if the first letters are the same.
edit
n = read(socket, buffer, 2);
if (n<0) {
die_horrible_death();
}
buffer[n] = 0; // since arrays are 0 indexed this should be after last read character
printf("I just read: \"%s\"\n", buffer);
Your server code writes only once something to the client, at the beginning. Later, in the loop - it just reads.
OTOH your client reads and writes in the loop.
Since you're working with sockets in a blocking mode - this means that your client eventually will become blocked in a call to recv.
P.S.
No offenses - but you have a lot to learn.
TCP is a tunnel. When your server sends 2 bytes of data in a single call to send/write - there's no guarantee your client will read this within a single call to recv.
Hence - reading and parsing always must be done in a sort of a loop.
You can't write a really robust client/server with ability to timeout/interrupt on demand using only blocking I/O.
Learn, learn, learn
I basically have a server set up and I'm accepting new clients(UNIX) and i'm using select() command to wait for activity on file descriptor but I'm not sure how to write from the clients side and then read it on the servers side
FD_ZERO(&readfds);
FD_SET(server_sockfd, &readfds);
FD_SET(STDIN_FILENO, &readfds);
while (1) {
testfds = readfds;
select(4 + MAXCLIENTS, &testfds, NULL, NULL, NULL);
for (fd = 0; fd < 4 + MAX_CLIENTS; fd++) {
if (FD_ISSET(fd, &testfds)) {
if (fd == server_sockfd) { /* new connection request */
client_sockfd = accept(server_sockfd, NULL, NULL);
if (num_clients < MAXCLIENTS) {
FD_SET(client_sockfd, &readfds);
num_clients++;
} else {
sprintf(message, "XSorry, too many clients. Try again later.\n");
write(client_sockfd, message, strlen(message));
close(client_sockfd);
}
} else if (fd == STDIN_FILENO) {
fgets(kb_message, BUFSIZ + 1, stdin);
if (strcmp(kb_message, "quit\n") == 0) {
sprintf(message, "XServer is shutting down.\n");
for (fd2 = 4; fd2 < 4 + MAX_CLIENTS; fd2++) {
if (FD_ISSET(fd2, &readfds)) {
write(fd2, message, strlen(message));
close(fd2);
}
}
close(server_sockfd);
exit(EXIT_SUCCESS);
} else {
sprintf(message, "M%s", kb_message);
for (fd2 = 4; fd2 < 4 + MAX_CLIENTS; fd2++)
if (FD_ISSET(fd2, &readfds))
write(fd2, message, strlen(message));
}
} else { /* client leaving */
close(fd);
FD_CLR(fd, &readfds);
num_clients--;
}
}
}
}
How would I handle write request from clients and then write back to them, would it be under "else" and how can I check if client is exiting or writing.
Thanks
The most common mistake with select(2) is not re-initializing the descriptor sets, since second, third, and forth arguments are input-output parameters.
Setup an fd_set, for reading before the outer loop, add listening socket descriptor to it, enter the loop, make a copy of the this fd_set and give the copy to select(2). When new connection arrives, add its descriptor to the original fd_set. Same for closed socket (error or EOF on read(2)) - remove the descriptor from the original fd_set.
Hope this helps.
You are correct in thinking you need the read code in your 'else' block. If a file descriptor triggers and it isn't stdin or the 'connect' descriptor, then it is one of your clients attempting to send you data. When one of those file descriptors is triggered in the select, you need to call 'read' on that descriptor to read the data into the buffer. The read command will return you the number of bytes read. If this is a positive number, then it indicates the client has sent you data. If it is zero, then that indicates that the client has ended the TCP connection to your server.
The else block will look something like:
else
{
//Existing connection has data for us to read
if((nBytes = read(fd, buffer, MAXBUFFER)) <= 0)
{
if(nBytes == 0)
{
//Actually, its sending us zero bytes, connection closed
printf("Socket %d hung up\n", fd;
}
else
printf ("Read Error"\n)
}
Also, Follow Nikolai N Fetissov's advice above and make sure that when client's connect you store their fd in a permanent fd_set structure, as the one you are using is being modified by the select call.
Your problem might be that you have a variable called read. It's going to mask one of hte functions you need to use - the read() system call to get data out of the socket. The client puts it in with write(). You might also want to check the return value from select(), which will tell you how many of the file descriptors are ready for reading. Then you can check which ones using FD_ISSET(). It looks like you're doing that part already (except you seem to be checking the wrong variable?)... just call read() on that file descriptor to get out the data the client wrote.
else
{
bzero(buf,100);
n=read(i,buf,100); // Read the client message
buf[n]='\0';
if(n==0) // Check the client is closed or not
{
printf("%d is closed\n",i);
close(i);
FD_CLR(i, &master);
if(i==fdmax)
fdmax--;
}
else
{
n=strlen(buf);
write(1,buf,n);
fflush(stdout);
write(1,"Enter the message\n",18);
bzero(buf,100);
read(0,buf,100);
buf[n]='\0';
write(i,buf,n);
fflush(stdout);
}
}
Notes:
After accept the client, Add the client in the fd set.
Then read the message from the client
If the message is equal to 0, then the client is closed.
If you want to send the message to the client, using the client fd, you can send to the client