I'm attempting to make a IRC chat bot using Winsock in C++. I am a newb at programming, and am new to programming with sockets.
I am attempting to connect to my Twitch channel. I can make the connection successfully, and pass several buffers (namely, my bot's password or oauth token, user name, and what channel I am trying to join).
However, when I call recv(), there's no data being sent from the Twitch server.
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define DEFAULT_BUFLEN 512
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <WinSock2.h>
#include <Ws2tcpip.h>
#include <cstdlib>
#include <iostream>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
using namespace std;
int main()
{
string Buffer;
char buffers[1024 * 8] = { "0" };
string oauth = "oauthtoken";
string nick = "text_adventure_bot";
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
int iResult;
string hostname = "irc.chat.twitch.tv";
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
WSABUF DataBuf;
WSADATA wsadata;
WORD wVersionRequested;
WORD DllVersion = MAKEWORD(2, 1);
iResult = WSAStartup(MAKEWORD(2, 2), &wsadata);
if(iResult != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
SOCKADDR_IN addr; //the ip
int sizeofaddr = sizeof(addr);
addr.sin_addr.s_addr = inet_addr("52.25.27.117");
addr.sin_port = htons(6667);
addr.sin_family = AF_INET;
SOCKET sock = socket(AF_INET, SOCK_STREAM, NULL);
if (connect(sock, (SOCKADDR*)&addr, sizeofaddr) != 0)
{
cout << "Connection error" << endl;
}
cout << "connected" << endl;
Buffer = "PASS " + oauth;
send(sock, Buffer.c_str(), (int)strlen(Buffer.c_str()), 0);
recv(sock, buffers, 1024 * 8, 0);
cout << Buffer.c_str() << endl << buffers << endl << endl;
Buffer + "NICK " + nick;
send(sock, Buffer.c_str(), strlen(Buffer.c_str()), 0);
recv(sock, buffers, 1024 * 8, 0);
cout << Buffer.c_str() << endl << buffers << endl << endl;
while (true) {
recv(sock, buffers, 1024 * 8, 0);
cout << buffers << endl << endl;
if (buffers[0] == 'PING') {
Buffer = "PONG :" + hostname + "\r\n";
send(sock, Buffer.c_str(), strlen(Buffer.c_str()), 0);
cout << Buffer.c_str() << endl << buffers << endl << endl;
}
}
return 0;
}
When I run this, all I see are my variable being passed, and then an infinite amount of zeros.
There are a number of problems with your code.
You are not checking the return value of socket() for error (I assume you are calling WSAStartup() beforehand, right?).
You are not sending any line breaks at the end of your PASS and NICK commands. IRC is a line-based protocol. That is why you are not getting any data from the server - it is waiting for you to complete your commands first.
various reserved characters in IRC must be escaped.
You are sending the PASS command twice, as you are using the + operator instead of the = operator when setting up your NICK command.
you are not sending any USER and JOIN commands.
You should not be using strlen() to calculate the length of a std::string. It has its own length() and size() methods for that purpose.
You are ignoring the return values of send() and recv(). TCP is a byte stream, but you are not taking into account that send() and recv() can return fewer bytes than requested. You need to call them in a loop until you have sent/receive all of the bytes you are expecting.
Try something more like this instead:
#include <windows.h>
#include <winsock.h>
#include <iostream>
#include <string>
#include <algorithm>
void replaceStr(std::string &str, const std::string &oldStr, const std::string &newStr)
{
std::string::size_type index = 0;
do
{
index = str.find(oldStr, index);
if (index == std::string::npos)
return;
str.replace(index, oldStr.length(), newStr);
index += newStr.length();
}
while (true);
}
std::string quote(const std::string &s)
{
std::string result = s;
replaceStr(result, "\x10", "\x10""\x10");
replaceStr(result, "\0", "\x10""0");
replaceStr(result, "\n", "\x10""n");
replaceStr(result, "\r", "\x10""r");
return result;
}
std::string unquote(const std::string &s)
{
std::string result = s;
std::string::size_type len = result.length();
std::string::size_type index = 0;
while (index < len)
{
index = result.find("\x10", index);
if (index = std::string::npos)
break;
result.erase(index, 1);
--len;
if (index >= len)
break;
switch (result[index])
{
case '0':
result[index] = '\0';
break;
case 'n':
result[index] := '\n';
break;
case 'r':
result[index] = '\r';
break;
}
++index;
}
return result;
}
std::string fetch(std::string &s, const std::string &delim)
{
std::string result;
std::string::size_type pos = s.find(delim);
if (pos == std::string::npos)
{
result = s;
s = "";
}
else
{
result = s.substr(0, pos);
s.erase(0, pos+delim.length());
}
return result;
}
bool sendStr(SOCKET sock, const std::string &s)
{
const char *ptr = s.c_str();
int len = s.length();
while (len > 0)
{
int ret = send(sock, ptr, len, 0);
if (ret == SOCKET_ERROR)
{
std::cout << "send() error: " << WSAGetLastError() << std::endl;
return false;
}
ptr += ret;
len -= ret;
}
return true;
}
bool sendCmd(SOCKET sock, const std::string &cmd)
{
std::cout << "Sending: " << cmd << std::endl;
return sendStr(sock, quote(cmd)) && sendStr(sock, "\r\n");
}
int main()
{
int exitCode = -1;
WSADATA wsa;
int ret = WSAStartup(MAKEWORD(2, 0), &wsa);
if (ret != 0)
{
std::cout << "Winsock init error: " << ret << std::endl;
goto done;
}
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
{
std::cout << "socket() error: " << WSAGetLastError() << std::endl;
goto done;
}
SOCKADDR_IN addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("52.25.27.117"); //the ip
addr.sin_port = htons(6667);
if (connect(sock, (SOCKADDR*)&addr, sizeof(addr)) != 0)
{
std::cout << "connect() error: " << WSAGetLastError() << std::endl;
goto cleanup:
}
std::cout << "connected" << std::endl;
std::string oauth = ...;
std::string nick = ...;
std::string user = ...;
std::string channel = ...;
sendCmd("PASS " + oauth);
sendCmd("NICK " + nick);
sendCmd("USER " + user);
sendCmd("JOIN " + channel);
char buf[1024];
std::string LineBuffer;
std::string::size_type StartIdx = 0;
do
{
int ret = recv(sock, buf, sizeof(buf), 0);
if (ret == SOCKET_ERROR)
{
std::cout << "recv() error: " << WSAGetLastError() << std::endl;
goto cleanup;
}
if (ret == 0)
{
std::cout << "Server disconnected" << std::endl;
break;
}
LineBuffer.append(buf, ret);
do
{
std::string::size_type pos = LineBuffer.find('\n', StartIdx);
if (pos == std::string::npos)
break;
std::string::size_type len = pos;
if ((pos > 0) && (LineBuffer[pos-1] == '\r'))
--len;
std::string msg = unquote(LineBuffer.substr(0, len));
LineBuffer.erase(0, pos+1);
StartIdx = 0;
std::string senderNick;
std::string senderHost;
if (!msg.empty() && (msg[0] == ':'))
{
std::string tmp = fetch(msg, " ");
tmp.erase(0, 1); // remove ':'
senderNick = fetch(tmp, "!");
senderHost = tmp;
}
std::cout << "Received: " << msg << std::endl;
if (msg == "PING")
sendCmd("PONG :" + hostname);
}
while(true);
}
while (true);
exitCode = 0;
cleanup:
closesocket(sock);
done:
return exitCode;
}
First, you ignore the return value of recv, so you have no idea how many bytes you received.
Second, nowhere did you actually implement the IRC message protocol (see section 2.3 of RFC1459). So you have no reason to assume that the first byte of your buffer will contain the first byte of the IRC protocol message. Only an actual implementation of the IRC message protocol can produce a buffer whose first byte is the first byte of the IRC message.
Similarly, you can't do this:
cout << buffers << endl << endl;
The operator<<(const char *) for a stream needs a pointer to a C-style string. Until you parse the data you received from the TCP connection and produce a C-style string from it, you must not treat it as a C-style string.
Also:
if (buffers[0] == 'PING') {
You really meant PING as a multi-byte character constant? There is no multibyte character named PING so far as I know. And, in any event, an IRC server sends the literal four character string "PING", not a single 'PING' character.
Related
I am newbie in programming world. I am trying to make Login system with C++ so I try to connect MySQL database with my server program. without using socket I can connect with database well. But my Final Goal is to success login to my local server and add information to my DB.
I found that if I add "driver.h" from MySQL Connector 8.0, It said the connection is gone. actually it cant connect to socket. but when I comment out the "driver.h" hearder, It connect to socket well. but without "driver.h", I cant connect to my DB.
What is the problem and How can I solve it? the version of VS is 2022, and MySQL version is 8.0.30. Below is my full Code of Client and Server.
And sorry I cant emebed image still I am newbie here
[So I post in Notion][1]:https://sunny-green-970.notion.site/Error-ea96fbef0ea64e1bb1fd966699657547
Client
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <WinSock2.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSAData wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET ServerSocket = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN ServerAddrIn;
memset(&ServerAddrIn, 0, sizeof(SOCKADDR_IN));
ServerAddrIn.sin_family = PF_INET;
ServerAddrIn.sin_addr.s_addr = inet_addr("127.0.0.1");
ServerAddrIn.sin_port = htons(1234);
printf("client\n");
if (connect(ServerSocket, (SOCKADDR*)&ServerAddrIn, sizeof(SOCKADDR_IN)) == SOCKET_ERROR) // 자기 ip와 port 필요없다. 서버에 접속하면 된다.
{
std::cout << "Connect Failed." << std::endl;
exit(-1);
}
int Numnber = 0;
int WinCount = 0;
int TotalGamePlay = 0;
//connect(ServerSocket, (SOCKADDR*)&ServerAddrIn, sizeof(SOCKADDR_IN));
printf("Please Type Number: ");
scanf("%d", &Numnber);
printf("Please Type WinCount: ");
scanf("%d", &WinCount);
printf("Please Type TotalGamePlay: ");
scanf("%d", &TotalGamePlay);
char Buffer[1024] = { 0, };
memcpy(Buffer, &Numnber, 4);
memcpy(&Buffer[4], &WinCount, 4);
memcpy(&Buffer[8], &TotalGamePlay, 4);
int SentLength = send(ServerSocket, Buffer, 12, 0);
printf("Sent Length : %d\n", SentLength);
if (SentLength == 0)
{
//연결 종료s
std::cout << "Disconnected Client." << std::endl;
}
else if (SentLength < 0)
{
//Error
std::cout << "Disconnected Client By Error : " << GetLastError() << std::endl;
}
WSACleanup();
return 0;
}
Server
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <iostream>
#include <WinSock2.h>
#include <Windows.h>
#include "jdbc/mysql_connection.h"
#include "jdbc/cppconn/exception.h"
#include "jdbc/cppconn/resultset.h"
#include "jdbc/cppconn/statement.h"
#include "jdbc/cppconn/prepared_statement.h"
#include "jdbc/cppconn/driver.h"
#pragma comment(lib, "ws2_32.lib")
#ifdef _DEBUG
#pragma comment(lib, "debug/mysqlcppconn.lib")
#else
#pragma comment(lib, "mysqlcppconn.lib")
#endif
using namespace std;
std::string Utf8ToMultiByte(std::string utf8_str);
int main()
{
bool bRunning = true;
WSAData wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET ServerSocket = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN ServerAddrIn;
memset(&ServerAddrIn, 0, sizeof(SOCKADDR_IN));
ServerAddrIn.sin_family = AF_INET;
ServerAddrIn.sin_port = htons(1234);
ServerAddrIn.sin_addr.s_addr = htonl(INADDR_ANY);
fd_set Original;
fd_set CopyReads;
timeval Timeout;
Timeout.tv_sec = 0;
Timeout.tv_usec = 10;
FD_ZERO(&Original);
FD_SET(ServerSocket, &Original);
bind(ServerSocket, (SOCKADDR*)&ServerAddrIn, sizeof(SOCKADDR_IN));
listen(ServerSocket, 0);
printf("Server\n");
while (bRunning)
{
CopyReads = Original;
//polling
int fd_num = select(0, &CopyReads, 0, 0, &Timeout);
if (fd_num == 0)
{
continue;
}
if (fd_num == SOCKET_ERROR)
{
bRunning = false;
break;
}
for (size_t i = 0; i < Original.fd_count; ++i)
{
//등록한 소켓 리스트 중에 이벤트 발생 했음
if (FD_ISSET(Original.fd_array[i], &CopyReads))
{
if (Original.fd_array[i] == ServerSocket)
{
//서버 소켓에 이벤트가 발생함, 연결 요청
SOCKADDR_IN ClientSockAddrIn;
memset((char*)&ClientSockAddrIn, 0, sizeof(SOCKADDR_IN));
int ClientSockAddrInSize = sizeof(SOCKADDR_IN);
SOCKET ClientSocket = accept(ServerSocket, (SOCKADDR*)&ClientSockAddrIn, &ClientSockAddrInSize);
FD_SET(ClientSocket, &Original);
std::cout << "Connected client : " << ClientSocket << std::endl;
}
else
{
SOCKET ClientSocket = Original.fd_array[i];
//Client 소켓, Recv, Send
char Buffer[1024] = { 0, };
//blocking, non blocking
//packet 설계, protocol
int RecvLength = recv(ClientSocket, Buffer, sizeof(Buffer), 0);
if (RecvLength == 0)
{
//연결 종료
std::cout << "Disconnected Client." << std::endl;
FD_CLR(ClientSocket, &Original);
closesocket(ClientSocket);
}
else if (RecvLength < 0)
{
//Error
std::cout << "Disconnected Client By Error : " << GetLastError() << std::endl;
FD_CLR(ClientSocket, &Original);
closesocket(ClientSocket);
}
else
{
//자료가 있으면 처리
//packet parse
int number = 0;
int wincount = 0;
int totalgameplay = 0;
memcpy(&number, &buffer[0], 4);
memcpy(&wincount, &buffer[4], 4);
memcpy(&totalgameplay, &buffer[8], 4);
try
{
sql::driver* driver; //workbench
sql::connection* connection; //접속
sql::statement* statement; //query
sql::resultset* resultset; //결과화면
sql::preparedstatement* preparedstatement;
//work벤치 켜기
driver = get_driver_instance();
//연결 버튼 누르기
connection = driver->connect("tcp://127.0.0.1:3306", "root", "secret");
if (connection == nullptr)
{
cout << "connect failed" << endl;
exit(-1);
}
사용 데이터베이스 선정(use)
connection->setschema("logindata");
preparedstatement = connection->preparestatement("insert into new_table(number, wincount, totalgameplay) values( ?, ? ,?)");
preparedstatement->setint(1, number);
preparedstatement->setint(2, wincount);
preparedstatement->setint(3, totalgameplay);
preparedstatement->executeupdate();
statement = connection->createstatement();
resultset = statement->executequery("select * from new_table ");
for (;resultset->next();)
{
cout << resultset->getint("number") << " : " <<
utf8tomultibyte(resultset->getstring("wincount")) << " : " <<
utf8tomultibyte(resultset->getstring("totalgameplay")) << endl;
}
delete resultset;
delete preparedstatement;
delete connection;
}
catch (sql::sqlexception e)
{
cout << "# err: sqlexception in " << __file__;
cout << "(" << __function__ << ") on line " << __line__ << endl;
cout << "# err: " << e.what();
cout << " (mysql error code: " << e.geterrorcode();
cout << ", sqlstate: " << e.getsqlstate() << " )" << endl;
}
/*for (size_t i = 0; i < original.fd_count; ++i)
{
if (original.fd_array[i] != serversocket)
{
std::cout << original.fd_array[i] << std::endl;
int sentlength = send(original.fd_array[i], buffer, 8, 0);
}
}*/
}
}
}
}
}
closesocket(ServerSocket);
WSACleanup();
return 0;
}
std::string Utf8ToMultiByte(std::string utf8_str)
{
std::string resultString; char* pszIn = new char[utf8_str.length() + 1];
strncpy_s(pszIn, utf8_str.length() + 1, utf8_str.c_str(), utf8_str.length());
int nLenOfUni = 0, nLenOfANSI = 0; wchar_t* uni_wchar = NULL;
char* pszOut = NULL;
// 1. utf8 Length
if ((nLenOfUni = MultiByteToWideChar(CP_UTF8, 0, pszIn, (int)strlen(pszIn), NULL, 0)) <= 0)
return nullptr;
uni_wchar = new wchar_t[nLenOfUni + 1];
memset(uni_wchar, 0x00, sizeof(wchar_t) * (nLenOfUni + 1));
// 2. utf8 --> unicode
nLenOfUni = MultiByteToWideChar(CP_UTF8, 0, pszIn, (int)strlen(pszIn), uni_wchar, nLenOfUni);
// 3. ANSI(multibyte) Length
if ((nLenOfANSI = WideCharToMultiByte(CP_ACP, 0, uni_wchar, nLenOfUni, NULL, 0, NULL, NULL)) <= 0)
{
delete[] uni_wchar; return 0;
}
pszOut = new char[nLenOfANSI + 1];
memset(pszOut, 0x00, sizeof(char) * (nLenOfANSI + 1));
// 4. unicode --> ANSI(multibyte)
nLenOfANSI = WideCharToMultiByte(CP_ACP, 0, uni_wchar, nLenOfUni, pszOut, nLenOfANSI, NULL, NULL);
pszOut[nLenOfANSI] = 0;
resultString = pszOut;
delete[] uni_wchar;
delete[] pszOut;
return resultString;
}
[1]: https://sunny-green-970.notion.site/Error-ea96fbef0ea64e1bb1fd966699657547
[2]: https://i.stack.imgur.com/VK8m0.png
A third question in the saga: How to correctly implement select to correctly get data from stdin and recv(). I recommend reading this and the other question it links to understand the situation.
Basically, I tried my luck at implementing select() myself. My code:
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
using namespace std;
int main (int argc, char** argv) {
if (argv[1] == NULL) {
cout << "\033[31mTARGET NOT SPECIFIED - TERMINATING...\033[0m\n";
return -1;
}
if (argv[2] == NULL) {
cout << "\033[31mPORT NOT SPECIFIED - TERMINATING...\033[0m\n";
return -2;
}
string target = argv[1];
int port = atoi(argv[2]);
cout << "GENERATING SOCKET...\n";
int chatter = socket(AF_INET, SOCK_STREAM, 0);
if (chatter == -1) {
cout << "\033[31mSOCKET GENERATION FAILURE - TERMINATING...\033[0m\n";
return -3;
}
cout << "\033[32mSUCCESSFULLY GENERATED SOCKET\033[0m\n";
struct sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, target.c_str(), &hint.sin_addr);
struct timeval tv;
tv.tv_usec = 0.0;
tv.tv_sec = 5;
int recval;
cout << "CONNECTING TO " << target << " AT PORT " << port << "...\n";
int connection_status = connect(chatter, (sockaddr*)&hint, sizeof(hint));
if (connection_status == -1) {
cout << "\033[31mCONNECTION FAILURE - TERMINATING...\033[0m\n";
return -4;
}
cout << "\033[32mCONNECTED TO HOST\033[0m\n";
char buf[4096] = {0};
string msg;
while (true) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(chatter, &rfds);
getline(cin, msg);
msg+"\r\n";
int sendmsg = send(chatter, msg.c_str(), msg.size()+1, 0);
if (sendmsg == -1) {
cout << "\033[31mMESSAGE SENDING FAILURE - TERMINATING...\033[0m\n";
return -5;
}
recval = select(chatter + 1, &rfds, NULL, NULL, &tv);
switch(recval) {
case(0):
cout << "\033[31mTIMEOUT\033[0m\n";
break;
case(-1):
cout << "\033[31mERROR\033[0m\n";
break;
default:
if (recv(chatter, buf, 4096, 0) < 0) {
cout << "\033[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...\033[0m\n";
return -6;
} else {
cout << recv(chatter, buf, 4096, 0) << "\n";
cout << buf << "\n";
}
break;
}
}
close(chatter);
return 0;
}
I keep getting TIMEOUT when trying the program on scanme.nmap.org and my HTTP server. What am I doing wrong?
At this point, after fixing something a user in the first question pointed out, I know that there isn't an issue with how I'm sending data. Just an issue with the way the program handles getting data from getline()/recv().
EDIT: NEW, IMPROVED, WORKING CODE THANKS TO ANSWERER
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
using namespace std;
int main (int argc, char** argv) {
if (argv[1] == NULL) {
cout << "\033[31mTARGET NOT SPECIFIED - TERMINATING...\033[0m\n";
return -1;
}
if (argv[2] == NULL) {
cout << "\033[31mPORT NOT SPECIFIED - TERMINATING...\033[0m\n";
return -2;
}
string target = argv[1];
int port = atoi(argv[2]);
cout << "GENERATING SOCKET...\n";
int chatter = socket(AF_INET, SOCK_STREAM, 0);
if (chatter == -1) {
cout << "\033[31mSOCKET GENERATION FAILURE - TERMINATING...\033[0m\n";
return -3;
}
cout << "\033[32mSUCCESSFULLY GENERATED SOCKET\033[0m\n";
struct sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, target.c_str(), &hint.sin_addr);
int recval;
cout << "CONNECTING TO " << target << " AT PORT " << port << "...\n";
int connection_status = connect(chatter, (sockaddr*)&hint, sizeof(hint));
if (connection_status == -1) {
cout << "\033[31mCONNECTION FAILURE - TERMINATING...\033[0m\n";
return -4;
}
cout << "\033[32mCONNECTED TO HOST\033[0m\n";
char buf[4096] = {0};
string msg;
while (true) {
struct timeval tv;
tv.tv_usec = 0.0;
tv.tv_sec = 5;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(chatter, &rfds);
getline(cin, msg);
msg += "\r\n";
const char *pMsg = msg.c_str();
size_t msgSize = msg.size();
do {
int numSent = send(chatter, pMsg, msgSize, 0);
if (numSent == -1) {
cout << "\033[31mMESSAGE SENDING FAILURE - TERMINATING...\033[0m\n";
close(chatter);
return -5;
}
pMsg += numSent;
msgSize -= numSent;
} while (msgSize > 0);
recval = select(chatter + 1, &rfds, NULL, NULL, &tv);
switch(recval) {
case(0):
cout << "\033[31mTIMEOUT\033[0m\n";
break;
case(-1):
cout << "\033[31mERROR\033[0m\n";
break;
default:
int numRead = recv(chatter, buf, 4096, 0);
if (numRead < 0) {
cout << "\033[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...\033[0m\n";
close(chatter);
return -6;
}
else if (numRead == 0) {
cout << "\033[31mDISCONNECTED - TERMINATING...\033[0m\n";
close(chatter);
break;
} else {
cout << numRead << "\n";
cout.write(buf, numRead);
cout << "\n";
}
break;
}
}
close(chatter);
return 0;
}
On some platforms, select() alters the passed timeval to indicate how much time is remaining. So this is likely the cause of your timeout errors, as you are setting the timeval only once and it will eventually fall to 0. You need to reset your tv variable every time you call select(), so move that inside your while loop.
Also, you have 2 calls to recv() where you should be using only 1 call. You are ignoring the bytes received by the 1st recv(), and if the server happens to send less then 4096 bytes then there won't be any data left for the next call to select() to detect, unless the connection is disconnected.
Change this:
if (recv(chatter, buf, 4096, 0) < 0) {
cout << "\033[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...\033[0m\n";
return -6;
} else {
cout << recv(chatter, buf, 4096, 0) << "\n";
cout << buf << "\n";
}
To this:
int numRead = recv(chatter, buf, 4096, 0);
if (numRead < 0) {
cout << "\033[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...\033[0m\n";
return -6;
}
else if (numRead == 0) {
cout << "\033[32mHOST DISCONNECTED\033[0m\n";
break;
} else {
cout << numRead << "\n";
cout.write(buf, numRead);
cout << "\n";
}
Also, msg+"\r\n"; is a no-op, you probably meant to use msg += "\r\n"; instead.
And, you should not be including the msg's null terminator when calling send(). And you are not accounting for the possibility that send() may not be able to send the whole data in one go. You need to call send() in a loop instead, eg:
const char *pMsg = msg.c_str();
size_t msgSize = msg.size();
do {
int numSent = send(chatter, pMsg, msgSize, 0);
if (numSent == -1) {
cout << "\033[31mMESSAGE SENDING FAILURE - TERMINATING...\033[0m\n";
return -5;
}
pMsg += numSent;
msgSize -= numSent;
}
while (msgSize > 0);
I am attempting to make a very simple UDP client / server setup with non-blocking sockets. I've isolated my problem to the server not binding to the requested Port number and instead binding to one arbitrarily (though it does seem to consistently be the same port).
Is it possible to request a specific port number?
I am attempting to not be IPv4 or IPv6 specific. So I'm not sure if I need to use getaddrinfo if I don't care about the IP address, but I thought that this way it would find whatever was available, or better yet, listen to all IP addresses coming into this port? But it's reporting :: which causes me a problem if I try to tell my clients later what IP address to connect to.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Ws2tcpip.h>
#include <cstring>
#include <iostream>
#include <chrono>
#include <iomanip>
#include <sstream>
static uint16_t SERVER_PORT = 56000;
std::wstring get_error_string(const DWORD error)
{
std::wstring returnResult = L"";
LPWSTR errorText = NULL;
FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&errorText,
0,
NULL);
if(NULL != errorText)
{
returnResult = errorText;
LocalFree(errorText);
errorText = NULL;
}
std::time_t t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::tm tm = *std::localtime(&t);
std::wstringstream sstr;
sstr << std::put_time<wchar_t>(&tm, L"%d%m%y %H:%M:%S");
return sstr.str() + L" ERROR (" + std::to_wstring(error) + L"): " + returnResult;
}
std::string convert_to_string(const sockaddr* addr)
{
char* str;
if(addr->sa_family == AF_INET)
{
str = new char[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(reinterpret_cast<const sockaddr_in*>(addr)->sin_addr), str, INET_ADDRSTRLEN);
}
else
{
str = new char[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_addr), str, INET6_ADDRSTRLEN);
}
std::string returnVal = str;
if(addr->sa_family == AF_INET)
returnVal += ":" + std::to_string(reinterpret_cast<const sockaddr_in*>(addr)->sin_port);
else
returnVal = "[" + returnVal + "]:" + std::to_string(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_port);
return returnVal;
}
int main()
{
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2,2), &wsaData);
if(result != 0)
{
std::wcerr << L"Network init failed: ";
switch(result)
{
case WSASYSNOTREADY:
std::wcerr << L"WSASYSNOTREADY";
break;
case WSAVERNOTSUPPORTED:
std::wcerr << L"WSAVERNOTSUPPORTED";
break;
case WSAEINPROGRESS:
std::wcerr << L"WSAEINPROGRESS";
break;
case WSAEPROCLIM:
std::wcerr << L"WSAEPROCLIM";
break;
case WSAEFAULT:
std::wcerr << L"WSAEFAULT";
break;
default:
std::wcerr << L"Unknown error code " << std::to_wstring(result);
}
std::wcerr << std::endl;
return 0;
}
addrinfo hints = {};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
addrinfo* serverInfoList;
int status = getaddrinfo(nullptr, std::to_string(SERVER_PORT).c_str(), &hints, &serverInfoList);
if(status != 0)
{
int lastError = WSAGetLastError();
std::wcerr << L"ERROR getaddrinfo: " << get_error_string(lastError) << std::endl;
}
addrinfo* addr = nullptr;
SOCKET sock;
for(addr = serverInfoList; addr != nullptr; addr = addr->ai_next)
{
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if(sock == INVALID_SOCKET)
{
int lastError = WSAGetLastError();
std::wcerr << L"ERROR socket: " << get_error_string(lastError) << std::endl;
continue; // we'll try the next option
}
u_long blocking = 1;
ioctlsocket(sock, FIONBIO, &blocking);
BOOL conrest = FALSE;
DWORD dwBytesReturned = 0;
WSAIoctl(sock, _WSAIOW(IOC_VENDOR, 12), &conrest, sizeof(conrest), NULL, 0, &dwBytesReturned, NULL, NULL);
int flag = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&flag), (socklen_t)sizeof(flag));
if(bind(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
{
int lastError = WSAGetLastError();
std::wcerr << L"ERROR bind: " << get_error_string(lastError) << std::endl;
if(closesocket(sock) == SOCKET_ERROR)
{
lastError = WSAGetLastError();
std::wcerr << L"ERROR closesocket: " << get_error_string(lastError) << std::endl;
}
continue;
}
sockaddr_storage connectedDetails;
std::memset(&connectedDetails, 0, sizeof(connectedDetails));
int addrLen = sizeof(connectedDetails);
if(getsockname(sock, reinterpret_cast<sockaddr*>(&connectedDetails), &addrLen) == SOCKET_ERROR)
{
int lastError = WSAGetLastError();
std::wcerr << L"ERROR getsockname: " << get_error_string(lastError) << std::endl;
}
std::cout << "Connected to " << convert_to_string(reinterpret_cast<sockaddr*>(&connectedDetails)) << std::endl;
break; // we've bound to one of them
}
if(addr == nullptr)
{
std::wcerr << L"ERROR: Could not bind to any addr for localhost port \"" << std::to_wstring(SERVER_PORT) << "\"" << std::endl;
return false;
}
freeaddrinfo(serverInfoList);
while(!(GetAsyncKeyState(VK_ESCAPE) & 0x8000))
{
}
result = WSACleanup();
if(result != 0)
{
std::wcerr << L"Network cleanup failed: ";
switch(result)
{
case WSANOTINITIALISED:
std::wcerr << L"WSANOTINITIALISED";
break;
case WSAENETDOWN:
std::wcerr << L"WSAENETDOWN";
break;
case WSAEINPROGRESS:
std::wcerr << L"WSAEINPROGRESS";
break;
default:
std::wcerr << L"Unknown error code " << std::to_wstring(result);
}
std::wcerr << std::endl;
}
return 0;
}
I'm expecting it to bind to port 56000 but it binds to other ports.
The easiest way is to build the sockaddr_in yourself, since you want to bind to all addresses.
// Create IPv6 UDP socket
X = socket(AF_INET6,SOCK_DGRAM,IPPROTO_UDP);
// Allow both ipv6/ipv4 bind
DWORD ag = 0;
setsockopt(X,IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ag, sizeof(DWORD));
sockaddr_in s = {0};
s.sin_port = htons(SERVER_PORT);
s.sin_family= AF_INET6;
if (bind(X,(sockaddr*)&s,sizeof(s)) == 0) { ... }
// In server.cpp after connection has established
std::string input;
input.reserve(5);
std::cout << "Enter message to send: ";
std::cin.ignore(); // =====(1)=====
std::getline(std::cin, input);
std::cout << "Sending..." << std::endl;
auto len = input.length();
auto bytes_sent = send(newFD, input.data(), len, 0); // =====(2)=====
std::cout << "Input length : " << input.length() << std::endl
<< "Input bytes sent : " << bytes_sent << std::endl;
My aim is to use std::string instead of plain old char[fixed] in simple tcp client server program. So in server.cpp I have 2 doubts. So far my initial guesses are working as expected. I've marked them above in code.
cin.ignore() vs cin.clear() + cin.sync()
std::string.data() vs std::string.c_str()
Which should I use? I'm not even sure of the difference b/w any of these and I don't know if they're contributing to my problem.
// In client.cpp
std::string message;
message.reserve(5);
auto len = message.capacity();
auto bytes_recv = recv(sockFD, &message.front(), len - 1, 0); // =====(1)=====
message[len] = 0; // =====(2)=====
close(sockFD);
freeaddrinfo(res);
std::cout << "Bytes recieved :" << bytes_recv << std::endl;
std::cout << message.c_str() << std::endl; // =====(3)=====
And in client.cpp, everything goes wrong when I try to send bigger strings. But I probably know the cause, however solution is somewhat tricky to implement.
Am I doing right thing to pass &std::string.front() to write incoming data?
This is wrong, string class should manage this, right? However since I'm directly writing to &front(), I guess length won't get updated or I'm not sure what happens but data surely gets lost when outputting with std::cout << message;.
I'm doing this just because I'm directly writing to &front, still it produces garbage if somehow data returned is smaller than total length, probably because it cannot find terminating character at right place?
server.cpp
// compile as 'g++ server.cpp -o server.app -std=c++14'
// run as : './server.app 8080'
#include <iostream>
#include <string>
#include <cstring>
extern "C" {
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
}
int main(int argc, char *argv[])
{
if(argc != 2) {
std::cerr << "Run program as 'program port'" << std::endl;
return -1;
}
auto &portNum = argv[1];
const unsigned int backLog = 5;
struct addrinfo hints, *res, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int gAddRes = getaddrinfo(NULL, portNum, &hints, &res);
if(gAddRes != 0) {
std::cerr << gai_strerror(gAddRes) << std::endl;
return -2;
}
std::cout << "Detecting addresses" << std::endl;
unsigned int numOfAddr = 0;
char ipStr[INET6_ADDRSTRLEN];
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
std::string ipVer = "IPv0";
if(p->ai_family == AF_INET) {
ipVer = "IPv4";
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
++numOfAddr;
}
else {
ipVer = "IPv6";
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
++numOfAddr;
}
inet_ntop(p->ai_family, addr, ipStr, sizeof(ipStr));
std::cout << "(" << numOfAddr << ") " << ipVer << " : " << ipStr
<< std::endl;
}
if(!numOfAddr) {
std::cerr << "Found no host address to use" << std::endl;
return -3;
}
std::cout << "Enter the number of host address to bind with:" << std::endl;
unsigned int choice = 0;
bool madeChoice = false;
do {
std::cin >> choice;
if(choice > (numOfAddr + 1) || choice < 1) {
madeChoice = false;
std::cout << "Wrong choice, try again!" << std::endl;
}
else
madeChoice = true;
} while(!madeChoice);
p = res;
bool isIPv4 = true;
if(choice > 1) {
unsigned int temp = 1;
while(choice < temp) {
p = p->ai_next;
++temp;
}
if(p->ai_family == AF_INET) {
isIPv4 = true;
}
else
isIPv4 = false;
}
int sockFD = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(sockFD == -1) {
std::cerr << "Error while creating socket" << std::endl;
freeaddrinfo(res);
return -4;
}
int bindR = bind(sockFD, p->ai_addr, p->ai_addrlen);
if(bindR == -1) {
std::cerr << "Error while binding socket" << std::endl;
close(sockFD);
freeaddrinfo(res);
return -5;
}
int listenR = listen(sockFD, backLog);
if(listenR == -1) {
std::cerr << "Error while Listening on socket" << std::endl;
close(sockFD);
freeaddrinfo(res);
return -6;
}
struct sockaddr_storage client_addr;
socklen_t client_addr_size = sizeof(client_addr);
int newFD =
accept(sockFD, (struct sockaddr *)&client_addr, &client_addr_size);
if(newFD == -1) {
std::cerr << "Error while Accepting on socket" << std::endl;
close(sockFD);
freeaddrinfo(res);
return -7;
}
std::string input;
input.reserve(5);
std::cout << "Enter message to send: ";
std::cin.ignore();
std::getline(std::cin, input);
std::cout << "Sending..." << std::endl;
auto len = input.length();
auto bytes_sent = send(newFD, input.data(), len, 0);
std::cout << "Input length : " << input.length() << std::endl
<< "Input bytes sent : " << bytes_sent << std::endl;
close(newFD);
close(sockFD);
freeaddrinfo(res);
return 0;
}
client.cpp
// compile as 'g++ client.cpp -o client.app -std=c++14'
// run as : './client.app 0 8080'
#include <iostream>
#include <cstring>
extern "C" {
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
}
int main(int argc, char *argv[])
{
if(argc != 3) {
std::cerr << "Run program as 'program ipaddress port'" << std::endl;
return -1;
}
auto &ipAddress = argv[1];
auto &portNum = argv[2];
struct addrinfo hints, *res, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int gAddRes = getaddrinfo(ipAddress, portNum, &hints, &res);
if(gAddRes != 0) {
std::cerr << gai_strerror(gAddRes) << std::endl;
return -2;
}
std::cout << "Detecting addresses" << std::endl;
unsigned int numOfAddr = 0;
char ipStr[INET6_ADDRSTRLEN];
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
std::string ipVer = "IPv0";
if(p->ai_family == AF_INET) {
ipVer = "IPv4";
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
++numOfAddr;
}
else {
ipVer = "IPv6";
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
++numOfAddr;
}
inet_ntop(p->ai_family, addr, ipStr, sizeof(ipStr));
std::cout << "(" << numOfAddr << ") " << ipVer << " : " << ipStr
<< std::endl;
}
if(!numOfAddr) {
std::cerr << "Found no host address to use" << std::endl;
return -3;
}
std::cout << "Enter the number of host address to bind with:" << std::endl;
unsigned int choice = 0;
bool madeChoice = false;
do {
std::cin >> choice;
if(choice > (numOfAddr + 1) || choice < 1) {
madeChoice = false;
std::cout << "Wrong choice, try again!" << std::endl;
}
else
madeChoice = true;
} while(!madeChoice);
p = res;
bool isIPv4 = true;
if(choice > 1) {
unsigned int temp = 1;
while(choice < temp) {
p = p->ai_next;
++temp;
}
if(p->ai_family == AF_INET) {
isIPv4 = true;
}
else
isIPv4 = false;
}
int sockFD = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(sockFD == -1) {
std::cerr << "Error while creating socket" << std::endl;
return -4;
}
int connectR = connect(sockFD, p->ai_addr, p->ai_addrlen);
if(connectR == -1) {
close(sockFD);
std::cerr << "Error while connecting socket" << std::endl;
return -5;
}
std::string message;
message.reserve(5);
auto len = message.capacity();
auto bytes_recv = recv(sockFD, &message.front(), len - 1, 0);
message[len] = 0;
close(sockFD);
freeaddrinfo(res);
std::cout << "Bytes recieved :" << bytes_recv << std::endl;
std::cout << message.c_str() << std::endl;
return 0;
}
Your question on this is too vague to provide a helpful answer.
data() and c_str() are effectively the same thing since C++11. It doesn't matter which one you use. EDIT: In C++17, data() will have a non-const overload that returns a non-const char*, so you will not need to do &message.front() to access a modifiable form of the underlying buffer. c_str() will remain const.
&message.front() is right... and wrong. That is the way to get a non-const char* to the contents of your std::string. BUT message is uninitialized and has a size() of 0 at that point in the code, so I'm not even sure that line of code is well-defined behavior. Rather than doing a reserve(5) I would construct your string like this: auto message = std::string(5, ' '); Then when you pass it into recv there will actually be valid stuff there for it to overwrite and you'll be able to read it from message afterwards.
Yes, this is wrong. You should be setting your string up to be the actual size you need. I suspect you can just pass in len instead of len - 1 if you do this. On this topic, are you certain everything you'll ever receive is only 4 bytes long? Or are you intentionally only reading 4 bytes at a time?
a) you don't need to pass c_str() to std::cout. << is overloaded to accept std::string as well. b) recv returns the number of bytes that you received. If that value is less than the size you initialized your message to, then the remaining characters in your string will be garbage (or ' ' chars if you followed my advice re:#3). I would do message.resize(bytes_recv); after receiving the message.
Your questions have been addressed by caps,, but my 2 cents. What about having your own send/recv functions and hiding the complexity?
For example along the lines of:
ssize_t recv(int sockfd, std::string &buf, size_t len, int flags) {
buf.resize(len); // current status unknown -> make it fit
ssize_t n = ::recv(sockfd, (void *)buf.data(), len, flags);
buf.resize(n >= 0 ? n : 0); // take error into account
return n;
}
In the following lines of code, I am taking a request line (GET or HEAD <filename> HTTP/1.0), and get the information about the file.
This is working correctly for html/text files and image/gif files.
However, when I put a directory in , the program tells it is a socket, or unknown, while it is supposed to tell it is a directory.
It is strange because I copied the code from UNIX man example, and the example code in man recognizes the directory when I put directory as the argument.
Only difference is that my program takes the file(or directory) name during execution from the user input(client-server), while the UNIX program takes the name from the command line argument.
What is the problem?
class request {
vector<string> requests;
string message;
bool valid, isGET, isHEAD;
public:
explicit request(char line[]): requests(split_string(line)), valid(true) {
struct stat sb;
if(stat(requests[1].c_str(), &sb) == -1) {
cerr << "Usage: GET or HEAD (filename) HTTP/1.0" << endl;
valid = false;
exit(EXIT_FAILURE);
}
string cont_type = "Content-Type: ";
switch (sb.st_mode & S_IFMT) {
case S_IFBLK: printf("block device\n"); break;
case S_IFCHR: printf("character device\n"); break;
case S_IFDIR: cont_type += "directory\n"; cout << "DIRECTORY" <<endl; break;
case S_IFIFO: printf("FIFO/pipe\n"); break;
case S_IFLNK: printf("symlink\n"); break;
case S_IFREG: {
string ext = requests[1].substr(requests[1].size()-6, 6);
if(ext.find(".html") != string::npos || ext.find(".txt") != string::npos) {cont_type += "text/html file\n";}
else if(ext.find(".jpg") != string::npos || ext.find(".jpeg") != string::npos || ext.find(".gif") != string::npos || ext.find(".png") != string::npos) {cont_type += "image/gif file\n";}
else {cont_type += "regular file\n";}
break;
}
case S_IFSOCK: printf("socket\n"); break;
default: printf("unknown?\n"); break;
}
}
Just in case I add the whole code, which is a server that can be connected using telnet localhost 8080:
#define BUF_LEN 8192
#include<queue>
#include<sstream>
#include<vector>
#include<string>
#include<iostream>
#include<cstring>
#include<cstdlib>
extern "C" {
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/socket.h>
#include<netdb.h>
#include<unistd.h>
#include<stdio.h>
#include<sys/time.h>
}
using std::vector;
using std::string;
using std::cerr;
using std::cout;
using std::endl;
using std::stringstream;
using std::queue;
using std::priority_queue;
char* time_stamp();
class request {
vector<string> requests;
string message;
off_t filesize;
bool valid, isGET, isHEAD;
public:
request() : valid(true) {}
explicit request(char line[]): requests(split_string(line)), valid(true) {
if(requests[0] == "GET") isGET = true;
else if(requests[0] == "HEAD") isHEAD = true;
else {
cerr << "Usage: GET or HEAD (filename) HTTP/1.0" << endl;
}
//chdir();
struct stat sb;
if(stat(requests[1].c_str(), &sb) == -1) {
cerr << "Usage: GET or HEAD (filename) HTTP/1.0" << endl;
valid = false;
// exit(EXIT_FAILURE);
}
filesize = sb.st_size;
string lmstring(ctime(&sb.st_mtime));
string lm = "Last-Modified: " + lmstring;
string cont_type = "Content-Type: ";
switch (sb.st_mode & S_IFMT) {
case S_IFBLK: cout << "block device" << endl; break;
case S_IFCHR: cout << "character device" << endl; break;
case S_IFDIR: cont_type += "directory\n"; cout << "DIRECTORY" <<endl; break;
case S_IFIFO: cout << "FIFO/pipe" << endl; break;
case S_IFLNK: cout << "symlink" << endl; break;
case S_IFREG: {
string ext = requests[1].substr(requests[1].size()-6, 6);
if(ext.find(".html") != string::npos || ext.find(".txt") != string::npos) {cont_type += "text/html file\n";}
else if(ext.find(".jpg") != string::npos || ext.find(".jpeg") != string::npos || ext.find(".gif") != string::npos || ext.find(".png") != string::npos) {cont_type += "image/gif file\n";}
else {cont_type += "regular file\n";}
break;
}
case S_IFSOCK: cout << "socket" << endl; break;
default: cout << "unknown?" << endl; break;
}
stringstream cl_ss;
cl_ss << "Content-Length: " << sb.st_size << '\n';
string cont_len = cl_ss.str();
message = lm + cont_type + cont_len;
FILE * pFile;
long lSize;
char * buffer;
size_t result;
/*
if(isGET) {
pFile = fopen ( requests[1].c_str() , "r" );
if (pFile==NULL) {fputs ("File error",stderr); exit (1);}
// obtain file size:
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
// allocate memory to contain the whole file:
buffer = (char*) malloc (sizeof(char)*lSize);
if (buffer == NULL) {fputs ("Memory error",stderr); exit (2);}
// copy the file into the buffer:
result = fread (buffer,1,lSize,pFile);
if (result != lSize) {fputs ("Reading error",stderr); exit (3);}
// the whole file is now loaded in the memory buffer.
// terminate
fclose (pFile);
free (buffer);
}
cout << buffer << endl;*/
}
bool operator<(const request& rhs) const{
return filesize < rhs.filesize;
}
string getMessage() const {
return this->message;
}
vector<string> split_string(char line[]) {
vector<string> vec_str;
char* token;
token = strtok(line, " ");
while (token != NULL) {
string temp_str(token);
vec_str.push_back(temp_str);
token = strtok(NULL, " ");
}
return vec_str;
}
};
int main() {
int status;
struct addrinfo host_info; // The struct that getaddrinfo() fills up with data.
struct addrinfo *host_info_list; // Pointer to the linked list of host_info's.
// The MAN page of getaddrinfo() states "All the other fields in the structure pointed
// to by hints must contain either 0 or a null pointer, as appropriate." When a struct
// is created in C++, it will be given a block of memory. This memory is not necessarily
// empty. Therefore we use the memset function to make sure all fields are NULL.
memset(&host_info, 0, sizeof host_info);
std::cout << "Setting up the structs..." << std::endl;
host_info.ai_family = AF_UNSPEC; // IP version not specified. Can be both.
host_info.ai_socktype = SOCK_STREAM; // Use SOCK_STREAM for TCP or SOCK_DGRAM for UDP.
host_info.ai_flags = AI_PASSIVE; // IP Wildcard
// Now fill up the linked list of host_info struts with ???????????????
status = getaddrinfo(NULL, "8080", &host_info, &host_info_list);
// getaddrinfo returns 0 on success, or some other value when an error occurred.
if (status != 0) std::cout << "getaddrinfo error" << gai_strerror(status);
std::cout << "Creating a socket..." << std::endl;
int socketfd; // The socket descripter
socketfd = socket(host_info_list->ai_family, host_info_list->ai_socktype, host_info_list->ai_protocol);
if(socketfd == -1) std::cout << "socket error " << std::endl;
std::cout << "Binding socket..." << std::endl;
// we use to make the setsockopt() function to make sure the port is not in use
// by a previous execution of our code.
int yes = 1;
status = setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
status = bind(socketfd, host_info_list->ai_addr, host_info_list->ai_addrlen);
if (status == -1) std::cout << "bind error" << std::endl;
std::cout << "Listen()ing for connections..." << std::endl;
status = listen(socketfd, 5);
if (status == -1) std::cout << "listen error" << std::endl;
int new_sd;
struct sockaddr_storage their_addr;
socklen_t addr_size = sizeof(their_addr);
new_sd = accept(socketfd, (struct sockaddr *)&their_addr, &addr_size);
if (new_sd == -1) {
std::cout << "listen error" << std::endl;
}
else {
std::cout << "Connection accepted. Using new socketfd : " << new_sd << std::endl;
}
// after success on listen()ing, loop waiting
int done;
ssize_t bytes_received;
char buf[BUF_LEN];
fd_set ready;
while (!done) {
/* fd_set contains the following fields:
typedef struct fd_set {
u_int fd_count; //how many are SET?
SOCKET fd_array[FD_SETSIZE]; //an array of SOCKETs
} fd_set;
*/
FD_ZERO(&ready);
FD_SET(new_sd, &ready);
FD_SET(fileno(stdin), &ready);
if (select((new_sd + 1), &ready, 0, 0, 0) < 0) {
cerr << "Error from select" << endl;
std::exit(1);
}
if (FD_ISSET(fileno(stdin), &ready)) {
if((bytes_received = read(fileno(stdin), buf, BUF_LEN)) <= 0)
done++;
send(new_sd, buf, bytes_received, 0);
}
std::cout << "Waiting to receive data..." << std::endl;
bytes_received = recv(new_sd, buf, BUF_LEN, 0); // recv or recvfrom???
// If no data arrives, the program will just wait here until some data arrives.
if (bytes_received == 0) std::cout << "host shut down." << std::endl;
if (bytes_received == -1) std::cout << "receive error!" << std::endl;
std::cout << bytes_received << " bytes received: " << std::endl;
buf[bytes_received] = '\0';
std::cout << "elements of 'buf'" << buf << std::endl;
std::cout << "Send()ing back a message..." << std::endl;
request tobeadded(buf);
request rq;
if(true) {
queue<request> qr;
qr.push(tobeadded);
rq = qr.front();
qr.pop();
}
else {
priority_queue<request> pqr;
pqr.push(tobeadded);
rq = pqr.top();
pqr.pop();
}
string timeheader = "Time: ";
const char* t_h = timeheader.c_str();
char* timestamp = time_stamp();
string server = "Server: myhttp\n";
const char* serv = server.c_str();
const char* lm_ct_cl = rq.getMessage().c_str();
char *msg = new char[strlen(t_h) + strlen(timestamp) + strlen(serv) + strlen(lm_ct_cl) + 1];
*msg = '\0';
strcat(msg, t_h);
strcat(msg, timestamp);
strcat(msg, serv);
strcat(msg, lm_ct_cl);
int len;
ssize_t bytes_sent;
len = strlen(msg);
bytes_sent = send(new_sd, msg, len, 0);
//write(fileno(stdout), buf, bytes_received);
}
std::cout << "Stopping server..." << std::endl;
freeaddrinfo(host_info_list);
close(new_sd);
close(socketfd);
return 0;
}
char* time_stamp() {
time_t current_time;
char* c_time_string;
/* Obtain current time as seconds elapsed since the Epoch. */
current_time = time(NULL);
if (current_time == ((time_t)-1))
{
(void) fprintf(stderr, "Failure to compute the current time.");
}
/* Convert to local time format. */
c_time_string = asctime(gmtime(¤t_time));
if (c_time_string == NULL)
{
(void) fprintf(stderr, "Failure to convert the current time.");
}
c_time_string[strlen(c_time_string)-1] = ' ';
return strcat(c_time_string, " GMT\n");
}