I need to transfer serialized data using OpenSSL. My code works but for some cases transferred data has some differences.
This is serialized data http://s000.tinyupload.com/?file_id=02935614701824936895
int sslWrite(SSL *ssl, const void *buf, int num)
{
const int MAX_SIZE = 0x10000;
const int INT_SIZE = sizeof(int);
const int numv = INT_SIZE + num;
if (numv > MAX_SIZE) {
return -1;
}
std::vector<char> bufv(numv);
memcpy(&bufv[0], &numv, INT_SIZE);
if (num != 0) {
memcpy(&bufv[INT_SIZE], buf, num);
}
int n = 0;
while (n < num) {
int w = SSL_write(ssl, &bufv[0], numv - n);
if (w < 0) {
return 0;
}
n += w;
}
return n;
}
int sslRead(SSL *ssl, void *buf, int num)
{
const int INT_SIZE = sizeof(int);
const int BUF_SIZE = 0x10000;
char bufv[BUF_SIZE];
int m = SSL_read(ssl, (char *)bufv, num);
if (m < 0) {
return m;
}
const int n = *(int *)(bufv);
if (n > BUF_SIZE - INT_SIZE) {
return -1;
}
while (m < n) {
int k = SSL_read(ssl, (char *)bufv + m, num - m);
if (k < 0) {
return k;
}
m += k;
}
memcpy(buf, &bufv[INT_SIZE], n - INT_SIZE);
return n - INT_SIZE;
}
-
// Server
#include <fstream>
#include <WS2tcpip.h>
#include <Winsock2.h>
#define _WINSOCKAPI_
#include <windows.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/applink.c>
int OpenListener(int port)
{
int sd;
struct sockaddr_in addr;
sd = socket(PF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
perror("can't bind port");
abort();
}
if (listen(sd, 10) != 0) {
perror("Can't configure listening port");
abort();
}
return sd;
}
SSL_CTX* InitServerCTX(void)
{
const SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = SSLv2_server_method();
ctx = SSL_CTX_new(method);
if (ctx == NULL) {
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
if (SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
abort();
}
if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
abort();
}
if (!SSL_CTX_check_private_key(ctx)) {
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
}
int main()
{
SSL_CTX *ctx;
int server;
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SSL_library_init();
ctx = InitServerCTX();
LoadCertificates(ctx, "sert.crt", "sert.key");
server = OpenListener(3456);
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
int client = accept(server, (struct sockaddr*)&addr, &len);
SSL *ssl;
printf("Connection: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
ssl = SSL_new(ctx);
SSL_set_fd(ssl, client);
if (SSL_accept(ssl) == -1) {
ERR_print_errors_fp(stderr);
}
else {
std::ifstream fin("data", std::ios::binary);
std::string data((std::istreambuf_iterator<char>(fin)), std::istreambuf_iterator<char>());
fin.close();
sslWrite(ssl, &data[0], (int)data.size());
const int BUF_SIZE = 0x10000;
std::string data1;
data1.resize(BUF_SIZE);
int n = sslRead(ssl, &data1[0], BUF_SIZE);
data1.resize(n);
/////////////////////////////////////////
bool b = (data == data1); // b - FALSE!!!
/////////////////////////////////////////
}
SSL_free(ssl);
closesocket(client);
}
-
// Client
#include <string>
#include <WS2tcpip.h>
#include <Winsock2.h>
#define _WINSOCKAPI_
#include <windows.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/applink.c>
SSL_CTX *InitCTX(void)
{
const SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = SSLv2_client_method();
ctx = SSL_CTX_new(method);
if (ctx == NULL) {
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
int OpenConnection(const char *hostName, int port)
{
struct hostent *host;
struct sockaddr_in addr;
if ((host = gethostbyname(hostName)) == NULL) {
perror(hostName);
abort();
}
int sd = socket(PF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if (connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
closesocket(sd);
perror(hostName);
abort();
}
return sd;
}
int main()
{
const std::string &hostName = "localhost";
const int port = 3456;
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SSL_library_init();
SSL_CTX *ctx = InitCTX();
int server = OpenConnection(hostName.c_str(), port);
SSL *ssl = SSL_new(ctx);
SSL_set_fd(ssl, server);
if (SSL_connect(ssl) == -1) {
ERR_print_errors_fp(stderr);
}
else {
const int BUF_SIZE = 0x10000;
std::string data;
data.resize(BUF_SIZE);
int n = sslRead(ssl, &data[0], BUF_SIZE);
data.resize(n);
sslWrite(ssl, &data[0], (int)data.size());
}
}
You must deal with partial writes and partial reads.
For SSL_write() you need to check the return value to make sure your data was completely written. If your data was only partially written you need to call SSL_write() again for the remaining data. Repeat until all your data is written.
For SSL_read() you need to keep reading until you get a zero return value, then check for a clean shutdown. While doing this you need to append to your buffer and build it up as you receive data.
Update:
#dascandy in comments is correct.
You need to know when to stop reading as well as dealing with partial reads. Closing the connection alone will not work in your case because when you close the connection with SSL you lose the ability to send the reply on the same connection. SSL does not have "half open" connections like TCP.
A reasonable approach is to prefix each message with a byte count when sending, and when receiving first read the byte count (using whatever encoding is convenient -- fixed length, text with delimiter, whatever) and then read exactly that number of bytes into your buffer.
Related
If I launch 2 instances of the code below in the same computer the multicast works fine.
If I launch it on a different computer in the same network I won't receive anything.
Any idea what could be wrong? This code should compile as is.
I am testing on win10 but I get similar results when I run this on linux.
#include "pch.h"
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include "winsock2.h"
#include <iostream>
#include <conio.h>
#include <thread>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
char mcastGroup[] = "224.1.2.3";
int mcastPort = 5435;
int CreateSocket()
{
return socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
}
void JoinGroup(int sck)
{
struct ip_mreq grp;
grp.imr_multiaddr.s_addr = inet_addr(mcastGroup);
grp.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(sck, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&grp, sizeof(grp)) < 0)
{
printf("Error in joining group\n");
closesocket(sck);
exit(1);
}
}
int receiver()
{
int sck = CreateSocket();
int reuse = 1;
if (setsockopt(sck, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
{
perror("Socket reuse address error\n");
closesocket(sck);
exit(1);
}
JoinGroup(sck);
struct sockaddr_in lclSck;
memset((char *)&lclSck, 0, sizeof(lclSck));
lclSck.sin_family = AF_INET;
lclSck.sin_port = htons(mcastPort);
lclSck.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sck, (struct sockaddr*)&lclSck, sizeof(lclSck)))
{
perror("Error in binding socket\n");
closesocket(sck);
exit(1);
}
while (1)
{
int blen;
char buf[1024];
blen = sizeof(buf);
memset(buf, 0, blen);
struct sockaddr_in addrin;
int addrinlen = sizeof(addrin);
memset(&addrin, 0, sizeof(addrin));
int res = recvfrom(sck, buf, blen, 0, (sockaddr *)&addrin, &addrinlen);
if (res<0)
{
printf("Message read error\n");
closesocket(sck);
exit(1);
}
else
{
printf(": %s\n", buf);
}
}
return 0;
}
int sender()
{
int sck = CreateSocket();
struct in_addr lclInterface;
lclInterface.s_addr = htonl(INADDR_ANY);
if (setsockopt(sck, IPPROTO_IP, IP_MULTICAST_IF, (char *)&lclInterface, sizeof(lclInterface)) < 0)
{
printf("Local interface error\n");
exit(1);
}
else
{
printf("Local interface set\n");
}
u_char ttl = 5;
setsockopt(sck, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl));
while (1)
{
int blen;
char buf[1024];
blen = sizeof(buf);
memset(buf, 0, blen);
for (int i = 0; i < 100; i++)
{
fgets(buf, blen, stdin);
sockaddr_in grpSck;
memset((char *)&grpSck, 0, sizeof(grpSck));
grpSck.sin_family = AF_INET;
grpSck.sin_port = htons(mcastPort);
grpSck.sin_addr.s_addr = inet_addr(mcastGroup);
if (sendto(sck, buf, blen, 0, (struct sockaddr*)&grpSck, sizeof(grpSck)) < 0)
{
printf("Error in sending message");
}
}
}
return 0;
}
int main()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
std::thread t1([&] { receiver(); return 0; });
sender();
WSACleanup();
}
This code should work, the problem was that I was using IP_MULTICAST_IF, that forces to use a network interface different from the default one. In case one needs to use such a thing I was able to get the multicast working by following
Thanks to Remy Lebeau advice, that is to make sure you are binding the sockets to IPs that are in the same network.
Here is working code the code:
#ifdef WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#endif
#include <thread>
char mcastGroup[] = "224.1.2.3";
int mcastPort = 5435;
void PrintAddrIn(sockaddr_in addr_in)
{
char str[255];
inet_ntop(AF_INET, &addr_in.sin_addr, (char *)str, sizeof(str));
printf("%s", str);
}
int receiver(int sck)
{
while (1)
{
char buf[1024];
memset(buf, 0, sizeof(buf));
struct sockaddr_in addrin;
socklen_t addrinlen = sizeof(addrin);
memset(&addrin, 0, sizeof(addrin));
int res = recvfrom(sck, buf, sizeof(buf), 0, (sockaddr *)&addrin, &addrinlen);
if (res<0)
{
printf("Message read error\n");
exit(1);
}
else
{
PrintAddrIn(addrin); printf(": %s\n", buf);
}
}
return 0;
}
int sender(int sck)
{
while (1)
{
sockaddr_in grpSck;
memset((char *)&grpSck, 0, sizeof(grpSck));
grpSck.sin_family = AF_INET;
grpSck.sin_port = htons(mcastPort);
grpSck.sin_addr.s_addr = inet_addr(mcastGroup);
for (int i = 0; i < 100; i++)
{
char buf[1024];
fgets(buf, sizeof(buf), stdin);
if (sendto(sck, buf, strlen(buf), 0, (struct sockaddr*)&grpSck, sizeof(grpSck)) < 0)
{
printf("Error in sending message");
exit(1);
}
}
}
return 0;
}
int main()
{
#ifdef WIN32
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
int sck = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// Set reuse
//
int reuse = 1;
if (setsockopt(sck, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
{
perror("Socket reuse address error\n");
exit(1);
}
else
{
printf("Socket reuse address successfull\n");
}
// Join mcast group
//
struct ip_mreq grp;
grp.imr_multiaddr.s_addr = inet_addr(mcastGroup);
grp.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(sck, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&grp, sizeof(grp)) < 0)
{
printf("Error in joining group\n");
exit(1);
}
else
{
printf("Group joined successfully\n");
}
// Bind socket
//
struct sockaddr_in lclSck;
memset((char *)&lclSck, 0, sizeof(lclSck));
lclSck.sin_family = AF_INET;
lclSck.sin_port = htons(mcastPort);
lclSck.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sck, (struct sockaddr*)&lclSck, sizeof(lclSck)))
{
perror("Error in binding socket\n");
exit(1);
}
else
{
printf("Socket binding successfull\n");
}
u_char ttl = 5;
setsockopt(sck, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl));
std::thread t1([&] { receiver(sck); return 0; });
sender(sck);
#ifdef WIN32
WSACleanup();
#endif
}
So I have a winsock application in which the computer tries to accept a client, and then starts a new thread with the a pointer to the class object with client info which it copies and then deletes with the delete keyword.
The backbone is copied but the beginning of the OOP-structure is my work and the current implementation seems to be giving me some issues.
I noticed the socket might not copy in the correct format, but have no idea on how to fix this. As the client tries to connect it throws me the error message of that the server refused the connection. Anyway, here is the code of the server.
UPDATE:
It's working.
// main.h
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <process.h>
#include <cstring>
#include <iostream>
#include <stdio.h>
#include <memory>
#pragma comment(lib,"ws2_32.lib" )
class clientData{
private:
SOCKET clientSocket;
std::string name;
public:
clientData(SOCKET &clientSock, const char *tName);
clientData(clientData *cliDat);
~clientData();
inline SOCKET *getClientSocket(){
return &clientSocket;
}
inline std::string *getName(){
return &name;
}
};
And here is the main cpp file
// main.cpp
#include "main.h"
unsigned int __stdcall ServClient(void *data);
int main(int argc, char *argv[])
{
WSADATA wsaData;
int iResult;
sockaddr_in addr;
SOCKET sock, client[10];
addr.sin_family = AF_INET;
addr.sin_port = htons(13337);
addr.sin_addr.S_un.S_addr = INADDR_ANY;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);//2.2
if (iResult)
{
printf("WSA startup failed");
return 0;
}
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
{
printf("Invalid socket");
return 0;
}
int addrlen = sizeof(sockaddr_in);
iResult = bind(sock, (sockaddr*)&addr, sizeof(sockaddr_in));
if (iResult)
{
printf("bind failed %u", GetLastError());
return 0;
}
iResult = listen(sock, SOMAXCONN);
if (iResult)
{
printf("iResult failed %u", GetLastError());
return 0;
}
int iterationCount = 0;
while ((iterationCount < 10 &&
(client[iterationCount] = accept(sock, (SOCKADDR*)&addr, &addrlen)) != SOCKET_ERROR))
{
if (client[iterationCount] == INVALID_SOCKET){
printf("invalid client socket",GetLastError());
continue;
}
++iterationCount;
char tempName[100] = { '\0' };
sprintf_s(tempName, sizeof(tempName), "Client %u", iterationCount);
clientData *tempCLdata = new clientData(client[iterationCount - 1], tempName);
_beginthreadex(0, 0, ServClient, (void*)tempCLdata, 0, 0);
tempCLdata = nullptr;
}
return 0;
}
unsigned int __stdcall ServClient(void *data)
{
clientData cliDat((clientData*)data);
delete (clientData*)data;
printf("Client connected\n");
int recvLen = 1;
char chunk[1024] = { '\0' };
while ((recvLen = recv(*cliDat.getClientSocket(), chunk, 1024, 0)) > 0){
printf("%.*s", recvLen, chunk);
}
if (recvLen == -1)
perror("Socket recv() problem..\n");
else
printf("End of data on socket, closing..\n");
closesocket(*cliDat.getClientSocket());
return 0;
}
clientData::clientData(SOCKET &clientSock, const char *tName){
clientSocket = clientSock;
name.assign(tName);
}
clientData::clientData(clientData *cliDat){
SOCKET *clientSocketPtr = cliDat->getClientSocket();
clientSocket = *clientSocketPtr;
name.assign(cliDat->getName()->c_str());
}
clientData::~clientData(){
}
Also here is the relevant bit of client code, as requested.
char buffer[1023] = { '\0' };
int heartbeatCount = 0;
while (true)
{
sprintf_s(buffer, sizeof(buffer), "Heartbeat %d", heartbeatCount + 1);
send(sock, buffer, strlen(buffer) + 1, 0);
++heartbeatCount;
Sleep(1000);
}
I need to transfer data using OpenSSL. But some data is not transferring.
There are two files with data.
dt1 works good. But my application is hanging when it tries to send dt2.
dt1
dt2
// SERVER
#include <fstream>
#include <vector>
#include <WS2tcpip.h>
#include <Winsock2.h>
#define _WINSOCKAPI_
#include <windows.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/applink.c>
int OpenListener(int port)
{
int sd;
struct sockaddr_in addr;
sd = socket(PF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
perror("can't bind port");
abort();
}
if (listen(sd, 10) != 0) {
perror("Can't configure listening port");
abort();
}
return sd;
}
SSL_CTX* InitServerCTX(void)
{
const SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = SSLv2_server_method();
ctx = SSL_CTX_new(method);
if (ctx == NULL) {
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
if (SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
abort();
}
if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
abort();
}
if (!SSL_CTX_check_private_key(ctx)) {
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
}
int main()
{
SSL_CTX *ctx;
int server;
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SSL_library_init();
ctx = InitServerCTX();
LoadCertificates(ctx, "sert.crt", "sert.key");
server = OpenListener(7654);
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
int client = accept(server, (struct sockaddr*)&addr, &len);
SSL *ssl;
printf("Connection: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
ssl = SSL_new(ctx);
SSL_set_fd(ssl, client);
if (SSL_accept(ssl) == -1) {
ERR_print_errors_fp(stderr);
}
else {
std::ifstream fin("dt1", std::ios::binary);
std::string data((std::istreambuf_iterator<char>(fin)),
std::istreambuf_iterator<char>());
fin.close();
//////////////////////////////////////////////
SSL_write(ssl, &data[0], (int)data.size()); // dt1- good. data2 - hanging
//////////////////////////////////////////////
const int BUF_SIZE = 0x100000;
std::string data1;
data1.resize(BUF_SIZE);
int n = SSL_read(ssl, &data1[0], BUF_SIZE);
if (n >= 0) {
data1.resize(n);
}
bool b = (data == data1);
}
SSL_free(ssl);
closesocket(client);
}
-
// CLIENT
#include <string>
#include <vector>
#include <WS2tcpip.h>
#include <Winsock2.h>
#define _WINSOCKAPI_
#include <windows.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/applink.c>
SSL_CTX *InitCTX(void)
{
const SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = SSLv2_client_method();
ctx = SSL_CTX_new(method);
if (ctx == NULL) {
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
int OpenConnection(const char *hostName, int port)
{
struct hostent *host;
struct sockaddr_in addr;
if ((host = gethostbyname(hostName)) == NULL) {
perror(hostName);
abort();
}
int sd = socket(PF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if (connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
closesocket(sd);
perror(hostName);
abort();
}
return sd;
}
int main()
{
const std::string &hostName = "localhost";
const int port = 7654;
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SSL_library_init();
SSL_CTX *ctx = InitCTX();
int server = OpenConnection(hostName.c_str(), port);
SSL *ssl = SSL_new(ctx);
SSL_set_fd(ssl, server);
if (SSL_connect(ssl) == -1) {
ERR_print_errors_fp(stderr);
}
else {
const int BUF_SIZE = 0x100000;
std::string data;
data.resize(BUF_SIZE);
int n = SSL_read(ssl, &data[0], BUF_SIZE);
data.resize(n);
SSL_write(ssl, &data[0], (int)data.size());
system("pause");
}
}
When I'm trying to connect to the server with only 1 client, the recv() function on the server does not delay.
But when I'm starting the client console more then 1 time (something like 7 times), there is a delay of something like 2000ms after you send to the server packet with the function send() until the server will print the packet in is console.
Is there any solution without starting a thread for each client? (Windows limits the number of threads for each process).
The code is compiled with Visual Studio 2008, and this is the full server code:
#include <WinSock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#include <Windows.h>
#include <stdio.h>
struct sslv3
{
#define max_clients 1024
private:
int cClient;
public:
SOCKET fd;
int CurrentClient()
{
return cClient;
}
struct client
{
client()
{
Valid = false;
}
bool Valid;
DWORD ip;
WORD port;
char ipstr[33];
char portstr[33];
SOCKET fd;
void StrGen()
{
wsprintf(ipstr, "%d.%d.%d.%d", ip & 0xFF, (ip & 0xFF00)/0x100, (ip & 0xFF0000)/0x10000, (ip & 0xFF000000)/0x1000000);
wsprintf(portstr, "%d", port);
}
} clients[max_clients];
//
sslv3(bool server_client)
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
cClient = 0;
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//
DWORD timeout = 1;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(DWORD));
}
int Bind(WORD port)
{
int ret = 0;
sockaddr_in local;
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(port);
if((ret = bind(fd, (struct sockaddr *)&local, sizeof(local)))
!= SOCKET_ERROR)
listen(fd, SOMAXCONN);
return ret;
}
int Accept()
{
SOCKET clientfd;
sockaddr_in client;
int addrlen = sizeof(client);
clientfd = accept(fd, (struct sockaddr *)&client, &addrlen);
if(clientfd == -1)
return -1;
clients[cClient].ip = client.sin_addr.S_un.S_addr;
clients[cClient].port = client.sin_port;
clients[cClient].StrGen();
clients[cClient].fd = clientfd;
clients[cClient].Valid = true;
//
DWORD timeout = 1;
setsockopt(clients[cClient].fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(DWORD));
cClient++;
if(cClient >= max_clients)
{
cClient = 0;
return max_clients - 1;
}
return cClient - 1;
}
int Connect(char ip[], WORD port)
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip);
addr.sin_port = htons(port);
return connect(fd, (const struct sockaddr*)&addr, sizeof(addr));
}
int Send(SOCKET sfd, void* buffer, int length)
{
return send(sfd, (char*)buffer, length, 0);
}
int Read(SOCKET sfd, void* buffer, int length)
{
return recv(sfd, (char*)buffer, length, 0);
}
};
sslv3 cssl(true);
DWORD WINAPI ReadThread(void* args)
{
while(true)
{
for(int j = 0; j <= cssl.CurrentClient(); j++)
{
if(cssl.clients[j].Valid)
{
char rpack[1024];
for(int i = 0; i < sizeof(rpack); i++)
rpack[i] = 0;
if(cssl.Read(cssl.clients[j].fd, rpack, sizeof(rpack)) > 0){
printf("%s:%s says: %s\n", cssl.clients[j].ipstr, cssl.clients[j].portstr, rpack);
}
}
}
Sleep(1);
}
return TRUE;
}
int main()
{
cssl.Bind(1234);
CreateThread(0,0,ReadThread,0,0,0);
while(true)
{
Sleep(1);
int cid = cssl.Accept();
if(cid != -1){
printf("%s:%s connected!\n", cssl.clients[cid].ipstr, cssl.clients[cid].portstr);
}
}
return 0;
}
The following is a full client code:
#include <WinSock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#include <Windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
struct sslv3
{
#define max_clients 1024
private:
int cClient;
public:
SOCKET fd;
int CurrentClient()
{
return cClient;
}
struct client
{
client()
{
Valid = false;
}
bool Valid;
DWORD ip;
WORD port;
char ipstr[33];
char portstr[33];
SOCKET fd;
void StrGen()
{
wsprintf(ipstr, "%d.%d.%d.%d", ip & 0xFF, (ip & 0xFF00)/0x100, (ip & 0xFF0000)/0x10000, (ip & 0xFF000000)/0x1000000);
wsprintf(portstr, "%d", port);
}
} clients[max_clients];
//
sslv3(bool server_client)
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
cClient = 0;
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//
DWORD timeout = 1;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(DWORD));
}
int Bind(WORD port)
{
int ret = 0;
sockaddr_in local;
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(port);
if((ret = bind(fd, (struct sockaddr *)&local, sizeof(local)))
!= SOCKET_ERROR)
listen(fd, SOMAXCONN);
return ret;
}
int Accept()
{
SOCKET clientfd;
sockaddr_in client;
int addrlen = sizeof(client);
clientfd = accept(fd, (struct sockaddr *)&client, &addrlen);
if(clientfd == -1)
return -1;
clients[cClient].ip = client.sin_addr.S_un.S_addr;
clients[cClient].port = client.sin_port;
clients[cClient].StrGen();
clients[cClient].fd = clientfd;
clients[cClient].Valid = true;
//
DWORD timeout = 1;
setsockopt(clients[cClient].fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(DWORD));
cClient++;
if(cClient >= max_clients)
{
cClient = 0;
return max_clients - 1;
}
return cClient - 1;
}
int Connect(char ip[], WORD port)
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip);
addr.sin_port = htons(port);
return connect(fd, (const struct sockaddr*)&addr, sizeof(addr));
}
int Send(SOCKET sfd, void* buffer, int length)
{
return send(sfd, (char*)buffer, length, 0);
}
int Read(SOCKET sfd, void* buffer, int length)
{
return recv(sfd, (char*)buffer, length, 0);
}
};
sslv3 cssl(false);
int main()
{
cssl.Connect("127.0.0.1", 1234);
while(true)
{
printf("say: ");
char buf[1024];
for(int i = 0; i < sizeof(buf); i++)
buf[i] = 0;
cin >> buf;
int len = strlen(buf);
cssl.Send(cssl.fd, buf, len);
}
return 0;
}
The server seems 'idle' for 2 seconds, because some clients are handled after 2 sleeps, 1 second each.
This is clearly not the right way to handle more than one client on a server. You may want to check on select() - reference.
A very good tutorial for socket programming is Beej's
I'm new to socket programming and I have this client that tries to connect to a server on the same computer. But the server hangs there after bind or accept—cause bind seems to be right but no output. I know that the server works because another client can connect just fine and the client seems to have done that. What causes the server to not see this incoming connection? I'm at the end of my wits here.
And I haven't been used to programming on Mac, so thank you so much for your patience if I have made some foolish mistakes.
My code is as follows:
server.cpp
using namespace std;
#include<iostream>
#include <netinet/in.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#define PORT 8888
#define BACKLOG 20
//#define DEST_IP "127.0.0.1"
int process_conn_server(int s)
{
ssize_t size =0;
char buffer[1024];
for( ; ; )
{
size = read(s,buffer,1024);
if(size == 0)
{
return 0;
}
}
sprintf(buffer, "%d bytes altogether\n", (int)size);
write(s, buffer,strlen(buffer)+1);
return 0;
}
int main(int argc,char *argv[])
{
//cout<<"?";
int ss, sc, r, err;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int opt=1;
pid_t pid;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
ss = socket(AF_INET, SOCK_STREAM, 0);
if(ss<0)
{
cout<<"[process infro]socket error"<<endl;
return -1;
}
cout<<"[process infro]socket successful"<<endl;
r = setsockopt(ss, SOL_SOCKET,SO_REUSEADDR, (void*)&opt,sizeof(opt));
if (r == -1)
{
perror("setsockopt(listen)");
return 0;
}
cout<<"[process infro]sockopt successful"<<endl;
cout<<"?";
err = bind(ss, (struct sockaddr*) &server_addr, sizeof( server_addr));
cout<<"err";
if(err < 0)
{
cout<<"[process infro]bind error"<<endl;
return -1;
}
cout<<"[process infro]bind successful";
err=listen(ss, BACKLOG);
if(err <0)
{
cout<<"[process infro]listen error"<<endl;
return -1;
}
cout<<"[process infro]lisen successful";
for( ; ; )
{
int addrlen = sizeof(struct sockaddr);
sc = accept(ss, (struct sockaddr*)&client_addr, (socklen_t *)&addrlen);
if(sc < 0)
{
continue;
}
pid = fork();
if (pid == 0)
{
close(ss);
process_conn_server(sc);
}
else
{
close(sc);
}
}
//opt=0;
//setsockopt(ss,SOL_SOCKET,SO_REUSEADDR,(void*)&opt,sizeof(len));
}
client.cpp
using namespace std;
#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <time.h>
#include <arpa/inet.h>
#include <fstream.h>
#define PORT 8888
#define DEST_IP "127.0.0.1"
void process_conn_client(int s)
{
ssize_t size = 0;
char buffer[1024];
//read from the file to be sent
fstream outfile("programm.txt",ios::in|ios::out);
if (outfile.fail())
{
printf("[process infro]cannot open the file to be sent\n");
return ;
}
printf("[process infro]successfully open the file to be sent\n");
while(!outfile.eof())
{
outfile.getline(buffer,1025,'\n');
write(s,buffer,1024);
size = read(s, buffer, 1024);
if(size = 0)
{
return ;
}
//write to the server
write(s,buffer,size);
//get response from the server
size=read(s,buffer,1024);
write(1,buffer,size);
}
outfile.close(); //关闭文件
}
int main(int argc,char *argv[])
{
int s;
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(DEST_IP);
server_addr.sin_port = htons(PORT);
s = socket(AF_INET, SOCK_STREAM, 0);
if(s < 0)
{
cout<<"[process infro]socke error"<<endl;
return -1;
}
cout<<"[process infro] socket built successfully\n";
//inet_pton(AF_INET, argv[1], &server_addr.sin_addr);
connect(s, (struct sockaddr*)&server_addr, sizeof(struct sockaddr));
cout<<"[process infor] connected\n";
process_conn_client(s);
close(s);
return 0;
}
This may be unrelated.... but it won't fit in a comment...
In your server you do this:
int process_conn_server(int s)
{
ssize_t size =0;
char buffer[1024];
for( ; ; )
{
// keep reading until read returns 0
size = read(s,buffer,1024);
if(size == 0)
{
return 0;
}
}
sprintf(buffer, "%d bytes altogether\n", (int)size);
write(s, buffer,strlen(buffer)+1);
return 0;
}
In your client you do this:
void process_conn_client(int s)
{
ssize_t size = 0;
char buffer[1024];
//read from the file to be sent
fstream outfile("programm.txt",ios::in|ios::out);
if (outfile.fail())
{
printf("[process infro]cannot open the file to be sent\n");
return ;
}
printf("[process infro]successfully open the file to be sent\n");
while(!outfile.eof())
{
outfile.getline(buffer,1025,'\n');
// write to server?
write(s,buffer,1024);
// read from server?
size = read(s, buffer, 1024);
if(size = 0)
{
return ;
}
//write to the server
write(s,buffer,size);
//get response from the server
size=read(s,buffer,1024);
write(1,buffer,size);
}
outfile.close();
}
It's a bit hard to follow because of your variable names, but it looks like your client is working under the assumption that your server will send back a response for every chunk of data received, which isn't the case. You server doesn't appear to have changed the accepted socket to non-blocking, so it's going to block on the read call until there is some data to read (it's never going to get 0)...
Are you sure it's failing before this point? Do you have some sample output?
Aso, in your call to accept, you pass addrlen...
int addrlen = sizeof(struct sockaddr);
I think this should be:
int addrlen = sizeof(struct sockaddr_in); /* sizeof(client_addr) */