c++ server client chat
I'm making a server, client app in c++ console based.
What I did so far:
- I can connect to the server.
- I can send messages to the server.
- The server can send the messages back.
But what I can't figure out, how can I let the server act also as a client to send messages to the client while he is processing received messages from the client?
People can use it as an example as well :D
Well I will post also some parts of the code:
server:
#include "stdafx.h"
using namespace std;
//our main function
void main()
{
int numClients;
long antwoord;
char chatname[100];
char bericht[250]; //messages
char sbericht[250]; //smessages
//here we set the Winsock-DLL to start
WSAData wsaData;
WORD DLLVERSION;
DLLVERSION = MAKEWORD(2,1);
//here the Winsock-DLL will be started with WSAStartup
//version of the DLL
antwoord = WSAStartup(DLLVERSION, &wsaData);
if(antwoord != 0)
{
WSACleanup();
exit(1);
}
else
{
cout << "WSA started successfully" <<endl;
cout << "The status: \n" << wsaData.szSystemStatus <<endl;
}
//the DLL is started
//structure of our socket is being created
SOCKADDR_IN addr;
//addr is our struct
int addrlen = sizeof(addr);
//socket sListen - will listen to incoming connections
SOCKET sListen;
//socket sConnect - will be operating if a connection is found.
SOCKET sConnect;
//setup of our sockets
//opgezocht op internet - AF_INET bekend dat het lid is van de internet familie
//Sock_STREAM betekenend dat onze socket een verbinding georiënteerde socket is.
sConnect = socket(AF_INET,SOCK_STREAM,NULL);
//now we have setup our struct
//inet_addr is our IP adres of our socket(it will be the localhost ip
//that will be 127.0.0.1
addr.sin_addr.s_addr = inet_addr("192.168.1.103");
//retype of the family
addr.sin_family = AF_INET;
//now the server has the ip(127.0.0.1)
//and the port number (4444)
addr.sin_port = htons(4444);
//here we will define the setup for the sListen-socket
sListen = socket(AF_INET,SOCK_STREAM,NULL);
if (sConnect == INVALID_SOCKET)
{
cout << "Error at socket(): \n" << WSAGetLastError() <<endl;
WSACleanup();
}
else
{
cout << "Connect socket() is OK!" <<endl;
}
if(sListen == INVALID_SOCKET)
{
cout << "Error at socket(): \n" << WSAGetLastError() <<endl;
WSACleanup();
}
else
{
cout << "Listen socket() is OK!" <<endl;
}
//here the sListen-socket will be bind
//we say that the socket has the IP adress of (127.0.0.1) and 开发者_StackOverflowis on port (4444)
//we let the socket become the struct "addr"
if(bind(sListen, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR)
{
cout << "bind() failed: \n" << WSAGetLastError() <<endl;
WSACleanup();
exit(1);
}
else{
cout << "bind() is OK!" <<endl;
}
//here we will tell what the server must do when a connection is found
//therefor we will create an endless loop
cout << "Waiting for a incoming connection..." <<endl;
for(;;)
{
//now we let the socket listen for incoming connections
//SOMAXCOMM heeft het nut dat het dan voordurend luisterd naar inkomende verbindingen zonder limiet
listen(sListen, SOMAXCONN);
while(numClients < SOMAXCONN)
{
//if a connection is found: show the message!
if(sConnect = accept(sListen, (SOCKADDR*)&addr, &addrlen))
{
cout << "A Connection was found!" <<endl;
antwoord = send(sConnect, "Welcome to our chat:", 21,NULL);
if(antwoord > 1)
{
antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL);
antwoord = recv(sConnect, chatname, sizeof(chatname), NULL);
while(antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL) && (antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL)) )
{
antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL);
antwoord = send(sConnect, chatname, sizeof(chatname), NULL);
}
}
else
{
cout << "The connection to the client has been lost... \n" << "please exit the server." <<endl;
break;
}
numClients++;
}
}
}
}
Client:
// ChatServer.cpp : Defines the entry point for the console application.
//
//include of the stdafx.h file where importent files are being included
#include "stdafx.h"
using namespace std;
void smessage()
{
}
//our main function
int main()
{
//here we set the Winsock-DLL to start
string bevestiging;
char chatname[100];
char bericht[250];
char sbericht[250];
string strbericht;
string strsbericht;
long antwoord;
//here the Winsock-DLL will be started with WSAStartup
//version of the DLL
WSAData wsaData;
WORD DLLVERSION;
DLLVERSION = MAKEWORD(2,1);
antwoord = WSAStartup(DLLVERSION, &wsaData);
if(antwoord != 0)
{
exit(1);
}
else
{
cout << "WSA started successfully" <<endl;
cout << "The status: \n" << wsaData.szSystemStatus <<endl;
}
SOCKADDR_IN addr;
int addrlen = sizeof(addr);
SOCKET sConnect;
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
if (sConnect == INVALID_SOCKET)
{
cout << "Error at socket(): \n" << WSAGetLastError() <<endl;
}
else
{
cout << "socket() is OK!\n" <<endl;
}
addr.sin_addr.s_addr = inet_addr("192.168.1.103");
addr.sin_family = AF_INET;
addr.sin_port = htons(4444);
cout << "What is your chat name?" <<endl;
cin.getline(chatname, 100);
cout << "Do you want to connect to the server? [Y/N]" <<endl;
cin >> bevestiging;
if (bevestiging == "N")
{
exit(1);
}
else
{
if(bevestiging == "Y")
{
connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
antwoord = recv(sConnect, bericht, sizeof(bericht), NULL);
strbericht = bericht;
cout << strbericht << chatname <<endl;
while(true)
{
if(antwoord > 1)
{
cin.clear();
cin.sync();
cout << chatname << " :" <<endl;
cin.getline(sbericht, 250);
antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL);
antwoord = send(sConnect, chatname, sizeof(chatname), NULL);
while(antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL) && (antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL)))
{
antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL);
antwoord = recv(sConnect, chatname, sizeof(chatname), NULL);
cout << chatname << ":" <<endl;
cout << sbericht <<endl;
cin.getline(sbericht, 250);
}
}
else
{
cout << "The connection to the server has been lost... \n" << "please exit the client." <<endl;
}
}
}
}
}
You would probably have to open another socket. The client would have to act as a server as well.
First of all: putting a 20mb zip file in to the web for about 4 interesting source files is not a good option. Your object files and debug output is of no interest to us, since we want to help with your source code. Try uploading a zip file containing only the source files the next time.
Secondly: If others want to understand your source code and are not familiar with your native language, they have to guess. Try using english as source code language for this and a variety of other reasons.
Now to answer your question:
The answer is already in your code. Currently, the server is looping until a maximum number of connects, receives input and sends back an answer. So actually you have already implemented it. I guess if you want to send initiated messages in both ways you have to alter your software architecture a bit.
Your code has a few fundamental problems:
The server can only handle one client at a time. If your server will ever have more than a single user on it (as a chat server invariably will), you need to be able to listen for more than one connection at once.
select
, orWSAEventSelect
andWaitForMultipleObjects
, would help a lot here.You assume that a whole fixed-size message will appear at a time. TCP can not guarantee that (as the "stream" concept considers the data as just a potentially infinite sequence of individual bytes), and a half-sent message could freeze up your server while it waits for the rest. Not a big deal if this is all on your LAN, but if you expose this service to the internet, you're asking for random lockups. In order to prevent that, get the data and put it in a buffer as it comes, processing it only when you have a whole message.
The conversation is done in lock-step. That is, the client sends a message, and waits for a response, and then (and only then) expects console input. With this design, there will always be one message received per message sent. In order to get around this, i'll often have a thread for the data going in each direction -- one that gets the console input and sends it to the server, while the other listens to the server and prints the message received. (Note, this means messages could be received while you're typing. That's kinda the point. But it makes console input a bit annoying.) Threading is a semi-advanced topic -- once you start creating new threads, you often have to worry about synchronization and such. But it's generally cleaner than the alternatives in this case.
Sample threaded code (very roughly, since i don't have a C++ compiler handy):
const int MessageLength = 250;
const int NameLength = 250;
char myname[NameLength];
bool sendFully(SOCKET s, char* buffer, size_t buffer_len, int flags)
{
char *end = buffer + buffer_len;
while (buffer != buffer_len)
{
int sent = send(s, buffer, end - buffer, flags);
if (sent == 0) return false;
buffer += sent;
}
return true;
}
DWORD WINAPI watchConsoleInput(void*)
{
char input[MessageLength];
while (true)
{
std::cin.getline(input, MessageLength);
if (!sendFully(sConnect, input, sizeof(input), 0))
break;
if (!sendFully(sConnect, myname, sizeof(myname), 0))
break;
}
return 0;
}
int main()
{
char chatname[NameLength];
char sbericht[MessageLength];
... get our name in myname ...
... do the connect stuff ...
HANDLE watcher = CreateThread(NULL, 0, watchConsoleInput, NULL, 0, NULL);
while (true)
{
// Added MSG_WAITALL to work around the whole-message-at-a-time thing
if (recv(sConnect, sbericht, sizeof(sbericht), MSG_WAITALL) != sizeof(sbericht))
break;
if (recv(sConnect, chatname, sizeof(chatname), MSG_WAITALL) != sizeof(sbericht))
break;
}
// Don't care about errors; we're just being polite
shutdown(sConnect, SD_BOTH);
closesocket(sConnect);
cout << "Connection lost\n";
// ExitProcess rather than just 'return', so we know the watcher thread dies
ExitProcess(0);
}
精彩评论