How to make HTTPS calls (POST requests) using WinHTTP on C++? - c++

I'm writing program on C++ to make calls (POST requests) to Java Servlets using WinHTTP. When I request POST via HTTP everything is OK, problem occurs when I request via HTTPS. It sends empty requests to the server but cuts off request body (but it has content)
int sendPostRequest(char *pszPostData, LPCTSTR servletUrl, char* resultBuffer, ofstream &outputFile) {
outputFile << "====================================== SENDING REEQUEST ======================================" << endl;
HINTERNET hSession = WinHttpOpen(
userAgent,
WINHTTP_ACCESS_TYPE_NO_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
if (!hSession)
{
_tprintf(_TEXT("Failed to open WinHTTP session: %ld\n"), GetLastError());
outputFile << "Failed to open WinHTTP session: %ld\n" << GetLastError() << endl;
return NULL;
}
else {
_tprintf(_TEXT("Oppening WinHTTP session successful: %ld\n"), GetLastError());
outputFile << "Oppening WinHTTP session successful: %ld\n" << GetLastError() << endl;
}
HINTERNET hConnect = WinHttpConnect(
hSession,
serverIP,
serverPort,
0);
if (!hConnect)
{
_tprintf(_TEXT("Failed to connect to server: %ld\n"), GetLastError());
outputFile << "Failed to connect to server: %ld\n" << GetLastError() << endl;
WinHttpCloseHandle(hSession);
return NULL;
}
else {
_tprintf(_TEXT("Connection to server successful: %ld\n"), GetLastError());
outputFile << "Connection to server successful: %ld\n" << GetLastError() << endl;
}
_tprintf(_TEXT("Post data : %ld\n"), pszPostData);
outputFile << "Post data : %ld\n" << pszPostData << endl;
DWORD dwDataLen = strlen(pszPostData);
HINTERNET hRequest = WinHttpOpenRequest(
hConnect,
_TEXT("POST"),
servletUrl,
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_REFRESH);
if (!hRequest)
{
_tprintf(_TEXT("Failed to open request: %ld\n"), GetLastError());
outputFile << "Failed to open request: %ld\n" << GetLastError() << endl;
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
else {
_tprintf(_TEXT("Opening request successful: %ld\n"), GetLastError());
outputFile << "Opening request successful: %ld\n" << GetLastError() << endl;
}
DWORD dwReqOpts = 0;
DWORD dwSize = sizeof(DWORD);
WinHttpSetOption(
hRequest,
WINHTTP_OPTION_SECURITY_FLAGS,
&dwReqOpts,
sizeof(DWORD));
BOOL done = false;
BOOL rc = WinHttpSendRequest(
hRequest,
contentTypeHeader,
-1,
(LPVOID)pszPostData,
dwDataLen,
dwDataLen,
NULL);
if (rc) {
rc = WinHttpReceiveResponse(hRequest, NULL);
_tprintf(_TEXT("Sending request successful: %ld\n"), GetLastError());
outputFile << "Sending request successful: %ld\n" << GetLastError() << endl;
}
else
{
_tprintf(_TEXT("Send request failed: %ld\n"), GetLastError());
outputFile << "Send request failed: %ld\n" << GetLastError() << endl;
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
// Get the status from the server
DWORD dwCode = 0;
if (rc)
{
rc = WinHttpQueryHeaders(
hRequest,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
NULL,
&dwCode,
&dwSize,
NULL);
}
if (dwCode != HTTP_STATUS_OK)
{
_tprintf(_TEXT("HTTP Request failed: %ld\n"), dwCode);
outputFile << "HTTP Request failed: %ld\n" << dwCode << endl;
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
}
else
{
_tprintf(_TEXT("HTTP Request is ok: %ld\n"), dwCode);
outputFile << "HTTP Request is ok : %ld\n" << dwCode << endl;
// Keep reading from the remote server until there's nothing left to read
DWORD dwBytesToBeRead = 0, dwBytesRead = 0;
//char szBuffer[8192] = { 0 };
//strcpy(resultBuffer, "");
do
{
if (!WinHttpQueryDataAvailable(hRequest, &dwBytesToBeRead))
{
_tprintf(_TEXT("No data available from server? %ld"), GetLastError());
outputFile << "No data available from server? %ld" << GetLastError() << endl;
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (!WinHttpReadData(
hRequest,
//szBuffer,
resultBuffer,
//sizeof(szBuffer),
RESULT_BUFFER_SIZE,
&dwBytesRead))
{
_tprintf(_TEXT("Failed to read data from server: %ld"), GetLastError());
outputFile << "Failed to read data from server: %ld" << GetLastError() << endl;
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (dwBytesRead > 0)
{
//szBuffer[dwBytesRead] = 0;
resultBuffer[dwBytesRead] = 0; // NULL-terminated returned buffer
}
} while (dwBytesRead > 0);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return 0;
}
return -1;
}
Where pszPostData - content of request body, servletUrl - url to Servlet (endpoint), resultBuffer - call result will be written to this buffer, outputFile - file for logs.
So how to make HTTPS calls without cutting of request body?

Regarding WinHttpSendRequest method, at the 2nd paramaet insert: L"content-type:application/x-www-form-urlencoded" and on the 3rd paramer -1 which is according to w3.
It should work with this correction.
Also, check the encoding of your .php file, gave me a trouble once.

I came across this today as I was facing a very similar issue. In WinHttpConnect() I was sending pswzServerName as 'example.com'. Problem is, in Apache, I was forcing a redirect on domain.com to www.domain.com and also http to https. This redirect was causing no POST data to be sent, wrong content-length header, and wrong content-type header because I had specified example.com in WinHttpConnect().
Two solutions worked with the last being the best choice :
Removing my htaccess non-www to www and http to https redirect
OR
just changing `pswzServerName` to 'www.example.com' (including the www.) in WinHttpConnect()
Spent a long time trying to figure out why winhttp wasn't working as this was easily overlooked so hopefully it helps someone else that might be in the same boat.

Even if it is an old question, this might be helpful:
Add WINHTTP_FLAG_SECURE to the WinHttpOpenRequest, and it should work.
...
HINTERNET hRequest = WinHttpOpenRequest(
hConnect,
_TEXT("POST"),
servletUrl,
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_REFRESH+WINHTTP_FLAG_SECURE); /// Here
...

Related

WINAPI Read all access rights for a custom file or a folder

I have a task based on an algorithm to receive the access rights for a certain file or folder and I've tried to implement it but some parts don't seem to be clear.
What I've been asked for:
1) Use the function GetNamedSecurityInfo(), for example:
GetNamedSecurityInfo(path,SE_FILE_OBJECT,DACL_SECURITY_INFORMATION,NULL,
NULL,&a,NULL, &pSD)
2) Futher, use an SID to receive the rights: Use
these functions to receive the SID: GetAclInformation(), then GetAce().
3) Now you can use the LookupAccountSid() function and if is was
successfull, compare pACE->Mask with all the constants, for example
"GENERIC_ALL, GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE for files
etc." displaying the access rights.
And how I tried to implement this algorithm: // First getting process SID
PSID g_pSID;
BOOL GetCurrentProcessSID()
{
DWORD dwSize = 0, dwError, dwResult = 0;
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
printf("OpenProcessToken Error %u\n", GetLastError());
return FALSE;
}
// Call GetTokenInformation to get the buffer size.
TOKEN_USER tU;
if (!GetTokenInformation(hToken, TokenUser, &tU, 0, &dwSize))
{
dwError = GetLastError();
if (dwError != ERROR_INSUFFICIENT_BUFFER)
{
std::cout << "GetTokenInformation failed, error " << dwError;
CloseHandle(hToken);
return 0;
}
}
PTOKEN_OWNER to = (PTOKEN_OWNER)LocalAlloc(LPTR, dwSize);
if (!to)
{
dwError = GetLastError();
std::cout << "LocalAlloc failed, error " << dwError;
CloseHandle(hToken);
return 0;
}
if (!GetTokenInformation(hToken, TokenOwner, to, dwSize, &dwSize))
{
dwError = GetLastError();
std::cout << "GetTokenInformation failed, error " << dwError;
LocalFree(to);
CloseHandle(hToken);
return 0;
}
g_pSID = to->Owner;
return TRUE;
}
//Then I used the iteration through the ACL list:
std::stringstream g_TestSecurityResult;
void TestSecurity( wchar_t* path )
{
g_TestSecurityResult = std::stringstream();
GetCurrentProcessSID();
PACL pDacl;
PSECURITY_DESCRIPTOR pSD;
GetNamedSecurityInfoW(path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDacl, NULL, &pSD);
ACL_SIZE_INFORMATION aclSizeInfo = { sizeof(ACL) };
BOOL fResult = GetAclInformation( pDacl, &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), ACL_INFORMATION_CLASS::AclSizeInformation );
if (fResult)
{
for (DWORD dwIndex = 0; dwIndex < aclSizeInfo.AceCount; ++dwIndex)
{
LPVOID pTempAce = nullptr;
fResult = ::GetAce(pDacl, dwIndex, &pTempAce);
if (fResult)
{
PSID pSid = &((ACCESS_ALLOWED_ACE*)pTempAce)->SidStart;
if (EqualSid(pSid, &g_pSID))
{
g_TestSecurityResult << "User: " << userNameFromSid(&g_pSID) << std::endl;
g_TestSecurityResult << "\tAccess mode: " << ((EXPLICIT_ACCESS*)pTempAce)->grfAccessMode << "\n";
g_TestSecurityResult << "\tAccess permissions: " << ((EXPLICIT_ACCESS*)pTempAce)->grfAccessPermissions << "\n";
g_TestSecurityResult << "\tInheritance: " << ((EXPLICIT_ACCESS*)pTempAce)->grfInheritance << "\n";
g_TestSecurityResult << std::endl;
}
}
else
{
g_TestSecurityResult << "GetAce() failed." << GetLastError();
break;
}
}
} else {
g_TestSecurityResult << "Error in GetAclInformation(): " << GetLastError();
}
}
std::string userNameFromSid(PSID userSid)
{
char buffName[MAX_BUFF_SIZE];
DWORD buffNameSize = MAX_BUFF_SIZE;
char buffDomain[MAX_BUFF_SIZE];
DWORD buffDomainSize = MAX_BUFF_SIZE;
SID_NAME_USE SidType;
if (LookupAccountSid(NULL, userSid, buffName, &buffNameSize, buffDomain, &buffDomainSize, &SidType)) {
return buffName;
} else {
DWORD dwResult = GetLastError();
printf("GetTokenInformation Error %u\n", dwResult);
}
/*Here some code to print error in a Message box*/
return "";
}
The problem: The code is working but at the line if (EqualSid(pSid, &g_pSID)) debugger goes through and skips the SID that I've received from my process. In other words, I can't get any information from the ACL list, even though I'm running the process and Visual Studio under Administrator account (not using the "Run as Administrator", and I'll try it but still... The received SID is valid and I can get the name of the process owner using the above function. What could be something that I'm doing wrong here?
Code sources:
https://gist.github.com/m4x1m1l14n/37f39c5d25855c2b1d3a6334851f7549
How to get the logged-on user's SID in Windows
GetTokenInformation, TOKEN_OWNER, и LookupAccountSidA
Thanks to #(Rita Han - MSFT) comment, I've forgotten to remove the ampersand mark from the pSID comparison
EqualSid(pSid, g_pSID);
instead of
EqualSid(pSid, &g_pSID);
And It's properly working for now

winsock2 client cannot send message to server

I am trying to have my client connect to the server (both C++ & winsock2) and do authentication but always failed. The server seems okay because I can telnet to the server and manually type in the password(which is 888) and get the response from server. The telnet session looks like this:
telnet works fine with server
I have search through internet for usage of send and recv but they just don't work here. I have also trying to append "\r" or "\n" after the password and also not working.
Here is the server code:
strcpy_s(sendBuff, "200 Welcome.\r\n");
send(client, sendBuff, strlen(sendBuff), 0);
while (true)
{
memset(rcvBuffer, 0,1024);
input="";
int n;
while ((n = recv(client, rcvBuffer, sizeof(rcvBuffer),0))>0) <<- server keeps waiting here
{
if(strchr(rcvBuffer, '\n') != 0)
break;
if (n>0)
input.append(rcvBuffer, n);
}
if (n == SOCKET_ERROR) {
cout << "Detect client disconnection" << endl;
break;
}
cout <<"Got message from client["<< input << "] sizeof(input) is" <<input.size()<< endl;
if (ParseCmd(input.c_str(), cmd, params)){
if (cmd == "QUIT"||cmd=="quit")
break;
if (cmd == "AUTH"||cmd=="auth"){
for (struct_user u : user) {
if (params == u.passwd){
this_user=u;
auth = true;
strcpy_s(sendBuff, "200 You are logged in.\r\n");
send(client, sendBuff, strlen(sendBuff), 0);
}
}
if (auth != true) {
strcpy_s(sendBuff, "401 Bad password.\r\n");
send(client, sendBuff, strlen(sendBuff), 0);
cout << "Bad Password" << endl;
}
}
if (cmd == "gethsi"||cmd=="GETHSI"){
... some codes here
} // end of gethsi
}
else
{
strcpy_s(sendBuff, "400 Invalid command.\r\n");
send(client, sendBuff, strlen(sendBuff), 0);
cout << "Invalid command" << endl;
}
Here is the client code:
memset(rcvbuff, 0, 512);
rcvResult = recv(ConnectSocket, rcvbuff, 512, 0); //Get welcome message
rcvbuff[rcvResult] = 0;
cout << "rcvbuf [welcome] " << rcvbuff << endl;
if (rcvbuff[0] == '2'&&rcvbuff[1] == '0'&&rcvbuff[2] == '0') {
memset(sendbuff, 0, 512);
strcpy_s(sendbuff, "auth 888");
sendResult = send(ConnectSocket, sendbuff, strlen(sendbuff), 0);
if (sendResult == SOCKET_ERROR) {
std::printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
std::printf("Bytes Sent: %ld\n", sendResult);
memset(rcvbuff, 0, 512);
rcvResult = recv(ConnectSocket, rcvbuff, 512, 0); <<-- keep waiting here
rcvbuff[rcvResult] = 0;
cout << "rcvbuf [authenticate] " << rcvbuff << endl;
The client has received the "200 Welcome" message and send out the "auth 888" message and keep waiting in " rcvResult = recv(ConnectSocket, rcvbuff, 512, 0);" as it has not receive anything from server. Server is waiting in the recv command waiting for input.
I was also thinking appending "\r\n" after the "auth 888" but very strange thing happens. See below picture. The upper half is client and lower half is server:
After appending "\r\n" to send string
Basically the server can only receive null string from the client as the size is 0.
I spend whole day and could not figure out why and hope somebody can help here. Thank you very much in advance.
P.S. I do not have enough reputation to post image. Hope somebody can help to post it for me. Thanks!
Nelson

Sending mouse position over internet

How to send mouse position from client A =to=>SERVER=to=>client B ? Example code below gives me position output every 2 seconds
What is a better/faster way to do this?
NOTICE: using Winsock and cURL gives antivirus malware warning
USAGE: for remote control
Current TEST example of sending mouse position from client A to SERVER to client A:
1.write mouse position
2.store x,y in send.txt file
3.upload sent.txt to server as temp.txt file
4.remove receve.txt if exist //error 80 if not
5.download temp.txt as receve.txt
6.read receve.txt and display coordinates in console
int x,y; //positions
LPCWSTR s=L"C://Documents and Settings//Administrator//Desktop//c++//FTP//send.txt";//location of file for sending
LPCWSTR r=L"C://Documents and Settings//Administrator//Desktop//c++//FTP//receve.txt";//location of received file
POINT cursor_pos;//for cursor position
HINTERNET hInternet;
HINTERNET hFtpSession;
hInternet = InternetOpen(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hInternet == NULL)
{
cout << "Error: " << GetLastError();
}
else
{
hFtpSession = InternetConnect(hInternet, L"www.test.net", INTERNET_DEFAULT_FTP_PORT, L"user", L"pass", INTERNET_SERVICE_FTP, 0, 0);
if (hFtpSession == NULL)//not connect
{
cout << "Error: " << GetLastError();
}
else
{
for(;;){
//file input
fstream inp;
inp.open(s);
GetCursorPos(&cursor_pos);
inp<<cursor_pos.x<<" "<<cursor_pos.y<<endl;//write curent position
inp.close();
//UPLOADING
if (!FtpPutFile(hFtpSession, s, L"//public_html//test//temp.txt", FTP_TRANSFER_TYPE_BINARY, 0))
{
cout << "ErrorPutFile: " << GetLastError();
return 0;
}
remove("C://Documents and Settings//Administrator//Desktop//c++//FTP//receve.txt");//error 80 if file exist so remove it
//DOWNLOADING
if(!FtpGetFile(hFtpSession,L"//public_html//test//temp.txt",r,TRUE,FILE_ATTRIBUTE_NORMAL,FTP_TRANSFER_TYPE_BINARY,0))
{
cout <<"ErrorGetFile"<<GetLastError();
return 0;
}//DELETING
if(!FtpDeleteFile(hFtpSession,L"//public_html//test//temp.txt")){
cout <<"ErrorGetFile"<<GetLastError();
return 0;
}
ifstream outp(r);
while(outp>>x>>y){
cout<<"X: "<<x<<" "<<"Y:"<<y<<endl;//read coordinates
}
outp.close();
}
}
}
return 0;
Thank you for your time :)
You might consider SignalR, it's great for those things. See https://pepitosolis.wordpress.com/2013/12/08/signalr-2-0-persistent-connection-another-example-tracking-your-mouse-pointer/ for an example

Upload Directory

How can I upload a directory using the FtpPutFile function or all the directory this is my code:
void FileSubmit(path ToUpload)
{
HINTERNET hInternet;
HINTERNET hFtpSession;
hInternet = InternetOpen(NULL,INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
if (hInternet == NULL) cout << ("No Internet Connection..\n");
else cout << ("Internet Connection Established\n");
hFtpSession = InternetConnect(hInternet,"host",INTERNET_DEFAULT_FTP_PORT, "user","pass", INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE,0 );
if (!hFtpSession) cout << ("Error in the FTP connection..\n");
else
{
cout <<("FTP Connection Established!\n");
FtpPutFile(hFtpSession, "D://test//*.doc", ToUpload.string().c_str(), FTP_TRANSFER_TYPE_ASCII, INTERNET_FLAG_PASSIVE);
if (!FtpPutFile(hFtpSession, "D://test//*.doc", ToUpload.string().c_str(), FTP_TRANSFER_TYPE_ASCII, INTERNET_FLAG_PASSIVE))
cout <<("File Transfer Failed..\n");
else cout << ("The file was sent..\n");
InternetCloseHandle(hFtpSession);
InternetCloseHandle(hInternet);
}
}
int main()
{
FileSubmit(destination);
return 0;
}
You can't 'upload' directories directly; you would need to create the directory with FtpCreateDirectory() then iterate over all the files in your local directory and call FtpPutFile() on each of them.
If you need a way of getting a list of files in a directory you can use Boost.Filesystem. Look for the directory_iterator and recursive_directory_iterator classes.

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 is 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, or WSAEventSelect and WaitForMultipleObjects, 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);
}