commit
f80993fed3
15 changed files with 600 additions and 0 deletions
@ -0,0 +1,3 @@ |
||||
app |
||||
build/ |
||||
test* |
@ -0,0 +1,55 @@ |
||||
{ |
||||
"files.associations": { |
||||
"ostream": "cpp", |
||||
"vector": "cpp", |
||||
"memory": "cpp", |
||||
"optional": "cpp", |
||||
"string_view": "cpp", |
||||
"string": "cpp", |
||||
"system_error": "cpp", |
||||
"thread": "cpp", |
||||
"array": "cpp", |
||||
"atomic": "cpp", |
||||
"bit": "cpp", |
||||
"*.tcc": "cpp", |
||||
"cctype": "cpp", |
||||
"chrono": "cpp", |
||||
"clocale": "cpp", |
||||
"cmath": "cpp", |
||||
"cstdarg": "cpp", |
||||
"cstddef": "cpp", |
||||
"cstdint": "cpp", |
||||
"cstdio": "cpp", |
||||
"cstdlib": "cpp", |
||||
"ctime": "cpp", |
||||
"cwchar": "cpp", |
||||
"cwctype": "cpp", |
||||
"deque": "cpp", |
||||
"map": "cpp", |
||||
"unordered_map": "cpp", |
||||
"exception": "cpp", |
||||
"algorithm": "cpp", |
||||
"functional": "cpp", |
||||
"iterator": "cpp", |
||||
"memory_resource": "cpp", |
||||
"numeric": "cpp", |
||||
"random": "cpp", |
||||
"ratio": "cpp", |
||||
"tuple": "cpp", |
||||
"type_traits": "cpp", |
||||
"utility": "cpp", |
||||
"fstream": "cpp", |
||||
"initializer_list": "cpp", |
||||
"iomanip": "cpp", |
||||
"iosfwd": "cpp", |
||||
"iostream": "cpp", |
||||
"istream": "cpp", |
||||
"limits": "cpp", |
||||
"new": "cpp", |
||||
"sstream": "cpp", |
||||
"stdexcept": "cpp", |
||||
"streambuf": "cpp", |
||||
"cinttypes": "cpp", |
||||
"typeinfo": "cpp" |
||||
} |
||||
} |
@ -0,0 +1,69 @@ |
||||
# Author Yannis Gerlach
|
||||
# HochSchule Osnabrück
|
||||
# 29.04.2020
|
||||
|
||||
# `make clean all` nicht mit -j verwenden! -> race condition im make file
|
||||
# statdessen: `make clean; make all -j` verwenden
|
||||
|
||||
NAME = app
|
||||
NAMETEST = test
|
||||
CFLAGS = -std=c++2a -O2 -g3 -pipe -Wall -rdynamic
|
||||
CXX = g++
|
||||
SRCF = src/
|
||||
INCF = inc/
|
||||
BUILDDIR = build/
|
||||
TESTF = tests/
|
||||
DEPF = $(BUILDDIR)deps/
|
||||
|
||||
INCLUDES = -I$(INCF)
|
||||
LDFLAGS = -lpthread -lcrypto
|
||||
SRCFILES = $(shell find $(SRCF) -name "*.cpp")
|
||||
OBJFILES = $(patsubst $(SRCF)%, $(BUILDDIR)%, $(patsubst %.cpp, %.o, $(SRCFILES)))
|
||||
DEPFILES = $(wildcard $(DEPF)*.d)
|
||||
|
||||
SOURCEDIRS = $(shell find $(SRCF) -type d -printf "%p/\n")
|
||||
BUILDDIRS = $(patsubst $(SRCF)%, $(BUILDDIR)%, $(SOURCEDIRS))
|
||||
|
||||
OBJFILESTEST = $(filter-out $(BUILDDIR)main.o, $(OBJFILES))
|
||||
|
||||
INCLUDES += $(addprefix -I, $(SOURCEDIRS))
|
||||
|
||||
all: $(NAME) runtest |
||||
|
||||
$(NAME): $(BUILDDIRS) $(DEPF) $(OBJFILES) |
||||
@echo "Linking $@"
|
||||
@$(CXX) $(CFLAGS) -o $@ $(filter %.o, $^) $(LDFLAGS)
|
||||
|
||||
$(BUILDDIR)%.o: $(SRCF)%.cpp |
||||
@echo "Compiling: $@"
|
||||
@$(CXX) $(CFLAGS) $(INCLUDES) $< -MM -MT $@ > $(DEPF)$(subst /,_,$*).d
|
||||
@$(CXX) -c -o $@ $(CFLAGS) $(INCLUDES) $<
|
||||
|
||||
$(NAME)_strip: $(NAME) |
||||
@echo "Strip $<"
|
||||
@strip -o $@ $<
|
||||
|
||||
%/: |
||||
mkdir -p $@
|
||||
|
||||
clean-depends: |
||||
$(RM) -r $(DEPF)
|
||||
|
||||
|
||||
clean: |
||||
$(RM) -r $(NAME) $(BUILDDIR) $(NAMETEST)
|
||||
|
||||
$(NAMETEST): $(BUILDDIRS) $(DEPF) $(TESTF)*.cpp $(OBJFILESTEST) |
||||
@echo "Compiling tests"
|
||||
@$(CXX) -o $@ $(filter %.o, $^) $(filter %.cpp, $^) $(CFLAGS) -I$(SRCF) $(INCLUDES) $(LDFLAGS)
|
||||
|
||||
runtest: $(NAMETEST) |
||||
@echo "Running tests"
|
||||
./$<
|
||||
|
||||
# fix assets :
|
||||
# find assets/ -name '*.*' -exec sh -c 'a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$0" ] && mv "$0" "$a" ' {} \;
|
||||
|
||||
.PHONY: clean all $(NAMETEST) clean-depends runtest |
||||
|
||||
include $(DEPFILES) |
@ -0,0 +1,34 @@ |
||||
#ifndef fileutility_hpp |
||||
#define fileutility_hpp |
||||
#include <openssl/sha.h> |
||||
#include <iostream> |
||||
#include <vector> |
||||
#include "settings.hpp" |
||||
class File; |
||||
class FileUtility; |
||||
|
||||
class FileUtility { |
||||
public: |
||||
static std::vector<File> files; |
||||
static void prepfile(const std::string& filepath); |
||||
static File getFile(const std::string& hash); |
||||
static std::string hashblock(char* data, uint64_t size); |
||||
|
||||
private: |
||||
static std::string hashfile(const std::string& filepath, uint64_t* filesize); |
||||
static std::string sha256_hash_string( |
||||
unsigned char hash[SHA256_DIGEST_LENGTH]); |
||||
}; |
||||
class File { |
||||
public: |
||||
File(const std::string& filename, |
||||
const std::string& hash, |
||||
const uint64_t& filesize); |
||||
|
||||
std::string filename; |
||||
std::string hash; |
||||
uint64_t filesize; |
||||
|
||||
private: |
||||
}; |
||||
#endif |
@ -0,0 +1,31 @@ |
||||
#pragma once |
||||
#ifndef packetmanager_hpp |
||||
#define packetmanager_hpp |
||||
#include <stdint.h> |
||||
#include "protocol.hpp" |
||||
|
||||
struct Packet { |
||||
char identifier = p_identifier_req; |
||||
uint8_t pid = p_p_err; |
||||
char file_hash[64] = ""; |
||||
}; |
||||
struct FileInfoPacket : Packet { |
||||
char filename[256] = ""; |
||||
uint8_t pid = p_p_info; |
||||
uint64_t filesize = 0; |
||||
}; |
||||
struct FileDataPacket : Packet { |
||||
char data[blocksize] = ""; |
||||
uint8_t pid = p_p_block; |
||||
uint32_t chunkid = 0; |
||||
char data_hash[65] = ""; |
||||
}; |
||||
|
||||
class PacketManager { |
||||
public: |
||||
PacketManager(); |
||||
void manageData(int fd, char* data, unsigned long size, void* client); |
||||
|
||||
private: |
||||
}; |
||||
#endif |
@ -0,0 +1,11 @@ |
||||
#ifndef protocol_hpp |
||||
#define protocol_hpp |
||||
#define DEF_PORT 8122 |
||||
#define p_headersize 16 |
||||
#define blocksize 65536 |
||||
#define p_identifier_req '#' |
||||
#define p_identifier_send '!' |
||||
#define p_p_err '0' |
||||
#define p_p_info '1' |
||||
#define p_p_block '2' |
||||
#endif |
@ -0,0 +1,12 @@ |
||||
#pragma once |
||||
#ifndef Settings_hpp |
||||
#define Settings_hpp |
||||
#include <cstdint> |
||||
class Settings { |
||||
public: |
||||
static void readArgs(int argc, char* argv[]); |
||||
static int port; |
||||
static char* ep; |
||||
static bool running; |
||||
}; |
||||
#endif |
@ -0,0 +1,19 @@ |
||||
#pragma once |
||||
#ifndef udpclient_hpp |
||||
#define udpclient_hpp |
||||
#include <iostream> |
||||
#include "packetmanager.hpp" |
||||
class UDPClient { |
||||
public: |
||||
UDPClient(const std::string& addr, int port); |
||||
UDPClient(const std::string& addr); |
||||
|
||||
void requestInfo(const std::string& hash); |
||||
|
||||
private: |
||||
void createSocket(); |
||||
std::string addr; |
||||
int port; |
||||
int clientsocket; |
||||
}; |
||||
#endif |
@ -0,0 +1,16 @@ |
||||
#pragma once |
||||
#ifndef udpserver_hpp |
||||
#define udpserver_hpp |
||||
#include "packetmanager.hpp" |
||||
|
||||
class UDPServer { |
||||
public: |
||||
UDPServer(); |
||||
~UDPServer(); |
||||
void start(); |
||||
|
||||
private: |
||||
int serversocket; |
||||
PacketManager* pmgr; |
||||
}; |
||||
#endif |
@ -0,0 +1,78 @@ |
||||
#include "fileutility.hpp" |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include "openssl/sha.h" |
||||
|
||||
std::vector<File> FileUtility::files; |
||||
|
||||
void FileUtility::prepfile(const std::string& filepath) { |
||||
std::cout << "Preparing File: " << filepath << std::endl; |
||||
uint64_t size = 0; |
||||
std::string hash = hashfile(filepath, &size); |
||||
File f(filepath, hash, size); |
||||
files.push_back(f); |
||||
} |
||||
File FileUtility::getFile(const std::string& hash) { |
||||
for (auto it : files) { |
||||
if (it.hash == hash) |
||||
return it; |
||||
} |
||||
return File("", "", 0); |
||||
} |
||||
std::string FileUtility::hashblock(char* data, uint64_t size) { |
||||
unsigned char hash[SHA256_DIGEST_LENGTH]; |
||||
SHA256_CTX sha256; |
||||
SHA256_Init(&sha256); |
||||
SHA256_Update(&sha256, data, size); |
||||
SHA256_Final(hash, &sha256); |
||||
return sha256_hash_string(hash); |
||||
} |
||||
|
||||
std::string FileUtility::hashfile(const std::string& filepath, |
||||
uint64_t* filesize) { |
||||
std::cout << "Hashing File: " << filepath << std::endl; |
||||
std::string retval; |
||||
FILE* file = fopen(filepath.c_str(), "rb"); |
||||
if (!file) |
||||
return "error!"; |
||||
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH]; |
||||
SHA256_CTX sha256; |
||||
SHA256_Init(&sha256); |
||||
const int bufSize = 32768; |
||||
unsigned char* buffer = (unsigned char*)malloc(bufSize); |
||||
int bytesRead = 0; |
||||
if (!buffer) |
||||
return "error!"; |
||||
uint64_t totalRead = 0; |
||||
while ((bytesRead = fread(buffer, 1, bufSize, file))) { |
||||
SHA256_Update(&sha256, buffer, bytesRead); |
||||
totalRead += bytesRead; |
||||
} |
||||
if (filesize != nullptr) |
||||
*filesize = totalRead; |
||||
SHA256_Final(hash, &sha256); |
||||
|
||||
retval = sha256_hash_string(hash); |
||||
fclose(file); |
||||
free(buffer); |
||||
return retval; |
||||
} |
||||
|
||||
std::string FileUtility::sha256_hash_string( |
||||
unsigned char hash[SHA256_DIGEST_LENGTH]) { |
||||
char outputBuffer[65]; |
||||
int i = 0; |
||||
|
||||
for (i = 0; i < SHA256_DIGEST_LENGTH; i++) { |
||||
sprintf(outputBuffer + (i * 2), "%02x", hash[i]); |
||||
} |
||||
|
||||
outputBuffer[64] = 0; |
||||
return outputBuffer; |
||||
} |
||||
|
||||
File::File(const std::string& filename, |
||||
const std::string& hash, |
||||
const uint64_t& filesize) |
||||
: filename(filename), hash(hash), filesize(filesize){}; |
@ -0,0 +1,32 @@ |
||||
#include <unistd.h> |
||||
#include <iostream> |
||||
#include <thread> |
||||
#include "fileutility.hpp" |
||||
#include "settings.hpp" |
||||
#include "udpclient.hpp" |
||||
#include "udpserver.hpp" |
||||
UDPServer server; |
||||
void* startUDPServer(); |
||||
|
||||
int main(int argc, char* argv[]) { |
||||
Settings::readArgs(argc, argv); |
||||
std::cout << "Starting on Port " << Settings::port << "!" << std::endl; |
||||
Settings::running = true; |
||||
std::thread server(&startUDPServer); |
||||
FileUtility::prepfile("testfile"); |
||||
UDPClient client("127.0.0.1", DEF_PORT); |
||||
usleep(10000); |
||||
for (auto it : FileUtility::files) { |
||||
std::cout << "Requesting info! " << it.hash << std::endl; |
||||
|
||||
client.requestInfo(it.hash); |
||||
} |
||||
while (Settings::running) { |
||||
} |
||||
if (server.joinable()) |
||||
server.join(); |
||||
} |
||||
void* startUDPServer() { |
||||
server.start(); |
||||
return 0; |
||||
} |
@ -0,0 +1,101 @@ |
||||
#include "packetmanager.hpp" |
||||
#include <netinet/in.h> |
||||
#include <string.h> |
||||
#include <fstream> |
||||
#include "fileutility.hpp" |
||||
#include "protocol.hpp" |
||||
|
||||
PacketManager::PacketManager() {} |
||||
void PacketManager::manageData(int fd, |
||||
char* data, |
||||
unsigned long size, |
||||
void* client) { |
||||
struct sockaddr_in clientaddr = *((struct sockaddr_in*)client); |
||||
|
||||
if (size >= sizeof(Packet)) { |
||||
Packet basic = *((Packet*)data); |
||||
if (basic.identifier == p_identifier_req) { |
||||
if ((basic.pid == p_p_block) && (size >= sizeof(FileDataPacket))) { |
||||
FileDataPacket packet = *((FileDataPacket*)data); |
||||
File f = FileUtility::getFile(packet.file_hash); |
||||
std::cout << "Block Request! -> " << packet.file_hash << std::endl; |
||||
|
||||
if (f.hash != "") { |
||||
std::cout << "File Found! -> " << packet.file_hash << std::endl; |
||||
|
||||
FileDataPacket toSend; |
||||
toSend.identifier = p_identifier_send; |
||||
toSend.chunkid = packet.chunkid; |
||||
memcpy(toSend.file_hash, packet.file_hash, sizeof(packet.file_hash)); |
||||
std::ifstream is(f.filename, std::ifstream::binary); |
||||
if (is) { |
||||
if (toSend.chunkid <= 0) |
||||
is.seekg(0); |
||||
else |
||||
is.seekg(packet.chunkid * blocksize + 1); |
||||
is.read(packet.data, blocksize); |
||||
std::string hash = FileUtility::hashblock(toSend.data, blocksize); |
||||
memcpy(toSend.data_hash, hash.c_str(), hash.length()); |
||||
sendto(fd, &toSend, sizeof(toSend), MSG_CONFIRM, |
||||
(const struct sockaddr*)&clientaddr, sizeof(clientaddr)); |
||||
} else { |
||||
std::cerr << "Couldn't read file! -> " << packet.file_hash |
||||
<< std::endl; |
||||
Packet toSend; |
||||
toSend.identifier = p_identifier_send; |
||||
memset(&toSend.file_hash, 0, sizeof(toSend.file_hash)); |
||||
sendto(fd, &toSend, sizeof(toSend), MSG_CONFIRM, |
||||
(const struct sockaddr*)&clientaddr, sizeof(clientaddr)); |
||||
} |
||||
|
||||
} else { |
||||
std::cout << "Couldn't read / find file! -> " << packet.file_hash |
||||
<< std::endl; |
||||
|
||||
Packet toSend; |
||||
toSend.identifier = p_identifier_send; |
||||
memset(&toSend.file_hash, 0, sizeof(toSend.file_hash)); |
||||
sendto(fd, &toSend, sizeof(toSend), MSG_CONFIRM, |
||||
(const struct sockaddr*)&clientaddr, sizeof(clientaddr)); |
||||
} |
||||
|
||||
} else if ((basic.pid = p_p_info) && (size >= sizeof(FileInfoPacket))) { |
||||
FileInfoPacket packet = *((FileInfoPacket*)data); |
||||
File f = FileUtility::getFile(packet.file_hash); |
||||
std::cout << "info Request! -> " << packet.file_hash << std::endl; |
||||
|
||||
if (f.hash != "") { |
||||
std::cout << "File found! -> " << packet.file_hash << std::endl; |
||||
|
||||
FileInfoPacket toSend; |
||||
toSend.identifier = p_identifier_send; |
||||
memcpy(toSend.file_hash, f.hash.c_str(), f.hash.length()); |
||||
memcpy(toSend.filename, f.filename.c_str(), f.filename.length()); |
||||
toSend.filesize = f.filesize; |
||||
sendto(fd, &toSend, sizeof(toSend), MSG_CONFIRM, |
||||
(const struct sockaddr*)&clientaddr, sizeof(clientaddr)); |
||||
|
||||
} else { |
||||
std::cerr << "Couldn't find file! -> " << packet.file_hash |
||||
<< std::endl; |
||||
|
||||
Packet toSend; |
||||
toSend.identifier = p_identifier_send; |
||||
memset(&toSend.file_hash, 0, sizeof(toSend.file_hash)); |
||||
sendto(fd, &toSend, sizeof(toSend), MSG_CONFIRM, |
||||
(const struct sockaddr*)&clientaddr, sizeof(clientaddr)); |
||||
} |
||||
} else { |
||||
std::cout << "Wtf?!" << std::endl; |
||||
|
||||
Packet toSend; |
||||
toSend.identifier = p_identifier_send; |
||||
memset(&toSend.file_hash, 0, sizeof(toSend.file_hash)); |
||||
sendto(fd, &toSend, sizeof(toSend), MSG_CONFIRM, |
||||
(const struct sockaddr*)&clientaddr, sizeof(clientaddr)); |
||||
} |
||||
|
||||
} else if (basic.identifier == p_identifier_send) { |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,35 @@ |
||||
#include "settings.hpp" |
||||
#include <getopt.h> |
||||
#include <unistd.h> |
||||
#include <iostream> |
||||
#include <sstream> |
||||
#include "protocol.hpp" |
||||
void Settings::readArgs(int argc, char* argv[]) { |
||||
static const struct option longopts[] = { |
||||
{"port", optional_argument, 0, 'p'}, |
||||
{"endpoint", optional_argument, 0, 'e'}, |
||||
0}; |
||||
|
||||
int index = 0; |
||||
int c; |
||||
std::stringstream str; |
||||
|
||||
while ((c = getopt_long(argc, argv, "p:e:", longopts, &index)) != -1) { |
||||
switch (c) { |
||||
case 'p': |
||||
str << optarg; |
||||
str >> port; |
||||
break; |
||||
case 'e': |
||||
Settings::ep = optarg; |
||||
break; |
||||
default: |
||||
fprintf(stderr, "Usage: %s [--port port] [--endpoint ip]\n", argv[0]); |
||||
exit(EXIT_FAILURE); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
int Settings::port = DEF_PORT; |
||||
char* Settings::ep = nullptr; |
||||
bool Settings::running = false; |
@ -0,0 +1,49 @@ |
||||
#include "udpclient.hpp" |
||||
#include <arpa/inet.h> |
||||
#include <netinet/in.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <sys/socket.h> |
||||
#include <sys/types.h> |
||||
#include <sstream> |
||||
|
||||
UDPClient::UDPClient(const std::string& addr, int port) |
||||
: addr(addr), port(port) { |
||||
createSocket(); |
||||
} |
||||
UDPClient::UDPClient(const std::string& addr) { |
||||
if (addr.find(':') <= addr.length()) { |
||||
std::string ip = addr.substr(0, addr.find(":")); |
||||
std::string port = addr.substr(addr.find(":") + 1, addr.length()); |
||||
int portnum = -1; |
||||
std::stringstream ss; |
||||
ss << port; |
||||
ss >> portnum; |
||||
this->port = portnum; |
||||
this->addr = ip; |
||||
} else { |
||||
this->addr = addr; |
||||
this->port = DEF_PORT; |
||||
} |
||||
createSocket(); |
||||
} |
||||
void UDPClient::createSocket() { |
||||
if ((clientsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { |
||||
perror("socket creation failed"); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
} |
||||
void UDPClient::requestInfo(const std::string& hash) { |
||||
struct sockaddr_in serveraddr; |
||||
memset(&serveraddr, 0, sizeof(serveraddr)); |
||||
serveraddr.sin_family = AF_INET; |
||||
serveraddr.sin_port = htons(port); |
||||
inet_pton(AF_INET, addr.c_str(), &(serveraddr.sin_addr)); |
||||
|
||||
FileInfoPacket fip; |
||||
fip.identifier = p_identifier_req; |
||||
memcpy(fip.file_hash, hash.c_str(), hash.length()); |
||||
sendto(clientsocket, &fip, sizeof(fip), MSG_CONFIRM, |
||||
(const struct sockaddr*)&serveraddr, sizeof(serveraddr)); |
||||
} |
@ -0,0 +1,55 @@ |
||||
#include "udpserver.hpp" |
||||
#include <arpa/inet.h> |
||||
#include <netinet/in.h> |
||||
#include <string.h> |
||||
#include <sys/socket.h> |
||||
#include <sys/types.h> |
||||
#include <unistd.h> |
||||
#include <iostream> |
||||
#include "protocol.hpp" |
||||
#include "settings.hpp" |
||||
UDPServer::UDPServer() { |
||||
pmgr = new PacketManager(); |
||||
} |
||||
UDPServer::~UDPServer() { |
||||
delete pmgr; |
||||
} |
||||
|
||||
void UDPServer::start() { |
||||
std::cout << "Starting UDP Server on Port " << Settings::port; |
||||
struct sockaddr_in serveraddr, clientaddr; |
||||
|
||||
if ((serversocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { |
||||
perror("socket creation failed"); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
|
||||
memset(&serveraddr, 0, sizeof(serveraddr)); |
||||
memset(&clientaddr, 0, sizeof(serveraddr)); |
||||
|
||||
// Filling server information
|
||||
serveraddr.sin_family = AF_INET; // IPv4
|
||||
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); |
||||
serveraddr.sin_port = htons(Settings::port); |
||||
|
||||
// Bind the socket with the server address
|
||||
if (bind(serversocket, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < |
||||
0) { |
||||
perror("bind failed"); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
while (Settings::running) { |
||||
memset(&clientaddr, 0, sizeof(serveraddr)); |
||||
char buffer[sizeof(FileDataPacket)]; |
||||
socklen_t len; |
||||
int n; |
||||
len = sizeof(clientaddr); // len is value/result
|
||||
n = recvfrom(serversocket, (char*)buffer, blocksize, MSG_WAITALL, |
||||
(struct sockaddr*)&clientaddr, &len); |
||||
|
||||
if (n < 0) |
||||
std::cerr << "Error Reading!"; |
||||
else |
||||
pmgr->manageData(serversocket, (char*)buffer, n, &clientaddr); |
||||
} |
||||
} |
Loading…
Reference in new issue