fp = fopen("image.jpg","rb");
if (!fp)
exit(1);
fseek(fp,0,SEEK_END);
len = ftell(fp);
fseek(fp,0,SEEK_SET);
buf = (char *)malloc(len);
fread(buf,len,1,fp);
fclose(fp);
if (WSAStartup(0x0202,&wsa) != 0)
{
printf("Error code : %d",WSAGetLastError());
return 1;
}
if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)
{
printf("Error code : %d" , WSAGetLastError());
WSACleanup();
exit(1);
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( 8888 );
if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)
{
printf("Error code : %d" , WSAGetLastError());
closesocket(s);
WSACleanup();
exit(EXIT_FAILURE);
}
sprintf(str,"%d",len);
strcpy(message,"HTTP/1.1 200 OK\r\nContent-Length: ");
sprintf(message,"%s %s",message,str);
sprintf(message,"%s %s",message,"\r\nContent-Type: image/jpeg\r\n\r\n");
sprintf(message,"%s %s",message,buf);
sprintf(message,"%s %s",message,"\r\n");
listen(s , 100);
c = sizeof(struct sockaddr_in);
while( (new_socket = accept(s , (struct sockaddr *)&client, &c)) != INVALID_SOCKET )
{
memset(recvdata,'\0',sizeof(recvdata));
recv(new_socket,recvdata,2000,0);
send(new_socket , message , strlen(message) , 0);
}
if (new_socket == INVALID_SOCKET)
{
printf("Error code : %d" , WSAGetLastError());
closesocket(s);
WSACleanup();
return 1;
}
closesocket(s);
WSACleanup();
return 0;
}
I have a problem with sending image file. This is server side of communication and browser will be client side. When i try to connect to server, server accepts connection and everything is ok. Then i want to send image as response, and it should be showed in browser. Can anyone tell me what is a problem here?
Many issues. Just to start:
You are not checking return values of system calls (listen(), recv(), send(), etc.), so you don't know about errors, and how much data you sent or received.
Printing binary data like from image file here with printf() is a Bad IdeaTM - it will be truncated at the first zero byte, or it might overrun your memory with a lack of such.
You are assuming recv() consumes full HTTP request from a client. It might not, so you are breaking HTTP protocol.
You are not closing connected client socket in your loop. That is a resource leak.
Related
I wrote a windows socket server with I/O Completion port to handle heavy data transmission. It works smoothly when there is only one client connected. when more than one client connect, other threads just seem blocked while only one thread works fine.
I changed my design to use select() with each thread for each connection, the problem seems still the same. By the way, the same design works fine in OSX.
However, when I run multiple instances of my process, a process serves each connection, it works great.
Anyone kind enough to enlighten me? I still prefer multithread design. Core code as following:
other part of code basically comes from: http://www.codeproject.com/Articles/13382/A-simple-application-using-I-O-Completion-Ports-an
//Worker thread will service IOCP requests
DWORD WINAPI WorkerThread(LPVOID lpParam)
{
int nThreadNo = (int)lpParam;
void *lpContext = NULL;
OVERLAPPED *pOverlapped = NULL;
CClientContext *pClientContext = NULL;
DWORD dwBytesTransfered = 0;
int nBytesRecv = 0;
int nBytesSent = 0;
DWORD dwBytes = 0, dwFlags = 0;
//Worker thread will be around to process requests, until a Shutdown event is not Signaled.
while (WAIT_OBJECT_0 != WaitForSingleObject(g_hShutdownEvent, 0))
{
BOOL bReturn = GetQueuedCompletionStatus(
g_hIOCompletionPort,
&dwBytesTransfered,
(LPDWORD)&lpContext,
&pOverlapped,
INFINITE);
//WriteToConsole("\nThread %d: GetQueuedCompletionStatus.", nThreadNo);
if (NULL == lpContext)
{
//We are shutting down
break;
}
//Get the client context
pClientContext = (CClientContext *)lpContext;
if ((FALSE == bReturn) || ((TRUE == bReturn) && (0 == dwBytesTransfered)))
{
//Client connection gone, remove it.
RemoveFromClientListAndFreeMemory(pClientContext);
continue;
}
WSABUF *p_wbuf = pClientContext->GetWSABUFPtr();
OVERLAPPED *p_ol = pClientContext->GetOVERLAPPEDPtr();
switch (pClientContext->GetOpCode())
{
case OP_READ:
pClientContext->SetTransLen(dwBytesTransfered);
if(!pClientContext->IsComplete())
{
pClientContext->SetOpCode(OP_READ);
dwFlags = 0;
//Overlapped send
nBytesSent = WSASend(pClientContext->GetSocket(), p_wbuf, 1,
&dwBytes, dwFlags, p_ol, NULL);
WriteToConsole("\nThread %d: WSASend continue bytes = %d.", nThreadNo, dwBytes);
if ((SOCKET_ERROR == nBytesSent) && (WSA_IO_PENDING != WSAGetLastError()))
{
//Let's not work with this client
RemoveFromClientListAndFreeMemory(pClientContext);
WriteToConsole("\nThread %d: WSASend failed.", nThreadNo);
}
}
else
{
WriteToConsole("\nsocket %d: WSASend complete.", pClientContext->GetSocket());
//clear cache
pClientContext->ResetWSABUF();
//for the next recv, must be triggered.
pClientContext->SetOpCode(OP_WRITE);
//Get the data.
nBytesRecv = WSARecv(pClientContext->GetSocket(), p_wbuf, 1,
&dwBytes, &dwFlags, p_ol, NULL);
if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != WSAGetLastError()))
{
WriteToConsole("\nThread %d: Error occurred while executing WSARecv().", nThreadNo);
//Let's not work with this client
RemoveFromClientListAndFreeMemory(pClientContext);
}
}
break;
case OP_WRITE:
pClientContext->SetTransLen(dwBytesTransfered);
if(pClientContext->IsComplete())
{
if(!pClientContext->ProcessCmd())
{
WriteToConsole("\nThread %d: ProcessCmd failed.", nThreadNo);
//Let's not work with this client
RemoveFromClientListAndFreeMemory(pClientContext);
}
WriteToConsole("\nThread %d: receive completed.", nThreadNo);
//Send the message back to the client.
pClientContext->SetOpCode(OP_READ);
dwFlags = 0;
//Overlapped send
nBytesSent = WSASend(pClientContext->GetSocket(), p_wbuf, 1,
&dwBytes, dwFlags, p_ol, NULL);
WriteToConsole("\nThread %d: WSASend bytes = %d.", nThreadNo, dwBytes);
if ((SOCKET_ERROR == nBytesSent) && (WSA_IO_PENDING != WSAGetLastError()))
{
WriteToConsole("\nThread %d: Error occurred while executing WSASend().", nThreadNo);
//Let's not work with this client
RemoveFromClientListAndFreeMemory(pClientContext);
}
}
else //continue receiving
{
pClientContext->SetOpCode(OP_WRITE);
//Get the data.
nBytesRecv = WSARecv(pClientContext->GetSocket(), p_wbuf, 1,
&dwBytes, &dwFlags, p_ol, NULL);
WriteToConsole("\nThread %d: WSARecv continue bytes = %d.", nThreadNo, dwBytes);
if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != WSAGetLastError()))
{
WriteToConsole("\nThread %d: Error occurred while executing WSARecv().", nThreadNo);
//Let's not work with this client
RemoveFromClientListAndFreeMemory(pClientContext);
}
}
break;
default:
//We should never be reaching here, under normal circumstances.
break;
} // switch
} // while
return 0;
}
Updates: since my server app need to send large amount of data to multiple clients, it works fine maybe for some minutes, but somehow for no reason, some threads just block without responding. Does it have anything to do with the size of data?
I have implemented a c++ winsock (win 32) with intention of crashing using an strcpy command. The socket itself is instantiated inside a thread. However, when I put the strcpy inside the recv loop, it does not seem to crash.
I know there is nothing wrong with the compiler since writing one with just strcpy crashes, I am thinking it is related to recv since it initiates a block on the process.
Here is the full code below for the sever, the crash I am trying to implement is in the form of strcpy(a, "AAAA..."); in regular circumstances it should crash, but here it does not. I would like to know why.
#define WIN32_LEAN_AND_MEAN
#include<windows.h>
#include<winsock2.h>
#include<stdlib.h>
#include<stdio.h>
#include<ws2tcpip.h>
#include <iostream.h>
#include <conio.h>
#define DEFAULT_PORT "1133"
#define DEFAULT_BUFLEN 512
struct thread_data
{
int m_id;
thread_data(int id) : m_id(id){}
};
char a[10];
DWORD WINAPI ServerThread (LPVOID pParam){
WSADATA wsaData;
struct addrinfo *result =NULL;
struct addrinfo hints;
SOCKET ListenSocket = INVALID_SOCKET;
do{
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
int iResult;
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
iResult = getaddrinfo(NULL,DEFAULT_PORT,&hints, &result);
if (iResult != 0 ){
printf("get addrinfo failed with error %d\n", iResult);
WSACleanup();
return 1;
} //end if
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket ==0){
printf("socket creation failed with error %d\n", WSAGetLastError());
}
//bind socket
iResult= bind( ListenSocket , result->ai_addr, (int)result->ai_addrlen);
if(iResult == SOCKET_ERROR){
printf("bind failed with error %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
printf ("initializing socket\n ");
iResult= listen(ListenSocket,SOMAXCONN);
if (iResult== SOCKET_ERROR){
printf("listen failed with %d\n",WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
SOCKET client ;
sockaddr_in from;
int fromlen=sizeof(from);
char temp[1024];
char temp_to_send[1024];
char temp_to_send_vuln[512];
printf("accepting client request\n");
client=accept(ListenSocket, (struct sockaddr*) &from, &fromlen);
printf("accepted socket\n");
iResult =1;
int iSendResult =1;
char c;
//start receiving from client
while( (iResult = recv(client,temp,1024,0 )) > 0 ){
c = temp[0];
temp[iResult] = '\0';
if(c!=13)
strcat(temp_to_send,temp);
//if enter is hit echo sent data to client
if(c ==13 ){
printf("sending %s \n",temp_to_send);
//I WANT TO CRASH THE PGORAM HERE!!
strcpy(a,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
strcat(temp_to_send_vuln,temp_to_send);
strcat(temp_to_send_vuln,"\r\n");
iSendResult = send(client,temp_to_send,strlen(temp_to_send),0);
//if user types "exit" the client socket would terminate
if (strcmp(temp_to_send,"exit") ==0){
printf("exit entered\n");
closesocket(client);
WSACleanup();
break;
}
re-initialize variables for next input
temp[0] = '\0';
temp_to_send[0] = '\0';
}//end if(ch ==13)
}//end recv
printf("termination of socket with error %d and buffer length is ", WSAGetLastError());
printf("client said %s\n", temp) ;
if (iResult == SOCKET_ERROR){
printf("receiving failed with error %d",WSAGetLastError());
}
if (iSendResult == SOCKET_ERROR){
printf("seding failed with error %d", WSAGetLastError());
closesocket(client);
WSACleanup();
exit(1);
}
} while(1);
closesocket(ListenSocket);
WSACleanup();
printf("program ended\n");
return 0;
}
//the main function that calls the thread
int main(void)
{
//create thread here
CreateThread(NULL, 0 ,ServerThread, new thread_data(0), 0,0);
//terminate program when escape character is hit
while(_getch()!=27);
return 0;
}
Your strcpy() call is just trashing a and then whatever else happens to be after it in global memory; it's undefined whether it'll crash or not. If you really want to crash out, just call strcpy( NULL, "whatever" ).
I have developed a client server UDP application. The Server UDP socket is set to be a BROADCAST UDP Socket. The code of both sides does not generate any error, but the message sent from BROADCAST UDP SERVER Side is not Received at client side. Kindly have a look at my code, i know there is some blunder I can't figure out. I really need help:
SERVER:
#define PORT 8888 //The port on which to listen for incoming data
int main()
{
SOCKET s;
struct sockaddr_in serverSocket, clientSocket;
char receiveBuffer[1000];
//int receiveBufferLength=1000;
int clientSocketLength;
int recv_len;
clientSocketLength = sizeof(clientSocket) ;
WSADATA wsa;
//Initialise winsock
printf("\nInitialising Winsock...");
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
{
printf("Failed. Error Code : %d",WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("Socket Initialised.\n");
//Create a socket
if((s = socket(AF_INET , SOCK_DGRAM , 0 )) == INVALID_SOCKET)
{
printf("Could not create socket : %d" , WSAGetLastError());
}
printf("Socket created.\n");
//Prepare the sockaddr_in structure
serverSocket.sin_family = AF_INET;
serverSocket.sin_addr.s_addr = INADDR_ANY;
serverSocket.sin_port = htons( PORT );
int broadcast =1;
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*) &broadcast, sizeof(broadcast)) < 0) {
//close(sock);
printf("Error in setting broadcast option");
}
//Bind
if( bind(s ,(struct sockaddr *)&serverSocket , sizeof(serverSocket)) == SOCKET_ERROR)
{
printf("\nBind failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("Bind done\n\n");
//keep listening for data
printf("\n\t\t\tWaiting for data...\n");
fflush(stdout);
//receiveBuffer[2000]=NULL;
if((recv_len = recvfrom(s, receiveBuffer, 1000, 0, (struct sockaddr *) &clientSocket, &clientSocketLength)) == SOCKET_ERROR)
{
printf("\n\nrecvfrom() failed with error code : %d" , WSAGetLastError());
//exit(EXIT_FAILURE);
while(1);
}
//print details of the client/peer and the data received
printf("\n\nReceived packet from %s:%d\n", inet_ntoa(clientSocket.sin_addr), ntohs(clientSocket.sin_port));
printf("\nClient Says: " );
printf(receiveBuffer,recv_len);
serverSocket.sin_addr.s_addr = INADDR_BROADCAST;
//now reply the client with the same data
if (sendto(s, receiveBuffer, recv_len, 0, (struct sockaddr*) &serverSocket, sizeof(serverSocket)) == SOCKET_ERROR)
{
printf("\nsendto() failed with error code : %d" , WSAGetLastError());
// exit(EXIT_FAILURE);
while(1);
}
else
printf("\nMessage Sent Back to Client");
while(1);
closesocket(s);
WSACleanup();
return 0;
}
CLIENT:
#define PORT 8888 //The port on which to listen for incoming data
#define SERVER "127.0.0.1" //ip address of udp server
//#define PORT 8888 //The port on which to listen for incoming data
int main(void)
{
struct sockaddr_in connectedSocket;
int s;
int length=sizeof(connectedSocket);
char receiveBuffer[1000];
char message[1000];
//clear the buffer by filling null, it might have previously received data
memset(receiveBuffer,'\0', 1000);
WSADATA wsa;
//Initialise winsock
printf("\nInitialising Winsock...\n");
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
{
printf("\nFailed. Error Code : %d",WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("\n.........Initialised.\n");
//create socket
if ( (s=socket(AF_INET, SOCK_DGRAM, 0)) == SOCKET_ERROR)
{
printf("\n\nsocket() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}
//setup address structure
memset((char *) &connectedSocket, 0, sizeof(connectedSocket));
connectedSocket.sin_family = AF_INET;
connectedSocket.sin_port = htons(PORT);
//connectedSocket.sin_port = INADDR_BROADCAST;
connectedSocket.sin_addr.S_un.S_addr = inet_addr(SERVER);
printf("\n\n\nEnter message : ");
gets(message);
//send the message
if (sendto(s, message,sizeof(message) , 0 , (struct sockaddr *) &connectedSocket, sizeof(connectedSocket)) == SOCKET_ERROR)
{
printf("\nsendto() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("\nMessage Successfully sent to Server");
// fflush(stdout);
if (recvfrom(s, receiveBuffer, 1000, 0, (struct sockaddr *) &connectedSocket,&length) == SOCKET_ERROR)
{
printf("\nrecvfrom() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
while(1);
}
printf("\nServer Says : ");
printf(receiveBuffer,sizeof(receiveBuffer));
while(1);
closesocket(s);
WSACleanup();
return 0;
}
You are sending the reply back to the client on the serverSocket. You already have a clientsocket that you received the message on, use it to send the message back from the server to the client.
Remove ...
serverSocket.sin_addr.s_addr = INADDR_BROADCAST;
and change ...
if (sendto(s, receiveBuffer, recv_len, 0, (struct sockaddr*) &serverSocket, sizeof(serverSocket)) == SOCKET_ERROR)
to
if (sendto(s, receiveBuffer, recv_len, 0, (struct sockaddr*) &clientSocket, sizeof(clientSocket)) == SOCKET_ERROR)
It should work now.
I have created single Server and Single Client echo application. It works fine for single server and single client. But now I want to make it more practical by handling multiple clients for a single Server. I came accross the idea of using listen() function at Server side to handle multiple client connections but then I came to know that listen is only used for TCP. Kindly help me with this. below is my functional code for single Server and Single Client. Help me with how can I modify it to make a Single Server Multiple Client application.
SERVER:
#define PORT 8888 //The port on which to listen for incoming data
int main()
{
SOCKET s;
struct sockaddr_in serverSocket, clientSocket;
char receiveBuffer[1000];
//int receiveBufferLength=1000;
int clientSocketLength;
int recv_len;
clientSocketLength = sizeof(clientSocket) ;
WSADATA wsa;
//Initialise winsock
printf("\nInitialising Winsock...");
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
{
printf("Failed. Error Code : %d",WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("Socket Initialised.\n");
//Create a socket
if((s = socket(AF_INET , SOCK_DGRAM , 0 )) == INVALID_SOCKET)
{
printf("Could not create socket : %d" , WSAGetLastError());
}
printf("Socket created.\n");
//Prepare the sockaddr_in structure
serverSocket.sin_family = AF_INET;
serverSocket.sin_addr.s_addr = INADDR_ANY;
serverSocket.sin_port = htons( PORT );
//Bind
if( bind(s ,(struct sockaddr *)&serverSocket , sizeof(serverSocket)) == SOCKET_ERROR)
{
printf("\nBind failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("Bind done\n\n");
//keep listening for data
while(1)
{
printf("\n\t\t\tWaiting for data...\n");
fflush(stdout);
//receiveBuffer[2000]=NULL;
if((recv_len = recvfrom(s, receiveBuffer, 1000, 0, (struct sockaddr *) &clientSocket, &clientSocketLength)) == SOCKET_ERROR)
{
printf("\n\nrecvfrom() failed with error code : %d" , WSAGetLastError());
//exit(EXIT_FAILURE);
while(1);
}
//print details of the client/peer and the data received
printf("\n\nReceived packet from %s:%d\n", inet_ntoa(clientSocket.sin_addr), ntohs(clientSocket.sin_port));
printf("\nClient Says: " );
printf(receiveBuffer,recv_len);
//now reply the client with the same data
if (sendto(s, receiveBuffer, recv_len, 0, (struct sockaddr*) &clientSocket, clientSocketLength) == SOCKET_ERROR)
{
printf("\nsendto() failed with error code : %d" , WSAGetLastError());
// exit(EXIT_FAILURE);
while(1);
}
else
printf("\nMessage Sent Back to Client");
}
closesocket(s);
WSACleanup();
return 0;
}
CLIENT:
#define PORT 8888 //The port on which to listen for incoming data
#define SERVER "10.0.1.25" //ip address of udp server
//#define PORT 8888 //The port on which to listen for incoming data
int main(void)
{
struct sockaddr_in connectedSocket;
int s;
int length=sizeof(connectedSocket);
char receiveBuffer[1000];
char message[1000];
//clear the buffer by filling null, it might have previously received data
memset(receiveBuffer,'\0', 1000);
WSADATA wsa;
//Initialise winsock
printf("\nInitialising Winsock...\n");
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
{
printf("\nFailed. Error Code : %d",WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("\n.........Initialised.\n");
//create socket
if ( (s=socket(AF_INET, SOCK_DGRAM, 0)) == SOCKET_ERROR)
{
printf("\n\nsocket() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}
//setup address structure
memset((char *) &connectedSocket, 0, sizeof(connectedSocket));
connectedSocket.sin_family = AF_INET;
connectedSocket.sin_port = htons(PORT);
//connectedSocket.sin_port = INADDR_BROADCAST;
connectedSocket.sin_addr.S_un.S_addr = inet_addr(SERVER);
while(1)
{
printf("\n\n\nEnter message : ");
gets(message);
//send the message
if (sendto(s, message,sizeof(message) , 0 , (struct sockaddr *) &connectedSocket, sizeof(connectedSocket)) == SOCKET_ERROR)
{
printf("\nsendto() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("\nMessage Successfully sent to Server");
// fflush(stdout);
if (recvfrom(s, receiveBuffer, 1000, 0, (struct sockaddr *) &connectedSocket,&length) == SOCKET_ERROR)
{
printf("\nrecvfrom() failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}
printf("\nServer Says : ");
printf(receiveBuffer,sizeof(receiveBuffer));
}
closesocket(s);
WSACleanup();
return 0;
}
Let me explain a few things for you that might be confusing.
First, in your server you need to set serverSocket.sin_addr.s_addr to INADDR_ANY even if you want to broadcast. You don't want to make it INADDR_BROADCAST. This is simply telling the server to bind on any IP address your computer has, cause a single computer can have more than 1 IP. Which you are doing correctly.
But how can you broadcast from the server? You need to use setsockopt with SO_BROADCAST parameter like this ...
int options = 1;
if ((setsockopt(s, SOL_SOCKET, SO_BROADCAST,(char *)&options,sizeof(options))) < 0){
printf("%d",WSAGetLastError());
}
This will make your server listens for broadcasts, but how can you send broadcasts? You need to set INADDR_BROADCAST in the s_addr field in the sockaddr structure before calling sendto function.
clientSocket.sin_addr.s_addr = INADDR_BROADCAST;
Then use sendto with clientSocket to boardcast your messsage back.
Second, in your client code, you have two options depending on your application.
If you want your clients to send message first to the server (direct without broadcast) then you are doing it right, by setting inet_addr(SERVER) so the client send the message to your server only.
But, if you want the client to broadcast the message, then you need to use setsockopt again in your client, same as in your server, and set the address in connectSocketstructure to INADDR_BROADCAST.
Finally, it would be better if you tell us what are you trying to accomplish here. If you want clients to connect to server first, then the server announce the client to other clients, then you might take TCP instead of UDP.
The ConnectEx function requires an "unconnected, previously bound socket". Indeed, if I omit the bind step in my example (see below), ConnectEx fails with WSAEINVAL.
Here's my current understanding: before calling ConnectEx, bind the socket to INADDR_ANY and port 0 (unless it is already bound):
struct sockaddr_in addr;
ZeroMemory(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = 0;
rc = bind(sock, (SOCKADDR*) &addr, sizeof(addr));
if (rc != 0) { ... bind failed; call WSAGetLastError to see why ... }
Or for an IPv6 socket:
struct sockaddr_in6 addr;
ZeroMemory(&addr, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_addr = in6addr_any;
addr.sin6_port = 0;
rc = bind(sock, (SOCKADDR*) &addr, sizeof(addr));
if (rc != 0) { ... bind failed; call WSAGetLastError to see why ... }
This lets the operating system assign a local address to our socket (as opposed to the remote address we are connecting to). connect does this step automatically, but ConnectEx does not.
My questions are:
Is my assessment correct?
Is there a way to do this automatic bind that is agnostic to the address family, or will I have to handle each of AF_INET, AF_INET6, AF_BTH (Bluetooth), etc. manually?
Working ConnectEx example (also on Gist: https://gist.github.com/4158972):
#include <stdio.h>
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
struct mswsock_s {
LPFN_CONNECTEX ConnectEx;
} mswsock;
static BOOL load_mswsock(void)
{
SOCKET sock;
DWORD dwBytes;
int rc;
/* Dummy socket needed for WSAIoctl */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET)
return FALSE;
{
GUID guid = WSAID_CONNECTEX;
rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid, sizeof(guid),
&mswsock.ConnectEx, sizeof(mswsock.ConnectEx),
&dwBytes, NULL, NULL);
if (rc != 0)
return FALSE;
}
rc = closesocket(sock);
if (rc != 0)
return FALSE;
return TRUE;
}
int main(int argc, char *argv[])
{
int rc;
BOOL ok;
WSADATA wsaData;
SOCKET sock;
rc = WSAStartup(MAKEWORD(2,2), &wsaData);
if (rc != 0) {
printf("WSAStartup failed: %d\n", rc);
return 1;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
printf("Your computer is from the wrong millenium.\n");
WSACleanup();
return 1;
}
if (!load_mswsock()) {
printf("Error loading mswsock functions: %d\n", WSAGetLastError());
return 1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
printf("socket: %d\n", WSAGetLastError());
return 1;
}
/* ConnectEx requires the socket to be initially bound. */
{
struct sockaddr_in addr;
ZeroMemory(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = 0;
rc = bind(sock, (SOCKADDR*) &addr, sizeof(addr));
if (rc != 0) {
printf("bind failed: %d\n", WSAGetLastError());
return 1;
}
}
/* Issue ConnectEx and wait for the operation to complete. */
{
OVERLAPPED ol;
ZeroMemory(&ol, sizeof(ol));
sockaddr_in addr;
ZeroMemory(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("173.194.37.36"); // google.com
addr.sin_port = htons(80);
ok = mswsock.ConnectEx(sock, (SOCKADDR*) &addr, sizeof(addr), NULL, 0, NULL, &ol);
if (ok) {
printf("ConnectEx succeeded immediately\n");
} else if (WSAGetLastError() == ERROR_IO_PENDING) {
printf("ConnectEx pending\n");
DWORD numBytes;
ok = GetOverlappedResult((HANDLE) sock, &ol, &numBytes, TRUE);
if (ok)
printf("ConnectEx succeeded\n");
else
printf("ConnectEx failed: %d\n", WSAGetLastError());
} else {
printf("ConnectEx failed: %d\n", WSAGetLastError());
return 1;
}
}
/* Make the socket more well-behaved. */
rc = setsockopt(sock, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0);
if (rc != 0) {
printf("SO_UPDATE_CONNECT_CONTEXT failed: %d\n", WSAGetLastError());
return 1;
}
/* This will fail if SO_UPDATE_CONNECT_CONTEXT was not performed. */
rc = shutdown(sock, SD_BOTH);
if (rc != 0) {
printf("shutdown failed: %d\n", WSAGetLastError());
return 1;
}
printf("Done\n");
return 0;
}
connect does this step automatically, but ConnectEx does not.
Correct.
Is my assessment correct?
Yes.
Is there a way to do this automatic bind that is agnostic to the address family, or will I have to handle each of AF_INET, AF_INET6, AF_BTH (Bluetooth), etc. manually?
I believe that INADDR_ANY is a bunch of zeros in all address families, so you could just try using the memset() and omitting the assignment to addr.sin_addr.s_addr completely. Whether this is kosher, portable, politically correct etc. is another question into which I will not enter.
It seems pretty curious that Microsoft didn't manage to have ConnectEx() call bind() internally, considering that saving system calls is the motivation for its existence, and also considering that most programs never bind an outbound socket at all.
It is possible to get the bind address for ConnectEx in an address family independent way.
Solution 1
Call getaddrinfo with the following options:
pServiceName = "0"
hints.ai_flags = AI_PASSIVE
hints.ai_family = address family of the socket
Then use the first result of the returned address list.
To get the address family of the socket you can use getsockopt with SO_PROTOCOL_INFOW.
Solution 2
Use SOCKADDR_STORAGE for the address structure and call INETADDR_SETANY which is defined in MSTcpIP.h. It supports AF_INET and AF_INET6.