Click here to Skip to main content
15,887,135 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
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>
//#include "list.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;

	// LIST TEST #1
	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;

			// Do not forget to put after WHILE(1)
			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));
				//int clientList[MAXUSERSONLINE] = { 0 };
				//for (int i = 0; i <= MAXUSERSONLINE; i++)
				//{

				if ((newSocketDescriptor = accept(socketDescriptor, (struct sockaddr *)&clientAddr, &clientAddrSize)) == SOCKET_ERROR)
				{
					printf("Accept error ");
					break;
				}
				//	newSocketDescriptor = clientList[i];
				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);
				//FD_CLR(socketDescriptor, &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);
					//RECV function
					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
					{
						// SEND message to CLIENT
						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);
				}
				//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;
}

// Check Socket Option
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;
}

// Print Client List
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;
	}
}

// pushBack for Client 
int pushBack(struct client** head, int numClient)
{
	//struct client* fd = 0;
	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.
Posted
Updated 27-Apr-17 11:36am
v5

1 solution

Quote:
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?

When you don't understand what your code is doing or why it does what it does, the answer is debugger.
Use the debugger to see what your code is doing. Just set a breakpoint and see your code performing, the debugger allow you to execute lines 1 by 1 and to inspect variables as it execute, it is an incredible learning tool.

Debugger - Wikipedia, the free encyclopedia[^]
Mastering Debugging in Visual Studio 2010 - A Beginner's Guide[^]
Basic Debugging with Visual Studio 2010 - YouTube[^]
The debugger is here to show you what your code is doing and your task is to compare with what it should do.
There is no magic in the debugger, it don't find bugs, it just help you to. When the code don't do what is expected, you are close to a bug.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900