so i'm trying to implement a basic client-server calculator with threads. I was able to implement the communication between cliente-server with sockets but i'm at a loss when i try to add threads to the code.
This is my first time using this forum so please sorry if i'm making a dumb question but i already searched for some answers and i can't find anything that helps :(
I can upload my whole code bit for now i think this will suffice:
Creating a thread in main:
while(cliente_socket_fd = accept(socket_fd,(struct sockaddr*)&name_cliente,(socklen_t*)&cliente_name_len))
{
pthread_t thread;
new_sock = malloc(1);
*new_sock = cliente_socket_fd;
if(i <= nThreads) // nThreads is the maximum of threads permitted
{
if(pthread_create(&thread, NULL, connectionSocket, (void*)new_sock) < 0)
{
perror("Error");
return 1;
}
i++;
}
}
Function that recives information from create thread:
void *connectionSocket(void *sockR)
{
int sock = *(int*)sockR;
int x;
char buffer[1024];
do{
x = strlen(buffer) + 1;
read(sock,buffer, x);
readSocket(sock, buffer);
}while(1 || strcmp(buffer,"end") != 0);
close(sock);
return 0;
}
Server read socket function:
int readSocket(int sock, const char* buffer)
{
//char buffer[1024];
//char message[1024] ;
int ssize;
ssize = strlen(buffer);
if(ssize == 0)
return 0;
char* message = (char*) malloc(ssize);
//memset(buffer, 0, sizeof buffer);
memset(message, 0, sizeof message);
strcat(message, buffer);
...
writeSocket(sock, message);
...
Function that writes in the socket:
void writeSocket(int sock, const char* mensagem)
{
int ssize = strlen(message) + 1;
write(sock, &ssize, sizeof(ssize));
write(sock, message, ssize);
}
Again sorry if i'm being a noob or asking too much but any help is greatly appreciated. Thanks.
in function: connectionSocket()
this line
x = strlen(buffer) + 1;
is garbage because strlen() stops when it finds a NUL byte.
However, the buffer has not been set to a known condition so there may or may not be a NUL byte in the buffer. I.E. undefined behavior.
the accept() function returns -1 when an error occurs, However the posted code will blindly continue into the body of the loop
Suggest writing that line similar to:
while( 1 )
{
cliente_socket_fd = accept(socket_fd,(struct sockaddr*)&name_cliente, (socklen_t*)&cliente_name_len) );
if( -1 == cliente_socket_fd )
{
perror( "accept failed" );
break; // exit loop
}
// implied else, accept successful
then, to be able to access those threads, later, this line:
if(pthread_create(&thread, NULL, connectionSocket, (void*)new_sock) < 0)
should be setting a 'unique' 'thread' rather than the only 'thread' Suggest:
pthread_t thread[ nThreads ] = {0};
if(pthread_create(&thread[i], NULL, connectionSocket, (void*)new_sock) < 0)
then, a socket is (effectively) an integer, which, depending on the underlying hardware architecture is 4 or 8 bytes So this line:
new_sock = malloc(1);
does not allocate enough bytes, it should be:
new_sock = malloc( sizeof( int ) );
if( !new_sock )
{ // then, malloc failed
perror( "malloc failed" );
break; // exit loop
}
// implied else, malloc successful
*new_sock = cliente_socket_fd;
this line:
}while(1 || strcmp(buffer,"end") != 0);
will never exit the loop. Suggest:
}while( strcmp(buffer,"end") != 0 );
this line:
read(sock,buffer, x);
will try to read some random number of bytes because the value in 'x' is not valid. Suggest;
read(sock,buffer, sizeof( buffer ));
the above are the most obvious problems in the posted code. If you had posted a cleanly compiles, short, example that can be run and still shows the problems, I could have helped you more.
Related
I'm doing an exercise about UDP sockets in C. When the client sends a specific message (e.g hi) the server has to send "Nice to meet you". If no standard reply is found the server sends "No suitable reply". My problem is that memset fails if I try to return the reply like this:
return "No suitable reply";
and it doesn't if I return the reply in this way:
char* foo = malloc(sizeof(char*));
memset(foo, 0, strlen(ses));
memcpy(foo, "No suitable reply", 17);
return foo;
I tried to google a solution to this and I found this and this, but they don't seem to address my problem (I first thought that memset doesn't work on a string declared like char string[] = "something" but in the second example they use memset on a static string).
Here is the whole code (the memset I'm talking about is right at the end):
/*
Alessandro Dussin 5AI
2018-17-11
Write a program to handle a single UDP "connection"
*/
//Standard libraries
#include <stdio.h>
#include <stdlib.h>
//Sockets libraries and connection ahndling
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
//Read/write ops on file descriptors
#include <unistd.h>
//String ops
#include <string.h>
#include <assert.h>
void chopN(char *str, size_t n)
{
assert(n != 0 && str != 0);
size_t len = strlen(str);
if (n > len)
return; // Or: n = len;
memmove(str, str+n, len - n + 1);
}
//Required by the exercise. Given a certain word or phrase, reply with a specific string
char* switchreply(char* str){
//Extracts the word or phrase (Basically removes the "/command " word)
chopN(str, strlen("/stdreply "));
int i = 0;
for(; i < strlen(str); i++){
if(str[i] == '\n'){
str[i] = '\0';
break;
}
}
if(strcmp(str, "ciao") == 0){
return "ciao anche a te!";
}
else if(strcmp(str, "I hate you") == 0){
return "I hate you too!";
}
return "";
}
char* stdreply(char *str){
char* tmp = malloc(sizeof(char)*128);
int i = 0;
//printf("Entered stdreply... str at the start of the func: %s\n", str);
for(; i < strlen(str); i++){
tmp[i] = str[i];
//printf("tmp: %s\n", tmp); //DEBUG
if(strcmp(tmp, "/echo ") == 0){ // if(strcmp() == 0) is necessary because
//otherwise 0 would be interpreted as FALSE
//printf("Echo detected\n"); //DEBUG
chopN(str, strlen("/echo "));
str[strlen(str)] = '\0';
return str;
}
else if(strcmp(tmp, "/stdreply ") == 0){
//printf("I got into the else if\n"); //DEBUG
char* tmpreply = calloc(strlen(str), sizeof(char*));
tmpreply = switchreply(str);
//printf("tmpreply: %s\n", tmpreply);
str = malloc(sizeof(char*)*strlen(tmpreply));
memcpy(str, tmpreply, strlen(tmpreply));
//str[strlen(str)] = '\0'; //DEBUG
//printf("str: %s\n", str); //DEBUG
return str;
}
else if(strcmp(tmp, "/TODO") == 0){
char* ses = malloc(sizeof(char*));
memset(ses, 0, strlen(ses));
memcpy(ses, "work in progress", 17);
return ses;
}
}
return "No suitable reply";
}
int main(int argc, char **argv){
if(argc < 2){
printf("Usage: ./server port");
exit(0);
}
int serverfd;
serverfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in server;
server.sin_port = htons(atoi(argv[1]));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
if(bind(serverfd, (struct sockaddr *)&server, sizeof(server)) < 0){
perror("Bind() error: ");
fflush(stderr);
}
//"UDP message receiver" variables declarations
int bytes; //Reads how many bytes the funcion recvfrom has read
struct sockaddr_in from;
char* buffer = malloc(sizeof(char*)); //String to which save the client message
memset(buffer, 0, strlen(buffer)); //and set it to zero
socklen_t fromlen = sizeof(struct sockaddr_in);
const char stdrep[] = "Message Received: "; //This string will always be
//printed upon receiving a message
char* reply = malloc(sizeof(char*)); //This is where the return value of
//stdreply() will be stored
memset(reply, 0, strlen(reply)); //and set it zero
//This while will keep "listening" for udp messages
while((bytes = recvfrom(serverfd, buffer, 1024, 0, (struct sockaddr *)&from, &fromlen)) > 0){
//From teacher's example. Write to stdout
write(1, stdrep, strlen(stdrep));
write(1, buffer, bytes);
//Detect a basically empty string (if the client has pressed only enter)
if(buffer[0] == '\n'){
bytes = sendto(serverfd, "You pressed only enter!\n", 18, 0, (struct sockaddr *)&from, fromlen);
}
//Act according to the client message
reply = stdreply(buffer);
bytes = sendto(serverfd, reply, strlen(reply), 0, (struct sockaddr *)&from, fromlen);
if (bytes < 0){
perror("sendto: ");
fflush(stderr);
}
memset(buffer, 0, 1024);
memset(reply, 0, strlen(reply)); //The seg fault happens right here
fflush(stdout);
}
return 0;
}
There are plenty of problems in the code you are posting.
As #JonBolinger already noted, sizeof(char*) returns the size in bytes of a pointer to a char. On Intel platforms this will be either 4 or 8, depending on whether you are running on 32-bit or 64-bit. (So you end up allocating buffers of 4 or 8 bytes)
You consistently try to clear your dynamically-allocated buffers with memset(). malloc() will return memory filled with garbage, and you indicate how many bytes to clear by using strlen() on the returned buffer. strlen() will scan the buffer until it finds the first 0 character to compute the string's length. Since the buffer is filled with garbage, this can easily give you a value outside of the boundaries of your memory block and you will end up corrupting memory.
Every call to malloc() should be matched to a free() call, otherwise you will leak memory. This is especially important if your program is long-running.
When you are working with temporary local strings (strings that are not returned to the caller), it is very common practice to use a local char array instead of malloc(). This way, the buffer gets allocated on the stack and will be released automatically when your function exits scope. Just be sure to use 'safe' string functions like strncpy() that will accept as a parameter the length of the buffer, to avoid overwrites.
void Example(char* anotherString ) {
char tmpString[256]; // this will create a local buffer with capacity of 256 bytes
strncpy(tmpString, anotherString, sizeof(tmpString)); // copy string, without risk of overflowing the buffer
}
Warning: NEVER attempt to return a local temporary buffer as a result, remember that it will no longer exist when the function exits and although the returned value may initially have meaningful results, they will certainly be destroyed as soon as you call another function. Instead of this, another common practice when you need a string return value, instead of returning a string allocated with malloc() -that would need to be released with free() - you pass a local buffer that will hold the result as a parameter, like this:
void func1() {
char result[256];
func2(result, 256);
// after calling, result will carry "a returned string"
}
void func2(char* result, size_t bufferLen) {
strncpy(result, "a returned string", bufferLen);
}
I think your code would benefit greatly if you can transform it to using this style where applicable.
reply = stdreply(buffer);
This does not copy a string. It overwrites the pointer with a different one, losing the original pointer.
memset(reply, 0, strlen(reply));
This would clear the string if it were allocated with malloc. If it is a constant string like "No suitable reply" then it may be read-only, so it generates a segfault.
I wrote a C application for a socialization network and also a simple room-based chat. I used ncurses, sockets and basic networking stuff.
The problem is that my function uses select() to read from server socket AND stdin so when I start to write a message, the output window freezes and only shows messages from other clients after I hit enter.
I tried everything possible .. Is there a way to fix this ?
I also tried to force nocbreak().It works okay but if I do that, when I write the message, the echoing is disabled and nothing shows up in the input window as I type, even though the message is there but like "invisible".
Here is the code :
ssize_t safePrefRead(int sock, void *buffer)
{
size_t length = strlen(buffer);
ssize_t nbytesR = read(sock, &length, sizeof(size_t));
if (nbytesR == -1)
{
perror("read() error for length ! Exiting !\n");
exit(EXIT_FAILURE);
}
nbytesR = read(sock, buffer, length);
if (nbytesR == -1)
{
perror("read() error for data ! Exiting !\n");
exit(EXIT_FAILURE);
}
return nbytesR;
}
ssize_t safePrefWrite(int sock, const void *buffer)
{
size_t length = strlen(buffer);
ssize_t nbytesW = write(sock, &length, sizeof(size_t));
if (nbytesW == -1)
{
perror("write() error for length ! Exiting !\n");
exit(EXIT_FAILURE);
}
nbytesW = write(sock, buffer, length);
if (nbytesW == -1)
{
perror("write() error for data ! Exiting !\n");
exit(EXIT_FAILURE);
}
return nbytesW;
}
void activeChat(int sC, const char *currentUser, const char *room)
{
char inMesg[513], outMesg[513];
char user[33];
int winrows, wincols;
WINDOW *winput, *woutput;
initscr();
nocbreak();
getmaxyx(stdscr, winrows, wincols);
winput = newwin(1, wincols, winrows - 1, 0);
woutput = newwin(winrows - 1, wincols, 0, 0);
keypad(winput, true);
scrollok(woutput, true);
wrefresh(woutput);
wrefresh(winput);
fd_set all;
fd_set read_fds;
FD_ZERO(&all);
FD_ZERO(&read_fds);
FD_SET(0, &all);
FD_SET(sC, &all);
wprintw(woutput, "Welcome to room '%s' \n Use /quitChat to exit !\n!", room);
wrefresh(woutput);
while (true)
{
memcpy( &read_fds, &all, sizeof read_fds );
if (select(sC + 1, &read_fds, NULL, NULL, NULL) == -1)
{
perror("select() error or forced exit !\n");
break;
}
if (FD_ISSET(sC, &read_fds))
{
memset(inMesg, 0, 513);
safePrefRead(sC, user);
safePrefRead(sC, inMesg);
wprintw(woutput, "%s : %s\n", user, inMesg);
wrefresh(woutput);
wrefresh(winput);
}
if (FD_ISSET(0, &read_fds))
{
//wgetnstr(winput, "%s", outMesg);
int a, i = 0;
while ( i < MAX_BUF_LEN && (a = wgetch(winput)) != '\n')
{
outMesg[i] = (char)a;
i++;
}
outMesg[i] = 0;
if (outMesg[0] == 0)
continue;
if (strcmp(outMesg, "/quitChat") == 0)
{
safePrefWrite(sC, outMesg);
break;
}
safePrefWrite(sC, outMesg);
delwin(winput);
winput = newwin(1, wincols, winrows - 1, 0);
keypad(winput, true);
wrefresh(winput);
}
}
delwin(winput);
delwin(woutput);
endwin();
}
-safePrefWrite and safePrefRead are wrappers for prexied read / write and error treating
-sC is the server socket.
LE: I tried using fork and threads. Using fork was behaving the same and threads were a disaster, the terminal was messed up.
Thank you.
modify the while(true) loop to only handle one char at a time for the stdin.
Which mostly means for stdin, read a single char:
if char is '\n' then handle as currently,
otherwise, just append char to the buffer to write.
Always, before appending a char to buffer to write, check that buffer is not full.
add code to handle the case where the buffer to write is full
end the function with this sequence:
delwin(winput);
delwin(woutput);
endwin();
endwin();
to end both windows.
Do not call endwin() during processing of the socket input.
Do not call endwin() when select() returns an error condition
the fd_set is not an intrinsic size in C, so use memcpy() to set
read_fds from all. suggest:
memcpy( &read_fds, &all, sizeof read_fds );
the parameter: currentUser is not used, suggest inserting the line:
(void)currentUser;
to eliminate a compiler warning message.
for readability, and ease of understandability, suggest #define the magic numbers 513 and 33 with meaningful names, then use those meaningful names throughout the code.
#define MAX_BUF_LEN (513)
#define MAX_USER_LEN (33)
this line: outMesg[i] = a; raises a compiler warning, suggest:
outMesg[i] = (char)a;
This line: while ( (a = wgetch(winput)) != '\n') can allow the buffer outMesg[] to be overrun, resulting in undefined behaviour and can lead to a seg fault event. suggest:
while ( i < MAX_BUF_LEN && (a = wgetch(winput)) != '\n')
Suggest posting the prototypes for the safePrefWrite() and safePrefRead() functions, similar to:
void safePrefRead( int, char * );
void safePrefWrite( int, char * );
As noted by #user3629249, there are several criticisms which can be applied to the sample code. However, OP's question is not addressed by those improvements.
OP seems to have overlooked these functions:
cbreak or raw, to make wgetch read unbuffered data, i.e., not waiting for '\n'.
nodelay or timeout, to control the amount of time wgetch spends waiting for input.
By the way, making select work with a curses program will make assumptions about the curses library internal behavior: getting that to work reliably can be troublesome.
Fixed it finally by using only the big loop.
Here is the code if anyone has the same problem in the future :
if (FD_ISSET(0, &read_fds))
{
inChar = wgetch(winput);
if (inChar == 27)
{
safePrefWrite(sC, "/quit");
break;
}
if (inChar == KEY_UP || inChar == KEY_DOWN || inChar == KEY_LEFT || inChar == KEY_RIGHT)
continue;
if (inChar == KEY_BACKSPACE || inChar == KEY_DC || inChar == 127)
{
wdelch(winput);
wrefresh(winput);
if (i != 0)
{
outMesg[i - 1] = 0;
i--;
}
}
else
{
outMesg[i] = (char)inChar;
i++;
}
if (outMesg[i - 1] == '\n')
{
outMesg[i - 1] = 0;
i = 0;
if (outMesg[0] == 0)
continue;
if (strcmp(outMesg, "/quit") == 0)
{
safePrefWrite(sC, outMesg);
break;
}
safePrefWrite(sC, outMesg);
delwin(winput);
winput = newwin(1, wincols, winrows - 1, 0);
keypad(winput, true);
wrefresh(winput);
memset(outMesg, 0, 513);
}
}
I also use raw() to disable signals and to treat the codes how I want.
Anything else above and below this "if" is just like in the 1st post.
I'm trying to get the source code of my website using c, I'm able to connect and everything but when I implement the recv() code, it only receives the last few bytes of the source code. I'd like to dynamically allocate space for the buffer to receive more using the C functions malloc and realloc.
This is the code I have so far:
char *buffer = NULL;
unsigned int i = 0;
unsigned long LEN = 200;
unsigned long cur_size = 0;
buffer = (char*)malloc(sizeof(char)*LEN);
do
{
if( status >= LEN )
{
cur_size += status;
buffer = (char*)realloc(buffer, cur_size);
}
status = recv(cSocket, buffer, LEN, 0);
if( status == 0 )
{
printf("Bye\n");
}
else if( status > 0 )
{
printf("%d\n", status);
}
else
{
printf("socket error=%d\n", WSAGetLastError());
break;
}
}while( status > 0 );
printf("%s\n", buffer);
It still doesn't print the whole source code. How should I go about this?
Pseudocode:
buffer = 'len chars';
loop:
if( status >= buffer ) buffer = 'resize to status chars';
status = recv(sock, buffer, len, 0);
end loop
As you resize the buffer in advance this needs to be reflected by its size. Which currently is not the case.
To fix this you could, for example, initialise cur_size with LEN by changing
unsigned long cur_size = 0;
to
unsigned long cur_size = LEN;
Assuming the fix above, you want to append to the buffer and not overwrite it with every call to recv().
To do so change this line
status = recv(cSocket, buffer, LEN, 0);
to be
status = recv(cSocket, buffer + cur_size - LEN, LEN, 0);
A more straight forward approach would be to not track the size of the buffer, but the number of bytes received and just always increase the buffer by a constant size.
Also the two calls to allocate memory can be replaced by one:
char *buffer = NULL;
unsigned long LEN = 200;
unsigned long bytes_received = 0;
unsigned long cur_size = 0;
int status = 0;
do
{
if (bytes_received >= cur_size)
{
char * tmp;
cur_size += LEN;
tmp = realloc(buffer, cur_size);
if (NULL == tmp)
{
fprintf(stderr, "realloc error=%d\n", WSAGetLastError());
break;
}
buffer = tmp;
}
status = recv(cSocket, buffer + bytes_received, LEN, 0);
if (status == 0)
{
printf("Bye\n");
}
else if (status > 0)
{
bytes_received += status;
printf("%d\n", status);
}
else /* < 0 */
{
fprintf(stderr, "socket error=%d\n", WSAGetLastError());
}
} while (status > 0);
printf("%s\n", buffer);
Well, after a bit of research, I came across this website and finally found what I was looking for.
Binary tides
Although it uses linux's fcntl, the windows equivalent is ioctlsocket which is used to set the socket's non-blocking mode.
To see the exact function, visit the website. I modified the version and set my socket to blocking mode.
int total_recv(SOCKET s)
{
int size_recv = 0, total_size = 0, block = 00;
char chunk[BUFLEN];
ioctlsocket(s, FIONBIO, (unsigned long*)&block); // set mode to block
// not necessary but clarification of function, mode is block by
// default
while( 1 )
{
memset(chunk, 0, BUFLEN);
if( ( size_recv = recv(s, chunk, BUFLEN, 0) ) == SOCKET_ERROR )
{
printf("Error receiving\n");
}
else if( size_recv == 0 )
{
break;
}
else
{
total_size += size_recv;
// i used file since console wouldn't show full source code
FILE *fp = NULL;
fp = fopen("source.txt", "a");
fprintf(fp, chunk);
fclose(fp);
}
}
return total_size;
}
I was searching for hours to get an answer about my question, but didnt find anything. Maybe I get some help here.
What I'm trying to do:
A Java-Client sends a message to a C-Server. The message contains different types like integer, short and also a string (e.g. message = int: total_msg_length; short: operation; string: hello --> total_msg-length=4 (size of integer), operation = 2 (size of short), hello = 5 (each letter is 1 byte=5).
So, how can I receive the message in my server? The code below receives an Integer (works fine). Next step will be to receive a short and then a string (converted in US-ASCII).
int *msg;
int recv_size;
int final_msg;
if( (recv_size = recv(client_socket, &msg, sizeof(msg), 0 )) < 0 ){
error_exit("Fehler bei recv(message_len)");
}
final_msg = endian_swap(msg);
printf("Message: %d\n", final_msg);
return final_msg;
Is there a way to use a byte array instead of char buffer? Im thankful for every help. Please excuse my bad english, I'm from germany :-)
You need to create a generic "read_n_bytes" function.
This you can use to read the message-size, the operation and the text, in three successive calls.
Those three calls you then wrap in a function to be called to read an entire message.
A generic reader might look like this:
/*
* Reads n bytes from sd into where p points to.
*
* returns 0 on succes or -1 on error.
*
* Note:
* The function's name is inspired by and dedicated to "W. Richard Stevens" (RIP).
*/
int readn(int sd, void * p, size_t n)
{
size_t bytes_to_read = n;
size_t bytes_read = 0;
while (bytes_to_read > bytes_read)
{
ssize_t result = read(sd, p + bytes_read, bytes_to_read);
if (-1 == result)
{
if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
continue;
}
# ifdef DEBUG
{
int errno_save = errno;
perror("read() failed");
errno = errno_save;
}
# endif
break;
}
else if(0 == result)
{
# ifdef DEBUG
{
int errno_save = errno;
fprintf(stderr, "%s: Connection closed by peer.", __FUNCTION__);
errno = errno_save;
}
# endif
break;
}
bytes_to_read -= result;
bytes_read += result;
}
return (bytes_read < bytes_to_read) ?-1 :0;
}
The Situation
After reading Unix Socket Programming, W.Richard Steven, I'm writing a P2P program in which the main thread creates thread pool in which five sub-threads live. it then monitors 50 sockets with kqueue(). when a event occurs in a specified socket (e.g, receiving data on the socket.), the main thread copies socket descriptor into a shared array and awakes one thread in the thread pool. the sub thread then processes a request from the socket. Also, I have protected the shared array using both mutex variable and conditional variable.
Question
The Author presents the source codes "server/serv08.c" and "server/pthread08.c" in the Section 30.12 and 30.13 in the book, respectively, as if there is no something wrong with this code. But, when I've written a code snippet similar to one author present, thread synchronization doesn't work well. Why does iput become equal to iget in main thread?
Code
--Global variable--
typedef struct tagThread_information
{
int sockfd;
} Thread_information;
Thread_information peer_fds[MAX_THREAD];
pthread_mutex_t peerfd_mutex;
pthread_cond_t peerfd_cond;
pthread_mutex_t STDOUT_mutex;
int iput;
int iget;
--Main thread--
void Wait_for_Handshake(download_session *pSession, int nMaxPeers)
{
struct kevent ev[50], result[50];
int kq, i, nfd;
int c = 1;
if( (kq = kqueue()) == -1)
{
fprintf(stderr, "fail to initialize kqueue.\n");
exit(0);
}
for(i = 0 ; i < nMaxPeers; i++)
{
EV_SET(&ev[i], pSession->Peers[i].sockfd, EVFILT_READ, EV_ADD, 0, 0, 0);
printf("socket : %d\n", (int)ev[i].ident);
}
// create thread pool. initialize mutex and conditional variable.
iput = 0;
iget = 0;
pthread_mutex_init(&STDOUT_mutex, NULL);
pthread_mutex_init(&peerfd_mutex, NULL);
pthread_cond_init(&peerfd_cond, NULL);
// Assume that MAX_THREAD is set to 5.
for(i = 0 ; i < MAX_THREAD; i++)
thread_make(i);
while(1)
{
nfd = kevent(kq, ev, nMaxPeers, result, nMaxPeers, NULL);
if(nfd == -1)
{
fprintf(stderr, "fail to monitor kqueue. error : %d\n", errno);
nMaxPeers = Update_peer(ev, pSession->nPeers);
pSession->nPeers = nMaxPeers;
continue;
}
for(i = 0 ; i < nfd; i++)
{
pthread_mutex_lock(&peerfd_mutex);
peer_fds[iput].sockfd = (int)result[i].ident;
if( ++iput == MAX_THREAD)
iput = 0;
if(iput == iget) // Here is my question.
{
exit(0);
}
pthread_cond_signal(&peerfd_cond);
pthread_mutex_unlock(&peerfd_mutex);
}
}
}
--sub thread--
void * thread_main(void *arg)
{
int connfd, nbytes;
char buf[2048];
for( ; ; )
{
/* get socket descriptor */
pthread_mutex_lock(&peerfd_mutex);
while( iget == iput)
pthread_cond_wait(&peerfd_cond, &peerfd_mutex);
connfd = peer_fds[iget].sockfd;
if ( ++iget == MAX_THREAD )
iget = 0;
pthread_mutex_unlock(&peerfd_mutex);
/* process a request on socket descriptor. */
nbytes = (int)read(connfd, buf, 2048);
if(nbytes == 0)
{
pthread_mutex_lock(&STDOUT_mutex);
printf("\n\nthread %ld, socket : %d, nbytes : %d\n\n\n", (long int)pthread_self(), connfd, nbytes);
printf("socket closed\n\n");
pthread_mutex_unlock(&STDOUT_mutex);
close(connfd);
continue;
}
else if(nbytes == -1)
{
close(connfd);
pthread_mutex_lock(&STDOUT_mutex);
printf("\n\nthread %ld, socket : %d, nbytes : %d\n\n\n", (long int)pthread_self(), connfd, nbytes);
perror("socket error : ");
write(STDOUT_FILENO, buf, nbytes);
printf("\n\n\n\n");
pthread_mutex_unlock(&STDOUT_mutex);
continue;
}
pthread_mutex_lock(&STDOUT_mutex);
printf("\n\nthread %ld, socket : %d, nbytes : %d\n\n\n", (long int)pthread_self(), connfd, nbytes);
write(STDOUT_FILENO, buf, nbytes);
printf("\n\n\n\n");
pthread_mutex_unlock(&STDOUT_mutex);
}
}
In your main thread:
if( ++iput == MAX_THREAD)
iput = 0;// so iput is 0 --> MAX_THREAD
And in your sub thread:
if ( ++iget == MAX_THREAD )
iget = 0;// So iget is 0 --> MAX_THREAD
Since the sub thread and the main thread runs at the "same time",and they are golbal values .the iput maybe equare to iget sometime.
From "UNIX Network Prgramming Volume 1, 2nd Edition", chapter 27.12, page 757, from the annotations to the lines 27-38 of server/serv08.c:
We also check that the iput index has not caught up with the iget index, which indicates that our array is not big enough.
For reference the lines mentioned above (take from here):
27 for ( ; ; ) {
28 clilen = addrlen;
29 connfd = Accept(listenfd, cliaddr, &clilen);
30 Pthread_mutex_lock(&clifd_mutex);
31 clifd[iput] = connfd;
32 if (++iput == MAXNCLI)
33 iput = 0;
34 if (iput == iget)
35 err_quit("iput = iget = %d", iput);
36 Pthread_cond_signal(&clifd_cond);
37 Pthread_mutex_unlock(&clifd_mutex);
38 }
What you have there is a typical circular buffer implementation.
The head and tail pointers/indices point to the same location when the circular buffer is empty. You can see this being tested in the code while (iget == iput) ... which means "while the queue is empty ...".
If, after an insertion at the head of a circular buffer, head points to tail, that is a problem. The buffer has overflowed. It is a problem because now the buffer now looks empty even though it is full.
That is to say, one unused location is reserved in the buffer; if the buffer has 4096 entries, we can only fill 4095. If we fill 4096, it then we have overflow: it looks like an empty circular buffer.
(We could use all 4096 locations if we allowed the index to go from 0 to 8192, using an extra bit to resolve the ambiguity, so that instead of wrapping to zero past 4095, the pointers would keep going to 4096 ... 8191. We would have to remember to access the array modulo 4096, of course. It's a big cost in complexity for the sake of recovering one wasted element.)
It looks like the code bails on circular buffer overflow because it is structured such that this condition cannot happen, and so it constitutes an internal error. The circular buffer overflows when there are too many descriptors being passed from the producer to the consumer in a single bout.
In general, circular buffer code cannot just bail when the buffer is full. Either the insertion operation has to balk and return an error, or it has to block for more space. So this is a special case based on assumptions particular to the example program.