Win32 multithreaded sockets - multithreading

I have a server class that has a method called handle_client as follows:
void server::handle_client()
{
do {
// Accept a client socket
EnterCriticalSection(&listenSocketCriticalSection);
SOCKET clientSocket = accept(listenSocket, NULL, NULL);
LeaveCriticalSection(&listenSocketCriticalSection);
// ... rest of the client handling code that reads
// from the client socket and sends appropriate response
// ...
} while(true);
}
I have a run method as follows:
void server::run()
{
// create the threads
for (int i = 0; i < THREAD_POOL_SIZE; i++) {
DWORD dwThreadId;
thread_pool_handle[i] = CreateThread(NULL, 0, thread_function, this, 0, &dwThreadId);
}
WaitForMultipleObjects(THREAD_POOL_SIZE, thread_pool_handle, true, INFINITE);
}
I have a thread_function as follows:
DWORD WINAPI thread_function(LPVOID lpParam)
{
server* pServer = (server*)lpParam;
pServer->handle_client();
}
I am creating a pool of threads that are all waiting for a client socket connection to be accepted. Since I have wrapped the accept within a critical section, only one thread will succeed at a time. Once a client socket is accepted by the server thread, that thread continues to go on to handle the request. The idea is that the thread will loop back indefinitely to the accept call after completing a request.
Questions:
Is the Critical Section necessary? (I think so, because otherwise the accept call from the multiple threads on the same listenSocket would clobber things. Is this correct?)
If handle_client loops indefinitely, what is the best way to cleanly terminate all the threads and exit the server? Should I use a special message from the client to trigger the thread terminations? Any other suggestions?
How should I handle the server process termination gracefully (as it pertains to the thread pool)?

It Is recommended to use Select model to store socket objects in multithreaded sockets. In Select model, you can use FD_CLR() to clear sockets when there are no network events.
I have the code for the server with the select socket, You can try to run and modify.
#include <iostream>
#include<WinSock2.h>
#include<windows.h>
#include<WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)//inet_addr
int main()
{
//1.Obtain version info
WSADATA wsaData = { 0 };
SOCKET hServer = { 0 };
WSAStartup(MAKEWORD(2, 2), &wsaData);
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("version failed %d\n", GetLastError());
return -1;
}
else
{
printf("version succeed \n");
}
//2.create socket
hServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (hServer == SOCKET_ERROR)
{
printf("create socket tcp failed %d\n", GetLastError());
return -1;
}
else
{
printf("create socket tcp succeed \n");
}
//3. Create a protocol address family
sockaddr_in ServerAddr = { 0 };
ServerAddr.sin_family = AF_INET6;
ServerAddr.sin_zero[8];
ServerAddr.sin_port = htons(8888);
ServerAddr.sin_addr.S_un.S_addr = inet_addr("192.168.2.50");;//modify your address
//4.bind
int nRet = bind(hServer, (sockaddr*)&ServerAddr, sizeof ServerAddr);
if (nRet == SOCKET_ERROR)
{
printf("bind failed %d\n", GetLastError());
closesocket(hServer);
WSACleanup();
return -1;
}
else
{
printf("bind succeed \n");
}
//5.listen
nRet = listen(hServer, 3);
if (nRet == SOCKET_ERROR)
{
printf("listen failed %d\n", GetLastError());
closesocket(hServer);
WSACleanup();
return -1;
}
else
{
printf("listen succeed \n");
}
sockaddr_in clientAddr = { 0 };// The protocol address family used to receive the client
int len = sizeof(clientAddr);// The size of the accepted client protocol address family information
// Create a select model to store socket objects
FD_SET fd_read;
FD_ZERO(&fd_read);
FD_SET(hServer, &fd_read);
//6. Accept client connections
while (1) {
FD_SET fd_tmp = fd_read;// Read backup can only be in
const timeval tv = { 1,0 };
int Ret = select(NULL, &fd_tmp, NULL, NULL, &tv);
if (Ret == 0) // No network events, TMP is automatically deleted
{
Sleep(1000);
continue;
}
for (int i = 0; i < fd_tmp.fd_count; i++)
{
// If there are network events for a listening socket, it proves that a client is connecting to the socket
if (fd_tmp.fd_array[i] == hServer)
{
SOCKET hclient;
hclient = accept(hServer, (sockaddr*)&clientAddr, &len);// If you do not want to store the protocol address family information of the client, you can pass a NULL address
if (hclient == SOCKET_ERROR)
{
printf("recieve information of client failed %d\n", GetLastError());
closesocket(hServer);
return -1;
}
printf("connecting: %s******** \n", inet_ntoa(clientAddr.sin_addr));
FD_SET(hclient, &fd_read);
}
else // The client socket has network events that prove that the client is sending data and the server is accepting the data
{
char buff[32] = { 0 };
int nRet = recv(fd_tmp.fd_array[i], (char*)buff, 32, NULL);
if (nRet > 0)
{
printf("message: %s\n", buff);
}
else// Removes the current socket fd_array from the fd_read
{
FD_CLR(fd_tmp.fd_array[i], &fd_read);
printf("Disconnect \n", GetLastError());
closesocket(hServer);
closesocket(fd_read.fd_array[i]);
break;
}
}
break;
}
}
//7.close socket
WSACleanup();
getchar();
return 0;
}

Related

Futex Error in C application that is using mutex and epoll

I am running epoll to process network connection. Everything is working perfectly I think.
Now i am trying to connect to the Postgresql Database from the child process. I have 4 child process.
Sometimes when i try to connect to the server. It hangs.
Sometimes i get this error
fserver: tpp.c:84: __pthread_tpp_change_priority: Assertion `new_prio
Would appreciate it if someone would be able to advice on my code. Thanks
If i remove the Postgresql Connection code , the problem disappears.
/******CHILD PROCESS **************/
void childProcess()
{
const char conninfo[] = "postgresql://postgres#localhost?port=5432&dbname=ram&password=xxx";
PGconn *conn = PQconnectdb(conninfo);
const ConnStatusType connStatusType = PQstatus(conn);
if (connStatusType == CONNECTION_BAD) {
printf("pg connection not OK");
if (conn) {
PQfinish(conn);
}
exit(1);
} else {
printf("pg connection OK");
}
int efd = epoll_create1(0);
if(efd == -1)
{
perror("Epoll Creation Failed\n");
}
nonblocksocket(&p2c_var);
struct epoll_event event0;
event0.data.fd = p2c_var;
event0.events = EPOLLIN | EPOLLET;
if(epoll_ctl(efd,EPOLL_CTL_ADD,p2c_var,&event0) == -1)
{
perror("First Failed to add file descriptor to epoll watch list\n");
}
nonblocksocket(&c2p_var);
struct epoll_event event1;
event1.data.fd = c2p_var;
event1.events = EPOLLOUT | EPOLLET;
if(epoll_ctl(efd,EPOLL_CTL_ADD,c2p_var,&event1) == -1)
{
perror("Second Failed to add file descriptor to epoll watch list\n");
}
struct epoll_event *ttlevents;
ttlevents = calloc(MAXEVENTS, sizeof(event0));
int head = 0,tail = 0;
pthread_mutex_t buflock;
pthread_mutex_t fdlock;
int bufhead=0,buftail=0;
struct fifoarr *fifoptr = (struct fifoarr *) calloc(QSIZE,sizeof(struct fifoarr));
int co = 0;
char st[1050];
memset(st,0,sizeof(st));
st[0] = 1;
while(1)
{
int eventcount = epoll_wait(efd,ttlevents,MAXEVENTS,-1);
for(int i =0;i<eventcount;i++)
{
if ((ttlevents[i].events & EPOLLERR) || (ttlevents[i].events & EPOLLHUP))
{
/* An error has occured on this fd, or the socket is not ready for reading (why were we notified then?) */
fprintf (stderr, "epoll error\n");
close (ttlevents[i].data.fd);
continue;
}
if(ttlevents[i].events & EPOLLOUT)
{
if(ttlevents[i].data.fd == c2p_var)
{
pthread_mutex_lock(&buflock);
int bufval = popfd(&bufhead,&buftail);
if(bufval != -1)
{
write(c2p_var,fifoptr[bufval].buffer,sizeof(fifoptr[bufval].buffer));
}
pthread_mutex_unlock(&buflock);
}
}
if(ttlevents[i].events & EPOLLIN)
{
if(ttlevents[i].data.fd == p2c_var)
{
char buffer[1050];
while(1)
{
memset(buffer,0,sizeof(buffer));
int c = read(ttlevents[i].data.fd,buffer,sizeof(buffer));
if(c == -1)
{
if(errno == EAGAIN)
{
printf("Completed Reading From Parent Process\n");
break;
}
}
if(c == 0)
{
printf("Parent terminated Connection.\n");
break;
}
if(buffer[0] != 1)
{
pthread_mutex_lock(&buflock);
int bufval = pushfd(&bufhead);
if(bufval != -1)
{ memset(fifoptr[bufval].buffer,0,sizeof(fifoptr[bufval].buffer)); memcpy(fifoptr[bufval].buffer,buffer,sizeof(buffer));
// processRequest(fifoptr[bufval].buffer,conn);
write(c2p_var,&st,sizeof(st));
}
pthread_mutex_unlock(&buflock);
}//buffer[0]
}//While
}
}//if epollin
} //for
}//while(1)
PQfinish(conn);
}
The program tends to hang.
Well it is working now.
It seems that mutex is the cause of the problem.
Thanks for all that looked at my code.

Socket Programming: Error on listen()

I'm working on a server part of my app and I came across a problem I can't seem to solve. The server initialization function, which is a part of ConnectionManager class, is as follows:
int ConnectionManager::init_server() {
// Log
OutputDebugString(L"> Initializing server...\n");
// Initialize winsock
WSAData wsa;
int code = WSAStartup(MAKEWORD(2, 2), &wsa);
if (code != 0) {
// Error initializing winsock
OutputDebugString(L"> Log: WSAStartup()\n");
output_error(code);
return -1;
}
// Get server information
struct addrinfo hints, *serverinfo, *ptr;
SOCKET sockfd = INVALID_SOCKET;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_protocol = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
if (getaddrinfo(NULL, PORT, &hints, &serverinfo) != 0) {
// Error when getting server address information
OutputDebugString(L"> Log: getaddrinfo()\n");
output_error(WSAGetLastError()); // Call Cleanup?
return -1;
}
for (ptr = serverinfo; ptr != NULL; ptr = ptr->ai_next) {
// Create socket
if ((sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol)) == INVALID_SOCKET) {
// Error when creating a socket
OutputDebugString(L"> Log: socket()\n");
output_error(WSAGetLastError()); // Call Cleanup?
continue;
}
// Set options
const char enable = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) == SOCKET_ERROR) {
// Error when setting options
OutputDebugString(L"> log: setsockopt()\n");
output_error(WSAGetLastError()); // call cleanup?
if (closesocket(sockfd) != 0) {
output_error(WSAGetLastError());
}
return -1;
}
// Bind socket
if (bind(sockfd, ptr->ai_addr, ptr->ai_addrlen) == SOCKET_ERROR) {
// Error on binding
OutputDebugString(L"> Log: bind()\n");
output_error(WSAGetLastError()); // Call Cleanup?
if (closesocket(sockfd) != 0) {
output_error(WSAGetLastError());
}
continue;
}
break;
}
freeaddrinfo(serverinfo);
if (ptr == NULL) {
OutputDebugString(L"Error: Failed to launch server.\n");
return -1;
}
// Listen
if (listen(sockfd, BACKLOG) == SOCKET_ERROR) {
OutputDebugString(L"> Log: listen()\n");
output_error(WSAGetLastError()); // Call Cleanup?;
return -1;
}
// Accept
struct sockaddr_storage clientinfo;
int size = sizeof(struct sockaddr_storage);
m_exchfd = accept(sockfd, (struct sockaddr *)&clientinfo, &size);
if (m_exchfd = INVALID_SOCKET) {
// Error when accepting
OutputDebugString(L"> Log: accept()\n");
output_error(WSAGetLastError()); // Call Cleanup?
if (closesocket(sockfd) != 0) {
output_error(WSAGetLastError());
}
return -1;
}
m_isConnected = true;
return 0;
}
The output_error function simply prints the message corresponding to the error using FormatMessage() function. However, I get the following output:
> Log: listen()
> ERROR: The attempted operation is not supported for the type of object referenced.
Thus, the error should be caused by the call to listen() which is confusing. Could anyone explain me what's the cause of the problem? I bet it should be something easy to fix but I just don't seem to see it.
The root of your problem is when calling getaddrinfo(), you are filling out the hints structure incorrectly.
You are assigning SOCK_STREAM to the ai_protocol field, and leaving the ai_socktype field set to 0. SOCK_STREAM is defined as 1, which is the same value as IPPROTO_ICMP, which is commonly used with SOCK_DGRAM sockets. So, getaddrinfo() is likely returning addrinfo entries whose ai_socktype field is set to SOCK_DGRAM. You can't use listen() on a datagram socket, thus the WSAEOPNOTSUPP error you are seeing:
If no error occurs, listen returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.
...
WSAEOPNOTSUPP
The referenced socket is not of a type that supports the listen operation.
You need to assign SOCK_STREAM to the hints.ai_socktype field instead, and set the hints.ai_protocol field to either 0 or IPPROTO_TCP (preferably the latter).
Also, getaddrinfo() returns an error code, just like WSAStartup() does. Don't use WSAGetLastError() to get its error code.
Aside from that, I see a number of other issues with your code.
SO_REUSEADDR requires a BOOL (4 byte integer), not a char (1 byte). You are passing in a pointer to a single char, but are telling setsockopt() that you are passing in a pointer to an int. setsockopt() will end up trying to read a value from stack memory you don't own.
When your loop calls closesocket(), you should reset sockfd to INVALID_SOCKET as well, and then after the loop you should check for that condition instead of checking ptr for NULL.
You should be calling listen() inside your loop instead of after it. Just because you bind() a socket successfully does not guarantee you can open its assigned listening port. You should keep looping until you actually open a listening port successfully. You might also consider adding an extra log message after the loop to know which local IP/Port pair is actually listening, so you know what clients are then able to connect() to.
When calling WSAGetLastError(), always call it immediately after a failed Winsock call. If you call anything else beforehand, you risk resetting the error code, since WSAGetLastError() is just an alias for GetLastError(), which many APIs use.
When calling accept(), you are using the = assignment operator instead of the == comparison operator when checking if m_exchfd is equal to INVALID_SOCKET. Even after you fix that, if accept() succeeds, you are leaking sockfd since you lose track of it and don't call closesocket() on it. If you are expecting only 1 client to connect, close the listening socket after the client is accepted. Otherwise, store the listening socket in your class and close it after you close the accepted client socket.
With all of that said, try something more like this:
void OutputWinsockError(LPCWSTR funcName, int errCode)
{
std::wostringstream msg;
msg << L"> Log: " << funcName << L"()\n";
OutputDebugStringW(msg.str().c_str());
output_error(errCode);
}
void OutputWinsockError(LPCWSTR funcName)
{
OutputWinsockError(funcName, WSAGetLastError());
}
int ConnectionManager::init_server() {
// Log
OutputDebugString(L"> Initializing server...\n");
// Initialize winsock
WSAData wsa;
int err = WSAStartup(MAKEWORD(2, 2), &wsa);
if (err != 0) {
// Error initializing winsock
OutputWinsockError(L"WSAStartup", err);
return -1;
}
// Get server information
struct addrinfo hints, *serverinfo, *ptr;
SOCKET sockfd = INVALID_SOCKET;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
err = getaddrinfo(NULL, PORT, &hints, &serverinfo);
if (err != 0) {
// Error when getting server address information
OutputWinsockError(L"getaddrinfo", err);
// Call Cleanup?
return -1;
}
for (ptr = serverinfo; ptr != NULL; ptr = ptr->ai_next) {
// Create socket
sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (sockfd == INVALID_SOCKET) {
// Error when creating a socket
OutputWinsockError(L"socket");
continue;
}
// Set options
const BOOL enable = TRUE;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&enable, sizeof(BOOL)) == SOCKET_ERROR) {
// Error when setting options
OutputWinsockError(L"setsockopt");
if (closesocket(sockfd) == SOCKET_ERROR) {
// Error when closing socket
OutputWinsockError(L"closesocket");
}
sockfd = INVALID_SOCKET;
continue;
}
// Bind socket
if (bind(sockfd, ptr->ai_addr, ptr->ai_addrlen) == SOCKET_ERROR) {
// Error on binding
OutputWinsockError(L"bind");
if (closesocket(sockfd) == SOCKET_ERROR) {
// Error when closing socket
OutputWinsockError(L"closesocket");
}
sockfd = INVALID_SOCKET;
continue;
}
// Listen on port
if (listen(sockfd, BACKLOG) == SOCKET_ERROR) {
// Error on listening
OutputWinsockError(L"listen");
if (closesocket(sockfd) == SOCKET_ERROR) {
// Error when closing socket
OutputWinsockError(L"closesocket");
}
sockfd = INVALID_SOCKET;
continue;
}
break;
}
freeaddrinfo(serverinfo);
if (sockfd == INVALID_SOCKET) {
OutputDebugString(L"Error: Failed to launch server.\n");
// Call Cleanup?
return -1;
}
// Accept
struct sockaddr_storage clientinfo;
int size = sizeof(clientinfo);
m_exchfd = accept(sockfd, (struct sockaddr *)&clientinfo, &size);
if (m_exchfd == INVALID_SOCKET) {
// Error when accepting
OutputWinsockError(L"accept");
if (closesocket(sockfd) == SOCKET_ERROR) {
OutputWinsockError(L"closesocket");
}
// Call Cleanup?
return -1;
}
m_isConnected = true;
// is not storing sockfd, close it
// m_listenfd = sockfd;
if (closesocket(sockfd) == SOCKET_ERROR) {
OutputWinsockError(L"closesocket");
}
return 0;
}

Make new connections when blocked in a select() call

I have 2 threads:
Thread A:
It's the select() loop. Perform socket handling for reading operations, such as accepting new connections, receiving data.
while (1) {
FD_ZERO(&fdReadSet);
numActiveSockets = 0;
for (std::unordered_map<SOCKET, TcpSocket*>::iterator it = m_sock_table.begin(); it != m_sock_table.end(); it++)
{
numActiveSockets++;
FD_SET(it->first, &fdReadSet);
}
int ret;
bool hasListen = false;
if (( ret = select(numActiveSockets, &fdReadSet, NULL, NULL, NULL)) == SOCKET_ERROR) {
printf("Select Failed, Error code = %d\n", WSAGetLastError());
return -1;
}
for (std::unordered_map<SOCKET, TcpSocket*>::iterator it = m_sock_table.begin(); it != m_sock_table.end(); it++)
{
if (FD_ISSET(it->first, &fdReadSet))
{
if (it->first == TcpSocket::m_listen_sock)
{
if (!hasListen)
{
sockaddr_in sock_addr;
int sockLength = sizeof(sock_addr);
SOCKET sock = accept(it->first, (sockaddr *) &sock_addr, &sockLength);
TcpSocket * socket = new TcpSocket();
socket->m_sock = sock;
m_sock_table[sock] = socket;
it = m_sock_table.begin();
hasListen = true;
}
}
else
{
char * buffer = it->second->GetWriteBuffer();
int numRead = recv(it->first, buffer, SOCKET_BUFFER_SIZE, 0);
if (numRead == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err == WSAECONNRESET)
{
printf("Connection [%i]: RESET Received. Closing Socket\n", it->first);
closesocket(it->first);
it = socketVector.erase(it->first); // iterator invalidated after erase
}
else
{
printf("Recv Failed. Error code = %d\n", err);
return -1;
}
}
else if (numRead == 0)//connection close
{
printf("Connection [%i]: Graceful exit. Closing Socket\n", it->first);
closesocket(it->first);
it = socketVector.erase(it->first); // iterator invalidated after erase
}
else {
/* Process received data */
}
}
}
}
}
Thread B:
Allow the application to perform connect() to establish new connections. If a connect() is successful, it will the add the returned socket to m_sock_table.
I have a socket table called m_sock_table which holds all the sockets. I use this m_sock_table to initialize the fdReadSet to be used in select().
-----------Problem-----------------
If thread A is blocked by select(), and at the same time thread B establish a new connection through connect(), the application wouldn't be able to receive data from the new connection, because fdReadset has not been updated withed the new connected socket.
What would be a good way to solve this problem? Or the design is just wrong from the start?
You could use a signal that doesn't do anything other than interrupting the system call:
#include <signal.h>
void do_nothing() { }
struct sigaction sa;
sa.sa_handler = do_nothing;
sigemptyset(sa.sa_mask);
#ifdef SA_INTERRUPT
sa.sa_flags = SA_INTERRUPT;
#else
sa.sa_flags = 0;
#endif
sigaction(SIGUSR1, &sa, 0);
Then, in thread B, after starting a new connection, send the signal, after making sure thread A will handle it:
/* need only be done once, but needed in every thread other than A */
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGUSR1)
pthread_sigmask(SIG_BLOCK, &sigs, 0);
/* each time we create a new connection */
kill(getpid, SIGUSR1);
With the above, select will return with an EINTR error -- so check for that and loop (adding the new connection to the set).

Winsock threaded server does not work well when multiple clients connect?

I have a server that spawns a thread for each client that connects to it. The thread then deals with receiving/sending data to the client. Here is my server code:
//////////////
// START //
/////////////
while( 1 )
{
if( listen(serverSocket, SOMAXCONN) == SOCKET_ERROR )
{
printf( "Listen failed with error: %ld\n", WSAGetLastError() );
break;
}
/*
* wait for clients...
*/
clientSocket = accept(serverSocket,
(struct sockaddr *)&clientSockAddr,
&clientSockAddrLen);
if( clientSocket == INVALID_SOCKET )
{
printf("%d:accept failed\n", WSAGetLastError());
closesocket(serverSocket);
WSACleanup();
return 1;
}
printf("Client accepted: IP: %s PORT: %d\n",
inet_ntoa(clientSockAddr.sin_addr),
clientSockAddr.sin_port );
THREADDATA threadData = { clientSocket };
sprintf_s(threadData.ip, "%s", inet_ntoa(clientSockAddr.sin_addr));
// spawn a thread for each client
hthread = CreateThread(
NULL, // don't inherit handle
0, // use default size for the executable
processClient,
(pTHREADDATA)&threadData, // thread data
0, // run right away
&threadId );
}
And here is what my thread function looks like:
/*
called by thread for each client
*/
DWORD WINAPI processClient(LPVOID lpParam)
{
int numBytesRecvd = 0,
numBytesSent = 0,
index = 0,
nLeft = SIZE,
TOTAL = SIZE;
char buff[SIZE];
int run = 1;
char msg[MSG_SIZE];
pTHREADDATA td = (pTHREADDATA)lpParam;
/* keep processing client */
while (run)
{
memset(buff, 0, SIZE);
numBytesRecvd = recv(td->clientSocket, buff, nLeft, 0);
/* CLIENT EXITED */
if( !strcmp(buff, "exit") )
{
printf("Client exited!\n");
run = 0;
}
if( numBytesRecvd > 0 )
{
printf("< %s\n", buff);
}
if (numBytesRecvd == 0)
{
printf("Client closed!\n");
run = 0;
}
if( numBytesRecvd == SOCKET_ERROR )
{
printf("%d:Recieve error!\n", WSAGetLastError());
run = 0;
}
}
closesocket(td->clientSocket);
ExitThread(0);
return 0;
}
Issue:
So I start the server and the client, say client1 (from command prompt in win 7), everything is fine. When I type something at the client1 terminal, it is printed at the server terminal.
Now I launch another client, client2, it gets connected to the server and whatever I type gets displayed at the server, but now when I type something at client1 it doesn't get displayed at the server terminal.
So basically every time I start a new client, only that client can talk to the server, the old clients cannot!!! But I thought the thread will keep handling each client? Or is it that cmd prompt in windows is not thread safe?
I can see two probable issues here : 1) You are passing threadData on the stack to a different thread. You should be allocating it on the heap and then pass it to the thread. 2)I think you are not assigning clientSocket correctly to threadData, shouldnt you be assigning to a member of the structure threadData?

Linux C Socket: Blocked on recv call

In my application i have created a thread for a simple http server, then from within my application i tried to connect to http server but control is blocked/hanged on recv call.
But if try to connect to my application's http server using linux GET command, I will be connected to http server successfully.
As per my understanding by searching the google i found that this is not the right approach.
But if i want to do this, in what should i create the sockets so that i can connect o my http server from within the application.
Below is how my http server socket created
pthread_create(&pt_server, NULL, http_srvr, NULL);
//http server handler
void *http_server()
{
int sockfd, new_fd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
socklen_t sin_size;
struct sigaction sa;
int yes=1;
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}
my_addr.sin_family = AF_INET; // host byte order
my_addr.sin_port = htons(HTTP_PORT); // short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1)
{
perror("bind");
exit(1);
}
printf("Listening to sockets\n");
if (listen(sockfd, BACKLOG) == -1)
{
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1)
{
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof(struct sockaddr_in);
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr,&sin_size)) == -1)
{
perror("accept");
continue;
}
printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));
handle_connection(new_fd);
}
}
And following is how i am doing http POST to my http server
/* create socket */
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return ERRSOCK;
setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0);
/* connect to server */
if (connect(s, &server, sizeof(server)) < 0)
ret=ERRCONN;
else {
if (pfd) *pfd=s;
/* create header */
if (proxy) {
sprintf(header,
"%s http://%.128s:%d/%.256s HTTP/1.0\015\012User-Agent: %s\015\012%s\015\012",
command,
http_server1,
http_port,
url,
http_user_agent,
additional_header
);
} else {
sprintf(header,
"%s /%.256s HTTP/1.0\015\012User-Agent: %s\015\012%s\015\012",
command,
url,
http_user_agent,
additional_header
);
}
hlg=strlen(header);
/* send header */
if (send(s,header,hlg,0)!=hlg)
ret= ERRWRHD;
/* send data */
else if (length && data && (send(s,data,length,0)!=length) )
ret= ERRWRDT;
else {
/* read result & check */
ret=http_read_line(s,header,MAXBUF-1);
and following are the contents of http_read_line, and in this function recv call blocked
static int http_read_line (fd,buffer,max)
int fd; /* file descriptor to read from */
char *buffer; /* placeholder for data */
int max; /* max number of bytes to read */
{ /* not efficient on long lines (multiple unbuffered 1 char reads) */
int n=0;
while (n<max) {
if (recv(fd,buffer,1,0)!=1) {
n= -n;
break;
}
n++;
if (*buffer=='\015') continue; /* ignore CR */
if (*buffer=='\012') break; /* LF is the separator */
buffer++;
}
*buffer=0;
return n;
}
You need to either send an HTTP 1.0 header, or else read about content-length in HTTP 1.1. You are reading the stream to EOS when the server is under no obligation to close the connection, so you block. The Content-Length header tells you how much data is in the body: you should only try to read that many bytes.
If you specify HTTP 1.0 (and no fancy headers) the server will close the connection after sending the response.
You have told "In my application i have created a thread for a simple http server, then from within my application
i tried to connect to http server but control is blocked/hanged on recv call."
That means the recv is never returning 0. Now when the recv function will
return a 0? ->When it gets a TCP FIN segment. It seems that your server is never
sending a TCP FIN segment to the client.
The reason that is most likely here is that, your client code needs modification.
You are sending data from from the client, but you are never sending the FIN,
so I assume that your server function is continuing forever and it had not
sent the FIN. This made the recv wait for ever.
In the current code perhaps the fix is to add a line
else {
/*Send the FIN segment, but we can still read the socket*/
shutdown(s, SHUT_WR);
/* read result & check */
ret=http_read_line(s,header,MAXBUF-1);
In this case the shutdown function sends the TCP FIN and the server function can return and possibly then it would do a proper close.
And on a proper close, the FIN from the server will be received by the client. This would make the recv return 0, instead of getting blocked for ever.
Now if you want to continue any further data transfer from the client, you need to again connect or may be you need to have some different algorithm.
I hope my explanation may help fix the current problem.

Resources