Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am testing out my code for reconnection in case the connection drop. However I'm facing some Segmentation Error after the socket is re-opened and re-connected.
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h> //socket
#include <bluetooth/bluetooth.h> //bluetooth
#include <bluetooth/rfcomm.h> //bluetooth
#include <string.h> //strlen
#include <arpa/inet.h> //inet_addr
struct BTConnection {
int client;
int s;
int retry;
};
struct BTConnection bt_1;
void *bt_connect(void *arg);
void *bt_send(void *arg);
void *bt_receive(void *arg);
void *bt_connect(void *arg)
{
struct BTConnection *connect = (struct BTConnection*)arg;
struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
char buf[1024] = { 0 };
socklen_t opt = sizeof(rem_addr);
// allocate socket
connect->s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
// bind socket to port 1 of the first available
// local bluetooth adapter
loc_addr.rc_family = AF_BLUETOOTH;
loc_addr.rc_bdaddr = *BDADDR_ANY;
loc_addr.rc_channel = (uint8_t) 1;
bind(connect->s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
// put socket into listening mode
listen(connect->s, 1);
// accept one connection
connect->client = accept(connect->s, (struct sockaddr *)&rem_addr, &opt);
ba2str( &rem_addr.rc_bdaddr, buf );
return 0;
}
void *bt_send(void *arg)
{
struct BTConnection *connect = (struct BTConnection*)arg;
char buf[1024] = { 0 };
int bytes_read, retry;
puts("[Bluetooth] Sending data to device...");
while(1) {
.....
pthread_mutex_lock(&mDataMutex);
puts("send mutex in");
while(connect->retry != 0);
puts("send mutex out");
pthread_mutex_unlock(&mDataMutex);
if(write(connect->client, return_msg, strlen(return_msg)) < 0) {
....
puts("Error reading from socket");
pthread_mutex_lock(&mDataMutex);
puts("send mutex");
if(connect->retry == 0)
connect->retry = 2;
pthread_mutex_unlock(&mDataMutex);
puts("send mutex end");
pthread_mutex_lock(&mDataMutex);
if(connect->retry == 2) {
close(connect->client);
close(connect->s);
bt_connect(&connect);
connect->retry = 0;
puts("send retry success");
} else
retry = 1;
pthread_mutex_unlock(&mDataMutex);
if(retry == 1)
{
puts("bluetooth send retrying in 8 secs");
sleep(8);
}
} else
retry = 0;
// clear buffer
memset(return_msg,0,sizeof return_msg);
memset(temp,0,sizeof temp);
// set to send data every second
sleep(1);
}
return 0;
}
void *bt_receive(void *arg)
{
// similar as above
}
int main(void)
{
bt_1.retry = 0;
int rc;
void *status;
pthread_t bt_connect_thread, bt_send_thread, bt_receive_thread;
pthread_attr_t attr;
/* Initialize and set thread detached attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
// Create BT connect thread and start it
rc = pthread_create(&bt_connect_thread, &attr, bt_connect, (void *)&bt_1);
if (rc)
{
printf("ERROR; return code from pthread_create() is %d\n", rc);
return (-1);
}
rc = pthread_join(bt_connect_thread, &status);
rc = pthread_create(&bt_send_thread, &attr, bt_send, (void *)&bt_1);
rc = pthread_create(&bt_receive_thread, &attr, bt_receive, (void *)&bt_1);
rc = pthread_join(bt_send_thread, &status);
rc = pthread_join(bt_receive_thread, &status);
close(bt_1.client);
close(bt_1.s);
pthread_attr_destroy(&attr);
return 0;
}
The code may look a little messy in sending and receiving part as I was using mutex to check and debug the output.
This is the output I am getting:
[Bluetooth] Allocating socket... Done!
[Bluetooth] Bind socket... Done!
[Bluetooth] Waiting for incoming connections...
[Bluetooth] Accepted connection from 44:6D:6C:6D:1B:BC
[Bluetooth] Bluetooth connection thread completed
[Bluetooth] Receiving data from device...
rcv mutex in
rcv mutex out
[Bluetooth] Sending data to device...
send mutex in
send mutex out
send mutex in
send mutex out
[Bluetooth-Receive] Error reading from socket
receive mutex
receive mutex end
[Bluetooth-Receive] Connections closed. Reconnecting...
[Bluetooth] Allocating socket... Done!
[Bluetooth] Bind socket... Done!
[Bluetooth] Waiting for incoming connections...
[Bluetooth] Accepted connection from 44:6D:6C:6D:1B:BC
[Bluetooth] Bluetooth connection thread completed
Segmentation fault
I am not sure what is wrong since I did ensure that i close the socket before re-opening it. Any help/guidance? Thanks!
Edit: Not sure if bt_send is jamming it since it didn't print out [Bluetooth-Send] Error reading from socket when I'm sending test data automatically every second... (and both send and receive are running simultaneously)
PS. New to C and Socket Programming
void *bt_connect(void *arg)
{
struct BTConnection *connect = (struct BTConnection*)arg;
Okay, so we need to call bt_connect passing it a pointer to a BTConnection.
struct BTConnection *connect = (struct BTConnection*)arg;
...
bt_connect(&connect);
Since connect is a pointer to a BTConnection, &connect is a pointer to a pointer. So why are we passing it to bt_connect?
Related
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 6 years ago.
Improve this question
I have a problem with my C Programm using Codeblocks/Mingw with the GNU GCC Compiler under a Windows environment.
I'm trying to implement a TCP-Server library, which alows to create and manage multiple servers simultanously.
I created some structures to manage the Servers:
typedef struct
{
BOOL bExitThread;
HANDLE hThread;
}TagThreadControl;
typedef struct
{
SOCKET sSocket;
int iMaxReceiveLengthPerMessage;
struct in_addr tagIPAddress;
USHORT usPort;
TagThreadControl tagThread;
TagQueue *ptagReveivedMessages;
}TagConnection_Internal;
typedef struct
{
BOOL bInternalProcessingActive;
UINT uiID;
SOCKET sSocket;
TagConnection_Internal *ptagConnections;
WSADATA tagWsa;
struct sockaddr_in tagServerInfo;
TagThreadControl tagThreadGetNewConnections;
int iMaxBacklog;
int iMaxConnections;
int iConnectionCount;
}TagServer;
Anyway, atm I'm focussing on just bringing a single one to run, and I occured a problem with using the function _beginthreadex. When I call it, it returns a valid handle (!NULL) and crashes internally when starting the thread. A new Server is created using the following function:
UINT uiTCPServer_Init_g(unsigned short usDestinationPort,
int iMaxReceiveLengthPerMessage,
int iMaxConnectionsCount,
int iMaxBacklogCount)
{
UINT uiNewID;
int iIndex;
WSADATA tagWsa;
/* First Server */
if(iServerCount_m==0)
{
if(!bIDGenerator_Init_g(100))
{
printf("bIDGenerator_Init_g(): failed\n");
return(INVALID_SERVER_HANDLE);
}
if(!bIDGenerator_GetNewID_g(&uiNewID))
{
printf("bIDGenerator_GetNewID_g(): failed\n");
return(INVALID_SERVER_HANDLE);
}
printf("Got ID: %d\n",uiNewID);
if(!(ptagServer_m=malloc(sizeof(TagServer))))
return(INVALID_SERVER_HANDLE);
if(!(ptagServer_m->ptagConnections=malloc(sizeof(TagConnection_Internal)*iMaxConnectionsCount)))
{
return(INVALID_SERVER_HANDLE);
}
/* Winsock DLL initislisieren */
if(WSAStartup(MAKEWORD(2,2),&tagWsa)!= 0)
{
printf("Init failed with error: %d\n",WSAGetLastError());
return(INVALID_SERVER_HANDLE);
}
printf("Initialised\n");
}
else
{
//TODO: Implement list, for multiple servers
printf("**NOT IMPLEMENTED!**\n");
return(INVALID_SERVER_HANDLE);
}
for(iIndex=0;iIndex<iMaxConnectionsCount;++iIndex)
ptagServer_m[iServerCount_m].ptagConnections[iIndex].iMaxReceiveLengthPerMessage=iMaxReceiveLengthPerMessage;
/* Create Socket for listening.. */
if((ptagServer_m[iServerCount_m].sSocket = socket(AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET)
{
printf("uiTCPServer_Init_g(): Failed to create Socket: %d\n",WSAGetLastError());
if(iServerCount_m==0)
WSACleanup();
return(INVALID_SERVER_HANDLE);
}
printf("Socket created\n"); // TODO: remove this
ptagServer_m[iServerCount_m].tagServerInfo.sin_family = AF_INET;
ptagServer_m[iServerCount_m].tagServerInfo.sin_addr.s_addr = INADDR_ANY;
ptagServer_m[iServerCount_m].tagServerInfo.sin_port = htons(usDestinationPort);
ptagServer_m[iServerCount_m].iMaxBacklog=iMaxBacklogCount;
ptagServer_m[iServerCount_m].iMaxConnections=iMaxConnectionsCount;
/* Socket binden */
if(bind(ptagServer_m[iServerCount_m].sSocket,
(struct sockaddr*)&ptagServer_m[iServerCount_m].tagServerInfo,
sizeof(ptagServer_m[iServerCount_m].tagServerInfo)) == SOCKET_ERROR)
{
printf("Bind failed: %d\n",WSAGetLastError());
if(iServerCount_m==0)
WSACleanup();
return(INVALID_SERVER_HANDLE);
}
printf("Bind done\n"); // TODO: remove this
ptagServer_m[iServerCount_m].uiID=uiNewID;
++iServerCount_m;
return(uiNewID);
Here's the Function called after the creation, which is crashing on calling _beginthreadex:
BOOL bTCPServer_StartListening_g(UINT uiServerID)
{
UINT uiThreadID;
TagServer *ptagCurrServer=ptagGetServerByID_m(uiServerID);
if(!ptagCurrServer)
return(FALSE);
/* Start listen on socket */
if(listen(ptagCurrServer->sSocket,
ptagCurrServer->iMaxBacklog) == SOCKET_ERROR)
{
printf("listen(): failed\n");
return(FALSE);
}
printf("listen on socket started...\n"); // TODO: remove this
ptagCurrServer->tagThreadGetNewConnections.bExitThread=FALSE;
if(!(ptagCurrServer->tagThreadGetNewConnections.hThread=(HANDLE)_beginthreadex(NULL,
0,
&Thread_GetNewConnections_m,
(void*)ptagCurrServer,
0,
&uiThreadID)))
{
printf("_beginthreadex(): failed starting Thread_GetNewConnections_m");
return(FALSE);
}
printf("_beginthreadex() returned valid handle\n"); // TODO: remove this
Sleep(2000); //Without that Sleep, returns successful and crashes after
printf("started listening thread\n"); // TODO: remove this
return(TRUE);
}
The Function called in the new Thread looks as follows:
unsigned __stdcall Thread_GetNewConnections_m(void *pvParam)
{
TagServer *ptagCurrServer=(TagServer*)pvParam;
struct sockaddr_in tagClientInfo;
int iSocketSize=sizeof(struct sockaddr_in);
int iFreeConnectionIndex;
UINT uiThreadID;
if(!ptagCurrServer)
{
printf("Thread_GetNewConnections_m(): Terminated in an unusual way\n");
return(0);
}
printf("started Thread_GetNewConnections_m()...\n"); // TODO: remove this
memset(ptagCurrServer->ptagConnections,0,sizeof(TagConnection_Internal)*ptagCurrServer->iMaxConnections);
while(!ptagCurrServer->tagThreadGetNewConnections.bExitThread)
{
printf("Getnewcons...\n"); // TODO: remove this
vCleanUpDeadConnections_m(ptagCurrServer);
// TODO: clean up dead connections here...
if(ptagCurrServer->iConnectionCount >= ptagCurrServer->iMaxConnections)
{
printf("Max Connections reached\n");
Sleep(100);
continue;
}
if((iFreeConnectionIndex=iTCPServer_GetNextFreeConnectionIndex_m(ptagCurrServer->ptagConnections,ptagCurrServer->iMaxConnections)) <0)
{
printf("iTCPServer_GetNextFreeConnectionIndex_m(): failed (Should not happen)\n");
Sleep(100);
continue;
}
printf("next free connection index: %d\n",iFreeConnectionIndex);
if((ptagCurrServer->ptagConnections[iFreeConnectionIndex].sSocket=accept(ptagCurrServer->sSocket,(struct sockaddr*)&tagClientInfo,&iSocketSize)) == INVALID_SOCKET)
{
printf("accept(): failed\n"); // TODO: Don't return, handle error somehow
Sleep(10);
continue;
}
printf("New connection established\n");
++ptagCurrServer->iConnectionCount;
/* Store Data from client */
memcpy(&ptagCurrServer->ptagConnections[iFreeConnectionIndex].tagIPAddress,&tagClientInfo.sin_addr,sizeof(struct in_addr));
ptagCurrServer->ptagConnections[iFreeConnectionIndex].usPort=ntohs(tagClientInfo.sin_port); //Convert form Network to host short
/* Start Thread for new Connection */
if((ptagCurrServer->ptagConnections[iFreeConnectionIndex].tagThread.hThread=(HANDLE)_beginthreadex(NULL,
0,
Thread_ConnectionHandler_m,
&ptagCurrServer->ptagConnections[iFreeConnectionIndex],
0,
&uiThreadID))==NULL)
{
printf("_beginthreadex(): failed to start Thread for new connection\n");
--ptagCurrServer->iConnectionCount;
closesocket(ptagCurrServer->ptagConnections[iFreeConnectionIndex].sSocket);
Sleep(100);
continue;
}
Sleep(10);
}
return(0);
}
Funny thing is, whenever I comment the whole code (excluding the return(0)) in this function and just leave a printf() there, the programm still crashes, without even entering the function. I've included the following header files, and some created myself:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <process.h>
#include <windows.h>
#include <winsock2.h>
If I try to run my compiled programm, the Trace looks as follows:
Curr index 0 (0), used: FALSE
Got ID: 1
Initialised
Socket created
Bind done
listen on socket started...
_beginthreadex() returned valid handle
Are there any additional compiler flags/header files, ... needed using Threads?
If more information is needed, just let me know.
Regards, XXXBold
I solved the problem, another function with wrong call of malloc() corrupted the memory, could solve the problem using Dr Memory
Here is the threaded-server code in C. My question is: do we need to set unused thread to NULL? In java, we need to set thread to NULL to let it return to thread pool.
I made the change to Martin Broadhurst's source code (see gray text as comment)
/*
* A threaded server
* by Martin Broadhurst (www.martinbroadhurst.com)
* Compile with -pthread
*/
#include <stdio.h>
#include <string.h> /* memset() */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <pthread.h>
#define PORT "32001" /* Port to listen on */
#define BACKLOG 10 /* Passed to listen() */
void *handle(void *pnewsock)
{
/* send(), recv(), close() */
return NULL;
}
int main(void)
{
int sock;
pthread_t thread;
struct addrinfo hints, *res;
int reuseaddr = 1; /* True */
/* Get the address info */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(NULL, PORT, &hints, &res) != 0) {
perror("getaddrinfo");
return 1;
}
/* Create the socket */
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock == -1) {
perror("socket");
return 1;
}
/* Enable the socket to reuse the address */
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1) {
perror("setsockopt");
return 1;
}
/* Bind to the address */
if (bind(sock, res->ai_addr, res->ai_addrlen) == -1) {
perror("bind");
return 0;
}
freeaddrinfo(res);
/* Listen */
if (listen(sock, BACKLOG) == -1) {
perror("listen");
return 0;
}
/* Main loop */
while (1) {
pthread_attr_t *attr; //<===I added this
size_t size = sizeof(struct sockaddr_in);
struct sockaddr_in their_addr;
int * ptr; //<===I added this
ptr = malloc(sizeof(int)); //<===I added this
ptr = accept(sock, (struct sockaddr*)&their_addr, &size);
if (newsock == -1) {
perror("accept");
}
else {
printf("Got a connection from %s on port %d\n",
inet_ntoa(their_addr.sin_addr), htons(their_addr.sin_port));
//I added the following "if" statement
if (pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED) != 0){
fprintf(stderr, "Failed to set thread detached\n");
}
else {
//if (pthread_create(&thread, NULL, handle, &newsock) != 0) {
if (pthread_create(&thread, attr, handle, ptr) != 0 ) {
fprintf(stderr, "Failed to create thread\n");
}
}
}
}
close(sock);
return 0;
}
==========-==============
code is from here:
http://martinbroadhurst.com/source/threaded-server.c.html
No. Well, it's not 100% clear what Java construct it is you're thinking of (I bet there's a close method you can call instead of setting it to null and having the GC take care of it), but that's irrelevant because...
pthread_t is an integer (maybe) type, not a pointer, so it can't be set to NULL.
C is not garbage collected, so even if it were a pointer the thread would have no way of knowing or caring that you set it to null.
POSIX threads does not use a thread pool. The pthread_create function actually creates a brand-new OS-level thread, and returning from the handler actually exits it. (Well, not really. It still hangs around until you call pthread_join, since you didn't create it as a detached thread.)
What you should do is create the threads as detached threads, since your code right now is leaking joinable threads.
Also, using &newsock as the argument is dangerous, since it gets destroyed and recreated in every iteration of the main loop. This is a race condition that probably never showed up in the author's testing because under light load the main thread would be waiting for the next accept to return while it is accessed on the worker thread, and on most systems the same space will be used over and over for the variable.
You can use malloc to create a place to store the socket fd in (which you will need to free at the end of your handler function), or, if your platform allows this (most do), just cast the value to a pointer then cast it back out in the handler function.
Since C doesn't have objects, there is no object that represents the thread and so nothing to set to NULL. A detached thread will go away when it terminates. An undetached thread will go away when it's joined.
You have a pthread_t as thread id, there is no object, and it don't work in the way as java gc.
Thread terminate in 1 of following cases:
its start function return,
the thread call pthread_exit()
canceled by pthread_cancel()
any of threads in the process call exit(),
the main thread returns,
in this case, all threads in the process will terminate immediately,
I'm writing a web server in C (which I suck with) using Pthreads (which I suck with even more) and I'm stuck at this point. The model for the server is boss-worker so the boss thread instantiates all worker threads at the beginning of the program. There is a global queue that stores the socket of the incoming connection(s). The boss thread is the one that adds all items (sockets) to the queue as the connections are accepted. All of the worker threads then wait for an item to be added to a global queue in order for them to take up the processing.
The server works fine as long as I connect to it less times than the number of worker threads that the server has. Because of that, I think that either something is wrong with my mutexes (maybe the signals are getting lost?) or the threads are being disabled after they run once (which would explain why if there are 8 threads, it can only parse the first 8 http requests).
Here is my global queue variable.
int queue[QUEUE_SIZE];
This is the main thread. It creates a queue struct (defined elsewhere) with methods enqueue, dequeue, empty, etc. When the server accepts a connection, it enqueues the socket that the incoming connection is on. The worker threads which were dispatched at the beginning are constantly checking this queue to see if any jobs have been added, and if there are jobs, then they dequeue the socket, connect to that port, and read/parse/write the incoming http request.
int main(int argc, char* argv[])
{
int hSocket, hServerSocket; /* handle to socket */
struct hostent* pHostInfo; /* holds info about a machine */
struct sockaddr_in Address; /* Internet socket address stuct */
int nAddressSize = sizeof(struct sockaddr_in);
int nHostPort;
int numThreads;
int i;
init(&head,&tail);
//**********************************************
//ALL OF THIS JUST SETS UP SERVER (ADDR STRUCT,PORT,HOST INFO, ETC)
if(argc < 3) {
printf("\nserver-usage port-num num-thread\n");
return 0;
}
else {
nHostPort=atoi(argv[1]);
numThreads=atoi(argv[2]);
}
printf("\nStarting server");
printf("\nMaking socket");
/* make a socket */
hServerSocket=socket(AF_INET,SOCK_STREAM,0);
if(hServerSocket == SOCKET_ERROR)
{
printf("\nCould not make a socket\n");
return 0;
}
/* fill address struct */
Address.sin_addr.s_addr = INADDR_ANY;
Address.sin_port = htons(nHostPort);
Address.sin_family = AF_INET;
printf("\nBinding to port %d\n",nHostPort);
/* bind to a port */
if(bind(hServerSocket,(struct sockaddr*)&Address,sizeof(Address)) == SOCKET_ERROR) {
printf("\nCould not connect to host\n");
return 0;
}
/* get port number */
getsockname(hServerSocket, (struct sockaddr *) &Address,(socklen_t *)&nAddressSize);
printf("Opened socket as fd (%d) on port (%d) for stream i/o\n",hServerSocket, ntohs(Address.sin_port));
printf("Server\n\
sin_family = %d\n\
sin_addr.s_addr = %d\n\
sin_port = %d\n"
, Address.sin_family
, Address.sin_addr.s_addr
, ntohs(Address.sin_port)
);
//Up to this point is boring server set up stuff. I need help below this.
//**********************************************
//instantiate all threads
pthread_t tid[numThreads];
for(i = 0; i < numThreads; i++) {
pthread_create(&tid[i],NULL,worker,NULL);
}
printf("\nMaking a listen queue of %d elements",QUEUE_SIZE);
/* establish listen queue */
if(listen(hServerSocket,QUEUE_SIZE) == SOCKET_ERROR) {
printf("\nCould not listen\n");
return 0;
}
while(1) {
pthread_mutex_lock(&mtx);
printf("\nWaiting for a connection");
while(!empty(head,tail)) {
pthread_cond_wait (&cond2, &mtx);
}
/* get the connected socket */
hSocket = accept(hServerSocket,(struct sockaddr*)&Address,(socklen_t *)&nAddressSize);
printf("\nGot a connection");
enqueue(queue,&tail,hSocket);
pthread_mutex_unlock(&mtx);
pthread_cond_signal(&cond); // wake worker thread
}
}
Here is the worker thread. This should be always running checking for new requests (by seeing if the queue is not empty). At the end of this method, it should be deferring back to the boss thread to wait for the next time it is needed.
void *worker(void *threadarg) {
pthread_mutex_lock(&mtx);
while(empty(head,tail)) {
pthread_cond_wait(&cond, &mtx);
}
int hSocket = dequeue(queue,&head);
unsigned nSendAmount, nRecvAmount;
char line[BUFFER_SIZE];
nRecvAmount = read(hSocket,line,sizeof line);
printf("\nReceived %s from client\n",line);
//***********************************************
//DO ALL HTTP PARSING (Removed for the sake of space; I can add it back if needed)
//***********************************************
nSendAmount = write(hSocket,allText,sizeof(allText));
if(nSendAmount != -1) {
totalBytesSent = totalBytesSent + nSendAmount;
}
printf("\nSending result: \"%s\" back to client\n",allText);
printf("\nClosing the socket");
/* close socket */
if(close(hSocket) == SOCKET_ERROR) {
printf("\nCould not close socket\n");
return 0;
}
pthread_mutex_unlock(&mtx);
pthread_cond_signal(&cond2);
}
Any help would be greatly appreciated. I can post more of the code if anyone needs it, just let me know. I'm not the best with OS stuff, especially in C, but I know the basics of mutexes, cond. variables, semaphores, etc. Like I said, I'll take all the help I can get. (Also, I'm not sure if I posted the code exactly right since this is my first question. Let me know if I should change the formatting at all to make it more readable.)
Thanks!
Time for a workers' revolution.
The work threads seem to be missing a while(true) loop. After the HTTP exchange and closing the socket, they should be looping back to wait on the queue for more sockets/requests.
I am working on a school project where we have to make a multithreaded web server. I am having a problem where when I call sem_wait on my semaphore (which should be initialized to 0 but already seems to be sem_post()ed to 1). I get a SIGABRT.
I am attaching my code below, and I put a comment on the line that is causing my problem. I've spent a few hours with the debugger with little luck.
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string>
#include <string.h>
#include <iostream>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <vector>
#include <semaphore.h>
#include <stdio.h>
#include <cstdlib>
#include <strings.h>
#define PORTNUM 7000
#define NUM_OF_THREADS 5
#define oops(msg) { perror(msg); exit(1);}
#define FCFS 0
#define SJF 1;
void bindAndListen();
void acceptConnection(int socket_file_descriptor);
void* dispatchJobs(void*);
void* replyToClient(void* pos);
//holds ids of worker threads
pthread_t threads[NUM_OF_THREADS];
//mutex variable for sleep_signal_cond
pthread_mutex_t sleep_signal_mutex[NUM_OF_THREADS];
//holds the condition variables to signal when the thread should be unblocked
pthread_cond_t sleep_signal_cond[NUM_OF_THREADS];
//mutex for accessing sleeping_thread_list
pthread_mutex_t sleeping_threads_mutex = PTHREAD_MUTEX_INITIALIZER;
//list of which threads are sleeping so they can be signaled and given a job
std::vector<bool> *sleeping_threads_list = new std::vector<bool>();
//number of threads ready for jobs
sem_t available_threads;
sem_t waiting_jobs;
//holds requests waiting to be given to one of the threads for execution
//request implemented as int[3] with int[0]== socket_descriptor int[1]== file_size int[2]== file_descriptor of requested file
//if file_size == 0 then HEAD request
std::vector<std::vector<int> >* jobs = new std::vector<std::vector<int> >();
pthread_mutex_t jobs_mutex = PTHREAD_MUTEX_INITIALIZER;
int main (int argc, char * const argv[]) {
//holds id for thread responsible for removing jobs from ready queue and assigning them to worker thread
pthread_t dispatcher_thread;
//initializes semaphores
if(sem_init(&available_threads, 0, NUM_OF_THREADS) != 0){
oops("Error Initializing Semaphore");
}
if(sem_init(&waiting_jobs, 0, 0) !=0){
oops("Error Initializing Semaphore");
}
//initializes condition variables and guarding mutexes
for(int i=0; i<NUM_OF_THREADS; i++){
pthread_cond_init(&sleep_signal_cond[i], NULL);
pthread_mutex_init(&sleep_signal_mutex[i], NULL);
}
if(pthread_create(&dispatcher_thread, NULL, dispatchJobs, (void*)NULL) !=0){
oops("Error Creating Distributer Thread");
}
for (int i=0; i<NUM_OF_THREADS; i++) {
pthread_mutex_lock(&sleeping_threads_mutex);
printf("before");
sleeping_threads_list->push_back(true);
printf("after");
pthread_mutex_unlock(&sleeping_threads_mutex);
}
printf("here");
for (int i=0; i<NUM_OF_THREADS; i++) {
//creates threads and stores ID in threads
if(pthread_create(&threads[i], NULL, replyToClient, (void*)i) !=0){
oops("Error Creating Thread");
}
}
/*
if(sem_init(&available_threads, 0, NUM_OF_THREADS) !=0){
oops("Error Initializing Semaphore");
}
if(sem_init(&waiting_jobs, 0, 0) !=0){ //this is the semaphore thats used in the sem_wait
oops("Error Initializing Semaphore");
}*/
bindAndListen();
}
//binds to socket and listens for connections
//being done by main thead
void bindAndListen(){
struct sockaddr_in saddr;
struct hostent *hp;
char hostname[256];
int sock_id, sock_fd;
gethostname(hostname, 256);
hp = gethostbyname(hostname);
bzero(&saddr, sizeof(saddr));
//errno = 0;
bcopy(hp->h_addr, &saddr.sin_addr, hp->h_length);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORTNUM);
saddr.sin_addr.s_addr = INADDR_ANY;
sock_id = socket(AF_INET, SOCK_STREAM, 0);
if(sock_id == -1){
oops("socket");
printf("socket");
}
if(bind(sock_id, (const sockaddr*)&saddr, sizeof(saddr)) ==0){
if(listen(sock_id, 5) ==-1){
oops("listen");
}
//each time a new connection is accepted, get file info and push to ready queue
while(1){
int addrlen = sizeof(saddr);
sock_fd = accept(sock_id, (sockaddr*)&saddr, (socklen_t*)&addrlen);
if (sock_fd > 0) {
acceptConnection(sock_fd);
}else {
oops("Error Accepting Connection");
}
}
}else{
oops("there was an error binding to socket");
}
}// end of bindAndListen()
//accepts connection and gets file info of requested file
//being done by main thread
void acceptConnection(int sock_fd){
printf("**Server: A new client connected!");
//only using loop so on error we can break out on error
while(true){
//used to hold input from client
char* inputBuff = new char[BUFSIZ];
int slen = read(sock_fd, inputBuff, BUFSIZ);
//will sit on space between HEAD/GET and path
int pos1 = 0;
//will sit on space between path and HTTP version
int pos2 = 0;
//need duplicate ptr so we can manipulate one in the loop
char* buffPtr = inputBuff;
//parses client input breaks up query by spaces
for(int i=0; i<slen; i++){
if(*buffPtr == ' '){
if (pos1 == 0) {
pos1 = i;
}else {
pos2 = i;
break;
}
}
buffPtr++;
}
if((pos1 - pos2) >=0){
std::string str = "Invalid Query";
write(sock_fd, str.c_str(), strlen(str.c_str()));
break;
}
printf("slen length %d\n", slen);
std::string* method = new std::string(inputBuff, pos1);
printf("method length %lu\n",method->length());
//increment the ptr for buff to the starting pos of the path
inputBuff += ++pos1;
printf("pos2 - pos1 %d\n", (pos2 - pos1));
printf("pos1 = %d pos2 = %d\n", pos1, pos2);
std::string* path = new std::string(inputBuff, (pos2 - pos1));
printf("path length %lu\n", path->length());
printf("part1 %s\n", method->c_str());
printf("part2 %s\n", path->c_str());
//opens file requested by client
int fd = open(path->c_str(), O_RDONLY);
if(fd < 0){
std::string* error = new std::string("Error Opening File");
*error += *path + std::string(strerror(errno), strlen(strerror(errno)));
write(sock_fd, error->c_str(), strlen(error->c_str()));
break;
}
int file_size;
if(method->compare("GET") == 0){
//gets file info and puts the resulting struct in file_info
struct stat file_info;
if(fstat(fd, &file_info) !=0){
oops("Error getting file info");
}
file_size = file_info.st_size;
}else if(method->compare("HEAD")){
file_size = 0;
}else{
write(sock_fd, "Invalid Query", strlen("Invalid Query"));
break;
}
//job to be pushed to ready queue
std::vector<int> job;
job.push_back(sock_fd);
job.push_back(file_size);
job.push_back(fd);
//check mutex guarding the ready queue
pthread_mutex_lock(&jobs_mutex);
//push job to back of ready queue
jobs->push_back(job);
//unlock mutex guarding the ready queue
pthread_mutex_unlock(&jobs_mutex);
//increment number of jobs in ready queue
sem_post(&waiting_jobs);
} //end of while(true)
// we only end up here if there was an error
fflush(stdout);
close(sock_fd);
}// end of acceptConnection()
//routine run by dispather thread
void *dispatchJobs(void*){
while(true){
//wait for a thread to be available to execute a job
sem_wait(&available_threads);
//wait for a job to be waiting in the ready queue
sem_wait(&waiting_jobs); //this is the line thats crashing
//aquire lock to check which threads are waiting
pthread_mutex_lock(&sleeping_threads_mutex);
//go through list of threads to see which is waiting
for(int i=0; i<sleeping_threads_list->size(); i++){
if(sleeping_threads_list->at(i)){
//unlocks lock for access to list of waiting threads
pthread_mutex_unlock(&sleeping_threads_mutex);
//allows us access to the list of condition variables to signal the thread to resume execution
pthread_mutex_lock(&sleep_signal_mutex[i]);
pthread_cond_signal(&sleep_signal_cond[i]);
pthread_mutex_unlock(&sleep_signal_mutex[i]);
}
}
}//end of while(true)
}//end of dispatchJobs()
//sends file or metadata to client
//run by worker thread
//pos is position of condition variable that it waits to be signaled in the sleep_signal_cond[] array
void* replyToClient(void* pos){
int position = (long)pos;
while(true){
//waits for dispather thread to signal it
pthread_mutex_lock(&sleep_signal_mutex[position]);
pthread_cond_wait(&sleep_signal_cond[position], &sleep_signal_mutex[position]);
pthread_mutex_unlock(&sleep_signal_mutex[position]);
//lock mutex to get job to be executed
pthread_mutex_lock(&jobs_mutex);
std::vector<int> job = jobs->front();
//removes job from front of vector
jobs->erase(jobs->begin());
//releases mutex
pthread_mutex_unlock(&jobs_mutex);
//socket file descriptor, used for writing to socket
int sock_fd =job[0];
int file_size = job[1];
//file descriptor for requested job
int fd = job[2];
//holds output to be written to socket
char* outputBuffer = new char[BUFSIZ];
//GET request, send file
if(file_size !=0){
int readResult = 0;
while ((readResult = read(fd, outputBuffer, BUFSIZ)) > 0) {
if(write(sock_fd, outputBuffer, readResult) != readResult){
printf("We may have a write error");
}
}
if(readResult < 0){
oops("Error Reading File");
}
if(readResult == 0){
printf("finished sending file");
}
}else{ // HEAD request
}
//increment number of available threads
sem_post(&available_threads);
}
}// end of replyToClient()
Check again the whole logic of the code - it is possible to reach here:
pthread_mutex_lock(&jobs_mutex);
std::vector<int> job = jobs->front();
//removes job from front of vector
jobs->erase(jobs->begin());
//releases mutex
pthread_mutex_unlock(&jobs_mutex);
with jobs->size () == 0, in which case front() and erase() invoke undefined behavior, which may well result in the effects you observe.
Check whether your program still crashes after the following change:
//lock mutex to get job to be executed
pthread_mutex_lock(&jobs_mutex);
if (jobs->size () == 0)
{
pthread_mutex_unlock (&jobs_mutex);
continue;
}
std::vector<int> job = jobs->front();
//removes job from front of vector
jobs->erase(jobs->begin());
//releases mutex
pthread_mutex_unlock(&jobs_mutex);
I haven't used POSIX semaphores, but I believe this is what is happening. I'm only familiar with Linux kernel semaphores, and you don't mention your system. The init function's 3rd parameter probably sets the count variable. You set it to 0 (= busy but no other processes waiting). The wait function probably invokes down(), which begins by decreasing the count variable by 1: to -1, which means the semaphore you mean to use is locked now. There is nothing in your program to ever unlock it I believe (from browsing your code - it's pretty long), so you are in trouble. Try setting it to 1 in init. This might be all that is needed.
this is a simple program I've been working on that listens to a socket, and starts a new thread to handle each connection to said socket.
In my while loop I get a Segmentation Fault, that has something to do with pthread_create (if I comment that line out the program loops properly). My knowledge of pointers is mediocre at best, and debugging with gdb didn't yield anything of value. This is gdb's output:
#0 0x0000000000000000 in ?? ()
#1 0x000000080064f4f1 in pthread_getprio () from /lib/libthr.so.3
#2 0x0000000000000000 in ?? ()
Error accessing memory address 0x7fffffbff000: Bad address.
The program gets through the while loop once successfully, and properly receives and responds to a connection at the socket, but then before getting into the second while loop, the program fails on a Segmentation Fault error.
Here's a condensed version of my program:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <pthread.h>
#define UNIX_PATH_MAX 100
#define SOCK_PATH "/tmp/demo_socket"
/*===============> CONNECTION HANDLER FUNCTION <=================*/
void *connection_handler(int connection_fd)
{
int nbytes;
char buffer[256];
nbytes = read(connection_fd, buffer, 256);
buffer[nbytes] = 0;
printf("\tMESSAGE FROM CLIENT: %s\n", buffer);
nbytes = snprintf(buffer, 256, "Hello from the server!");
write(connection_fd, buffer, nbytes);
close(connection_fd);
return;
}
/*==========================> MAIN <=============================*/
int main(void)
{
struct sockaddr_un addr; //socket address information
int sock_fd, conn_fd; //socket file descriptors
socklen_t addr_len = sizeof(struct sockaddr_un); //size of sockaddr_un structure
pid_t child_pid; //pid holder
pthread_t thread; // thread identifier
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0)
return 1;
unlink(SOCK_PATH);
memset(&addr, 0, addr_len);
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path) - 1); // Copies up to sizeof(addr.sun_path)-1 bytes from SOCK_PATH into addr.sun_path
printf("> Socket sun_family = %d (AF_UNIX), Socket sun_path = %s ...\n", addr.sun_family, addr.sun_path);
/*----------------------FAIL CHECKS-------------------------*/
if (bind(sock_fd, (struct sockaddr *) &addr, addr_len) != 0)
return 1;
if (listen(sock_fd, 5) != 0)
return 1;
printf("> Listening to socket bound at %s ...\n\n", SOCK_PATH);
/*--------------------WHILE LOOP----------------------------*/
while ( (conn_fd = accept(sock_fd, (struct sockaddr *) &addr, &addr_len)) > -1) {
pthread_create(&thread , NULL, connection_handler(conn_fd), NULL);
printf("> Closing connection at %d inside server process ...\n", conn_fd);
close(conn_fd);
printf("> Reached bottom of loop!\n");
}
/*---------------------------FIN------------------------------*/
close(sock_fd);
unlink(SOCK_PATH);
printf("> Socket closed and unlinked from path ... Done!\n ");
return 0;
}
Any help would be greatly appreciated!
This is wrong:
pthread_create(&thread , NULL, connection_handler(conn_fd), NULL);
pthread_create requires the address of the function to run in the new thread. What your code does is call connection_handler in the main thread and then pass the result of connection_handler to pthread_create as the function address.
What you need is the following:
pthread_create(&thread , NULL, connection_handler, (void*)conn_fd);
You'll also need to change connection_handler to take void* instead of int:
void *connection_handler(void* arg)
{
intptr_t connection_fd = (intptr_t)arg;
...
}
Your usage of pthread_create is incorrect. the third argument should be a pointer to a function of type void *(*start_routine) (void *), instead you are passing the return of connection_handler.
Change connection_handler to receive a void * argument (and make sure it returns an actual value), eg.
#include <stdint.h>
void *connection_handler(void *arg)
{
intptr_t connection_fd = (intptr_t)arg;
...
return NULL;
}
and change your call to something like the following
pthread_create(&thread, NULL, &connection_handler, (void *)conn_fd);
You should also make sure to either start the thread detached, detach the thread with pthread_detach or join it later with pthread_join
buffer[nbytes] = 0;
This will overflow if you've read 256 bytes. Increase buffer size or decrease read size by one.
Most likely because you are closing the socket connection in two places. There is good chance that in one run of the thread its not yet got around to the write but in your parent thread already closed the connection.
Why do you need to create so many threads? Wouldn't one worker thread suffice? you can pile up the jobs on to this worker thread...