I stated learning C language and now I'm trying to get deal with WinSockets. I have to write Server and Client application using Multiplexing and non-blocking sockets (in prespective it will be a chat but now I just want to make it finally work with simple message exchange).
What I have tried:
Let me show you my code of Server and Client and I'll explain what and how I want to do and what problems I've met.
Server code:
<pre>
#pragma comment(lib, "ws2_32.lib")
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#define PRINTUSERS if (nclients)\
printf("We have %d user online\n", nclients);\
else printf("No users online\n***************\n");
#define PORT 3765
#define BACKLOG 10
#define MAXDATASIZE 1024
#define MAXUSERSONLINE 64
struct client
{
int numClient;
struct client* next;
};
void print(struct client* head);
int pushBack(struct client** head, int numClient);
int checkSockOpt(int socket);
int main(int argc, char* argv[])
{
struct sockaddr_in serverAddr;
struct sockaddr_in clientAddr;
SOCKET socketDescriptor;
fd_set readSocketDescriptor;
fd_set writeSocketDescriptor;
int newSocketDescriptor;
WSADATA wsaData;
WORD wsaStart;
timeval tv;
struct client* head = 0;
pushBack(&head, 111);
pushBack(&head, 222);
pushBack(&head, 999);
print(head);
int retVal;
int clientAddrSize = sizeof(clientAddr);
char bufferData[MAXDATASIZE] = { 0 };
int nclients = 0;
int numBytes = 0;
if (argc > 1)
{
printf("Uses: <chatserver>\n");
return -1;
}
do
{
if ((wsaStart = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0)
{
printf("WSAStartup error ");
break;
}
if ((socketDescriptor = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
printf("Socket error ");
closesocket(socketDescriptor);
break;
}
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(PORT);
serverAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(socketDescriptor, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
printf("Bind error ");
closesocket(socketDescriptor);
break;
}
if (listen(socketDescriptor, BACKLOG) == SOCKET_ERROR)
{
printf("Listening error ");
closesocket(socketDescriptor);
break;
}
while (1)
{
FD_ZERO(&readSocketDescriptor);
FD_ZERO(&writeSocketDescriptor);
FD_SET(socketDescriptor, &readSocketDescriptor);
FD_SET(socketDescriptor, &writeSocketDescriptor);
printf("Setup SERVER socket descriptor number: %d\n", socketDescriptor);
tv.tv_sec = 5;
if ((retVal = select(socketDescriptor + 1, &readSocketDescriptor, &writeSocketDescriptor, NULL, &tv)) == SOCKET_ERROR)
{
printf("Select error ");
break;
}
if ((FD_ISSET(socketDescriptor, &readSocketDescriptor)) != 0)
{
printf("CHECK FD_ISSET value #1: %d\n", FD_ISSET(socketDescriptor, &readSocketDescriptor));
if ((newSocketDescriptor = accept(socketDescriptor, (struct sockaddr *)&clientAddr, &clientAddrSize)) == SOCKET_ERROR)
{
printf("Accept error ");
break;
}
struct client* head = 0;
pushBack(&head, newSocketDescriptor);
print(head);
nclients++; PRINTUSERS
printf("Server got connection from %s\n", inet_ntoa(clientAddr.sin_addr));
FD_SET(newSocketDescriptor, &readSocketDescriptor);
printf("CHECK FD_ISSET value #2: %d\n", (FD_ISSET(socketDescriptor, &readSocketDescriptor)));
printf("newSocketDescriptor = %d, READ_FD status #1 after accept = %d\n", newSocketDescriptor, FD_ISSET(socketDescriptor, &readSocketDescriptor));
checkSockOpt(newSocketDescriptor);
if (FD_ISSET(newSocketDescriptor, &readSocketDescriptor) != 0)
{
pushBack(&head, newSocketDescriptor);
print(head);
if ((numBytes = recv(newSocketDescriptor, bufferData, MAXDATASIZE, 0)) == SOCKET_ERROR)
{
printf("Recv error ");
break;
}
bufferData[numBytes] = '\0';
printf("Recv: retVal = %d, bufferData = %s\n", retVal, bufferData);
printf("newSocketDescriptor = %d, READ_FD status #2 after recv = %d\n", newSocketDescriptor, FD_ISSET(socketDescriptor, &readSocketDescriptor));
if (numBytes == -1)
{
printf("One user has disconnected");
nclients--;
PRINTUSERS
}
else
{
char *testmsg = "Hello, world!\n";
if (send(newSocketDescriptor, testmsg, strlen(testmsg), 0) == SOCKET_ERROR)
{
printf("Send error ");
break;
}
FD_CLR(newSocketDescriptor, &readSocketDescriptor);
}
printf("newSocketDescriptor = %d, READ_FD status #3 after send = %d\n", newSocketDescriptor, FD_ISSET(socketDescriptor, &readSocketDescriptor));
FD_SET(newSocketDescriptor, &readSocketDescriptor);
closesocket(newSocketDescriptor);
}
}
else
{
printf("Sorry, time limit is expired, there is no active sockets\n");
}
}
} while (FALSE);
printf("- Error code: %d\n", WSAGetLastError());
closesocket(socketDescriptor);
WSACleanup();
return 0;
}
int checkSockOpt(int socket)
{
int optVal = 0;
int optLen = sizeof(int);
if (getsockopt(socket, SOL_SOCKET, SO_ACCEPTCONN, (char*)&optVal, &optLen) != SOCKET_ERROR)
{
printf("SockOpt Value: %ld. Error code: %d\n", optVal, WSAGetLastError());
}
return 0;
}
void print(struct client* head)
{
struct client* list = head;
while (list != 0)
{
printf("Client socket %d successfully added to the list\n", list->numClient);
list = list->next;
}
}
void printAllFD(struct client* head)
{
struct client* list = head;
while (list != 0)
{
printf("Client socket %d successfully added to the list\n", list->numClient);
list = list->next;
}
}
int pushBack(struct client** head, int numClient)
{
struct client* fd = (struct client*)malloc(sizeof(struct client));
struct client* list = *head;
fd->numClient = numClient;
fd->next = 0;
if (list == 0)
{
*head = fd;
return 0;
}
while (list->next != 0)
{
list = list->next;
}
list->next = fd;
return 0;
}
Client code:
<pre>
#pragma comment(lib, "ws2_32.lib")
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#define SERVERADDR argv[1]
#define PORT 3765
#define MAXDATASIZE 1024
int main(int argc, char *argv[])
{
struct sockaddr_in destAddr = { 0 };
SOCKET socketDescriptor;
WSADATA wsaData;
fd_set readSet;
fd_set writeSet;
timeval tv;
char bufferData[MAXDATASIZE];
int numBytes;
unsigned long int nb = 1;
int retVal;
if (argc != 2)
{
printf("Uses: <client> <hostname>\nExample: \"client 127.0.0.1\"\n");
return -1;
}
do
{
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("WSAStartup error ");
break;
}
if (gethostbyname(SERVERADDR) == NULL)
{
printf("Gethostbyname error ");
break;
}
if ((socketDescriptor = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
printf("Socket error ");
break;
}
if (ioctlsocket(socketDescriptor, FIONBIO, (unsigned long *)&nb) != 0)
{
printf("ioctlsocket error ");
break;
}
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(PORT);
destAddr.sin_addr.s_addr = inet_addr(SERVERADDR);
printf("Attempt to connect %s:%d\n", inet_ntoa(destAddr.sin_addr), htons(destAddr.sin_port));
tv.tv_sec = 20;
if (connect(socketDescriptor, (sockaddr *)&destAddr, sizeof(destAddr)))
{
printf("Connect error ");
FD_ZERO(&writeSet);
FD_SET(socketDescriptor, &writeSet);
retVal = select(socketDescriptor + 1, NULL, &writeSet, NULL, &tv);
if (retVal == SOCKET_ERROR)
{
printf("Send non-blocking error ");
break;
}
else if (retVal == 0)
{
printf("Non-blocking connect time limit is expired");
break;
;
}
}
printf("Connection with %s\n", SERVERADDR);
while (1)
{
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_SET(socketDescriptor, &readSet);
FD_SET(socketDescriptor, &writeSet);
retVal = select(1, &readSet, &writeSet, NULL, &tv);
if (retVal == SOCKET_ERROR)
{
printf("Select error ");
break;
}
else if (retVal == 0)
{
printf("Time limit is expired ");
continue;
}
char *testmsg = "Hello, Earth!\n";
if (FD_ISSET(socketDescriptor, &writeSet) != 0)
{
if (send(socketDescriptor, testmsg, strlen(testmsg), 0) == SOCKET_ERROR)
{
printf("Send error ");
break;
}
printf("Send function - OK\n");
}
if (FD_ISSET(socketDescriptor, &readSet) != 0)
{
if ((numBytes = recv(socketDescriptor, bufferData, MAXDATASIZE, 0)) == SOCKET_ERROR)
{
printf("Recv error ");
break;
}
printf("Recv function - OK\n");
}
bufferData[numBytes] = '\0';
printf("Received text: %s\n", bufferData);
}
} while (FALSE);
printf("- Error code: %d\n", WSAGetLastError());
closesocket(socketDescriptor);
WSACleanup();
return 0;
}
Client: not sure but it seems almost ok. I get my sockDescriptor, make my socket non-blocking using ioctlsocket function, then make connection with connect. As I need to send a message I set my writeSet with FD_ZERO and FD_SET and use select function. It seems fine (or not?)
After I have to wrtie my logic in while(1) cycle. I set up my read and write descriptors, user select function (is it used correct?) and check my write descriptor if it ready to send a message. Then send. And the same with recv. After I need to add the symbol of the end of string "\0" and print my text received from server.
Server: first of all I wanna talk about the list of connected clients. I wrote a structure (client) and a few function to work with them (In the end of this post I'll show my list function, how it should work). The function pushBack make a list, print, obviously, print the list. How to add my new client to this list? And I also have to clean the memory. As I didn't get how to add my client, I don't know how to clean that.
Then my server logic: socket -> bind -> listen and while(1) where I set up my readfds and writefds>. Then I use select for my socketDescriptor and if everything is ok I check if it's ready for work. Then accept for my new client user (newSocketDescriptor - is that what I whould add to my list?). Then I try to add to my list (not even sure if it works fine). nclients is my client counter. Then I make FD_SET for my newSocketDescriptor and AFTER here is my issue where I'm not even sure what started to happen. I use recv, check if the user is disconnected and if it's not - send message and FD_CLR my newSocketDescriptor.
Server output in cmd:
Setup SERVER socket descriptor number: 256
CHECK FD_ISSET value #1: 1
Client socket 264 successfully added to the list
We have 1 user online
Server got connection from 127.0.0.1
CHECK FD_ISSET value #2: 1
newSocketDescriptor = 264, READ_FD status #1 after accept = 1
SockOpt Value: 0. Error code: 0
Client socket 264 successfully added to the list
Client socket 264 successfully added to the list
Recv: retVal = 1, bufferData = Hello, Earth!
Hello, Earth!
Hello, Earth!
newSocketDescriptor = 264, READ_FD status #2 after recv = 1
newSocketDescriptor = 264, READ_FD status #3 after send = 1
Setup SERVER socket descriptor number: 256
Client output:
Attempt to connect 127.0.0.1:3765
Connect error Connection with 127.0.0.1
Send function - OK
Received text: ä
Send function - OK
Received text: ä
Send function - OK
Received text: ä
Send function - OK
Received text: ä
Send function - OK
Received text: ä
Send function - OK
eceived text: ä
Send error - Error code: 10054
As you cand see I have the error with 10054 code but couldn't get for 1.5 days where is the problem. Could you help me, please?
I also don't understand why I receive a few messages in server and client part (there is a random symbol everytime in client part, don't also understand why). Why?
Thank you for attention, the question is big, sorry for that.