libevent fires send when it receveives packet - c

I am building MQTT broker now.
SO, when I am receiving a MQTT packet from the client.That packet is also being send simultaneously.I can't find out the reason why. I am posting my code snippet below please do point out where am I going wrong .
static void onServEcho(struct bufferevent* bev, void* cbarg) {
EvBufferEvent evbuf(bev);
struct evbuffer *input = bufferevent_get_input(bev);
struct evbuffer *output = bufferevent_get_output(bev);
size_t len = evbuffer_get_length(input);
evutil_socket_t accept_fd = bufferevent_getfd(bev);
// Copy all the data from the input buffer to the output buffer.
char *data;
//data =(char *) malloc(len);
data = new char[len];
// cout<<"dasd"<<data<<endl;
evbuffer_copyout(input, data, len);
// evbuf.copy_data(input, data, len);
char converted[len];
int i;
for (i = 0; i < len; i++) {
sprintf(&converted[i * 2], "%02X", data[i]);
// cout<<"data is"<< data[i]<<endl;
*/
}
// std::stringstream stream;
// char packet[len];
//cout << "the hex data is" << converted << "with size" << len << endl;
// for(i=0;i<(2*len;i=i+2) {
// string connect_char;
// connect_char.insert(0,"0x");
/// connect_char.append(converted,i,2);
//char *buffer=static_cast<char*>(connect_char);
/// unsigned int buffer=strtoul(connect_char.c_str(), NULL, 16);
// char W = static_cast<char>(buffer);
// cout<<"the characterr is "<<W<<endl;
// }
//char *packet=convert(converted,20);
//cout<<"the packet converted is "<<packet<<endl;
string connect_comd;
//connect_comd="0x";
connect_comd.insert(0, "0x");
connect_comd.append(converted, 0, 2);
// strcpy(connect_comd,converted[0]);
// strcpy(connect_comd,converted[1]);
//unsigned int buf = strtoul(connect_comd.c_str(), NULL, 16);
//int buf=0x10;
int message_type = (ToBits(data[0]).to_ulong());
// cout<<"the message type received now is"<<GET_MESSAGE(data[0])<<endl;
//cout << "the connectcommand is" << buf << endl;
cout << "the message flag received now is" << ToBits(data[0]) << endl;
if (GET_MESSAGE(message_type) == CONNECT) {
cout << "connect packet received from mqtt client" << endl;
ConnectPack_Parser(data, len, accept_fd);
} else if (GET_MESSAGE(message_type) == SUBSCRIBE) {
cout << "subscribe packet received from mqtt client" << endl;
SubscribePack_Parser(data, len, accept_fd);
}
// }
// hexify()
//evbuffer_add_buffer(output, input);
evbuffer_add_buffer(output, input);
//evbuffer_remove_buffer(input,output,len);
free(data);
}
edit:
i am not just posting the code for a fix to the code but i am cluless as to how to avoid that send from the server end using lib event .
the line 'evbuffer_add_buffer(output, input);' automatically sends the received packet which i can trace from the wireshark.The thing is this line according to the libevent documentation is needed for optimal performance.

the code is working for now.I am posting it for those who may need it. If you only want the read operation don't use the ' evbuffer_add_buffer(output, input)' code since this instantly writes it to the socket or in other words sends the data to the connected client so the CONNECT packet was send simultaneously

Related

io_uring user_data field is always zero

I'm playing around with io_uring, https://kernel.dk/io_uring.pdf, to see if it can be used for async file I/O for logging. This is a simple program that opens a file, stats the file, and then reads the first 4k from the file. This program runs to completion successfully when the file exists and is readable. But the user_data field in the completion queue entry is always zero. The documentation for io_uring says:
user_data is common across op-codes, and is untouched by the kernel. It's simply copied to the completion event, cqe, when a completion event is posted for this request.
Since the completions are not ordered the user_data field is needed to match completions with submissions. If the field is always zero then how can it be used?
#include <iostream>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <liburing.h>
#include <stdlib.h>
int main() {
struct io_uring ring;
// see man io_uring_setup for what this does
auto ret = io_uring_queue_init(64, &ring, 0);
if (ret) {
perror("Failed initialize uring.");
exit(1);
}
std::cout << "I/O uring initialized successfully. " << std::endl;
auto directory_fd = open("/tmp", O_RDONLY);
if (directory_fd < 0) {
perror("Failed to open current directory.");
exit(1);
}
struct io_uring_sqe *submission_queue_entry = io_uring_get_sqe(&ring);
submission_queue_entry->user_data = 100;
io_uring_prep_openat(submission_queue_entry, directory_fd, "stuff", O_RDONLY, 0);
submission_queue_entry = io_uring_get_sqe(&ring);
submission_queue_entry->user_data = 1000;
struct statx statx_info;
io_uring_prep_statx(submission_queue_entry, directory_fd, "stuff", 0, STATX_SIZE, &statx_info);
//TODO: what does this actually return?
auto submit_error = io_uring_submit(&ring);
if (submit_error != 2) {
std::cerr << strerror(submit_error) << std::endl;
exit(2);
}
int file_fd = -1;
uint32_t responses = 0;
while (responses != 2) {
struct io_uring_cqe *completion_queue_entry = 0;
auto wait_return = io_uring_wait_cqe(&ring, &completion_queue_entry);
if (wait_return) {
std::cerr << "Completion queue wait error. " << std::endl;
exit(2);
}
std::cout << "user data " << completion_queue_entry->user_data << " entry ptr " << completion_queue_entry << " ret " << completion_queue_entry->res << std::endl;
std::cout << "size " << statx_info.stx_size << std::endl;
io_uring_cqe_seen(&ring, completion_queue_entry);
if (completion_queue_entry->res > 0) {
file_fd = completion_queue_entry->res;
}
responses++;
}
submission_queue_entry = io_uring_get_sqe(&ring);
submission_queue_entry->user_data = 66666;
char buf[1024 * 4];
io_uring_prep_read(submission_queue_entry, file_fd, buf, 1024 * 4, 0);
io_uring_submit(&ring);
struct io_uring_cqe* read_entry = 0;
auto read_wait_rv = io_uring_wait_cqe(&ring, &read_entry);
if (read_wait_rv) {
std::cerr << "Error waiting for read to complete." << std::endl;
exit(2);
}
std::cout << "Read user data " << read_entry->user_data << " completed with " << read_entry->res << std::endl;
if (read_entry->res < 0) {
std::cout << "Read error " << strerror(-read_entry->res) << std::endl;
}
}
Output
I/O uring initialized successfully.
user data 0 entry ptr 0x7f4e3158c140 ret 5
size 1048576
user data 0 entry ptr 0x7f4e3158c150 ret 0
size 1048576
Read user data 0 completed with 4096
What happens if you try and set user_data after your calls to io_uring_prep_openat()/io_uring_prep_statx()?
I ask this because doing a Google search for io_uring_prep_statx suggests it comes from liburing library.
Searching the liburing source for io_uring_prep_openat leads us to a definition of io_uring_prep_openat() in liburing.h:
static inline void io_uring_prep_openat(struct io_uring_sqe *sqe, int dfd,
const char *path, int flags, mode_t mode)
{
io_uring_prep_rw(IORING_OP_OPENAT, sqe, dfd, path, mode, 0);
sqe->open_flags = flags;
}
Searching the liburing source for io_uring_prep_statx leads to a definition of io_uring_prep_statx():
static inline void io_uring_prep_statx(struct io_uring_sqe *sqe, int dfd,
const char *path, int flags, unsigned mask,
struct statx *statxbuf)
{
io_uring_prep_rw(IORING_OP_STATX, sqe, dfd, path, mask,
(__u64) (unsigned long) statxbuf);
sqe->statx_flags = flags;
}
Chasing the calls gets us to the definition of io_uring_prep_rw:
static inline void io_uring_prep_rw(int op, struct io_uring_sqe *sqe, int fd,
const void *addr, unsigned len,
__u64 offset)
{
sqe->opcode = op;
sqe->flags = 0;
sqe->ioprio = 0;
sqe->fd = fd;
sqe->off = offset;
sqe->addr = (unsigned long) addr;
sqe->len = len;
sqe->rw_flags = 0;
sqe->user_data = 0;
sqe->__pad2[0] = sqe->__pad2[1] = sqe->__pad2[2] = 0;
}
PS: I notice you have a comment that says
//TODO: what does this actually return?
auto submit_error = io_uring_submit(&ring);
Well, if we search the liburing repo for "int io_uring_submit" we come across the following in src/queue.c:
/*
* Submit sqes acquired from io_uring_get_sqe() to the kernel.
*
* Returns number of sqes submitted
*/
int io_uring_submit(struct io_uring *ring)
This ultimately chains calls down to io_uring_enter() syscall (raw man page) so you can read that for more detail.
Update: The questioner says moving the assignment solved their problem so I invested some time thinking about the text they quoted. Upon further reading I have picked up a subtlety (emphasis added):
user_data is common across op-codes, and is untouched by the kernel. It's simply copied to the completion event, cqe, when a completion event is posted for this request.
There's a similar statement earlier in the document (again emphasis added):
The cqe contains a user_data field. This field is carried from the
initial request submission, and can contain any information that the the application needs to identify said request. One common use case is to have it be the pointer of the original request. The kernel will not touch this field, it's simply carried straight from submission to completion event.
The statement applies to io_uring kernel syscalls but io_uring_prep_openat() / io_uring_prep_statx() are liburing functions. liburing is a userspace helper library so the statements above about user_data do not have to apply to all liburing functions.
If the field is always zero then how can it be used?
The field is being zeroed by certain liburing preparation helper functions. In this case it can be only be set (and retain the new value) after those helper function have been called. The io_uring kernel syscalls behave per the quote.

Does TUN interface support recvmmsg/sendmmsg to receive/send multiple messages in a single system call

sendmmsg/recvmmsg provide the option to send and receive multiple packets on socket in a single system call. Are these operations supported for TUN adaptor on Linux using C socket API.
Here is the sample code I tried but get errno=Socket operation on non-socket
struct mmsghdr hdrs[ARRAY_SIZE];
unsigned char data[ARRAY_SIZE][BUFF_SIZE];
struct iovec iovecs[ARRAY_SIZE];
memset(hdrs, 0, sizeof(hdrs));
for (int i = 0; i < ARRAY_SIZE; i++)
{
iovecs[i].iov_base = data[i];
iovecs[i].iov_len = BUFF_SIZE;
hdrs[i].msg_hdr.msg_iov = &iovecs[i];
hdrs[i].msg_hdr.msg_iovlen = 1;
}
while (true)
{
LOG_DEBUG(log__) << "blocking to read on fd=" << fd;
int retVal = recvmmsg(fd, hdrs, ARRAY_SIZE, 0, NULL);
LOG_DEBUG(log__) << "retVal=" << retVal;
if (retVal < 0)
{
LOG_ERROR(log__) << "failed in recvmmsg, retVal=" << retVal << ", errno=" << strerror(errno);
continue;
}
LOG_DEBUG(log__) << "read " << retVal << " messages";
for (int i = 0; i < retVal; i++)
{
LOG_DEBUG(log__) << "read data of length " << hdrs[i].msg_len;
}
}

I want to ask about the use of the kea dhcp server, I hope someone can help me

I want to modify the dhcp4 source code to notify the ddns server to set the dynamic domain name when assigning the lease, but I added the nanomsg statement in dhcp4_srv.cc. When my nanomsg performs shutdown or close, the dhcp4 service will automatically close. This is why there is no other way to implement dynamic domain names (my dynamic domain name mainly sends the field set by the foreground and the mac address and IP address to the ddns server, or it may be the login account of the foreground).
Someone can help me? thank you very much.
if (lease) {
// We have a lease! Let's set it in the packet and send it back to
// the client.
// 我们有租约! 让我们在数据包中进行设置,然后将其发送回客户端。
if (fake_allocation) {
//租约建议
LOG_INFO(lease4_logger, DHCP4_LEASE_ADVERT)
.arg(query->getLabel())
.arg(lease->addr_.toText());
} else {
//成功授予租约
LOG_INFO(lease4_logger, DHCP4_LEASE_ALLOC)
.arg(query->getLabel())
.arg(lease->addr_.toText())
.arg(lease->valid_lft_);
int rc = 0;
int pair_socket = 0;
int str_len = 0;
char buf[256] = { 0 };
char buf1[256] = { 0 };
int timeo = 5000;
//计算长度
str_len = strlen(HELLOWORLD);
//初始化socket
pair_socket = nn_socket(1, NN_PAIR);
if (pair_socket == -1) {
printf("nn_socket failed! error: %s.\n", nn_err_strerror(errno));
//system("pause");
nn_err_abort();
//return 0;
}
//设置超时
rc = nn_setsockopt(pair_socket, 0, NN_SNDTIMEO, &timeo, sizeof(timeo));
rc = nn_setsockopt(pair_socket, 0, NN_RCVTIMEO, &timeo, sizeof(timeo));
//连接服务端
rc = nn_connect(pair_socket, SOCKET_ADDRESS2);
if (rc < 0) {
printf("bind failed! error: %s.\n", nn_err_strerror(errno));
//system("pause");
nn_err_abort();
//return 0;
}
//将hello world复制到buf中
memcpy(buf, HELLOWORLD, str_len);
//发送数据
rc = nn_send(pair_socket, buf, str_len, 0);
if (rc < 0) {
printf("nn_send failed! error: %s.rc = %d.\n", nn_err_strerror(errno), rc);
}
//打印
printf("send:%s\n", buf);
//这里主要是测试使用,平时项目不要使用标签
//接收数据
rc = nn_recv(pair_socket, buf1, 256, 0);
if (rc < 0) {
printf("nn_recv failed! error: %s.rc = %d.\n", nn_err_strerror(errno), rc);
}
//打印
printf("recv:%s\n", buf1);
memset(buf1, 0, 256);
//Sleep(1000);
//关闭套接字
rc = nn_shutdown(pair_socket, 1);
if (rc != 1) {
printf("nn_close failed! error: %s.\n", nn_err_strerror(errno));
//system("pause");
nn_err_abort();
//return 0;
}
std::cout << "testtttttttttttttttttttttttttttttttttt "
<< "hostnameeeeeeeeeeeeeeeeeeeeeeeeeeeeee:" << lease->hostname_ << std::endl;
std::cout << "testtttttttttttttttttttttttttttttttttt "
<< "ipppppppppppppppppppppppppppppppppppp:" << lease->addr_.toText() << std::endl;
std::cout << "lease ALLOC " << __LINE__ << " file name " << __FILE__
<< " HOST:" << lease->hostname_ << std::endl;
std::cout << "lease ALLOC " << __LINE__ << " file name " << __FILE__
<< " IP:" << lease->addr_.toText() << std::endl;
}
It sounds like you need some general information on the KEA software developed by Internet Systems Consortium aka: https://kea.isc.org/ I would read through the Kea docs, mailing lists and the general development wiki to try to hone in on the specific issue you are having with your code. Once you are able explain the issue you are having with your code you can edit your question with your details, and there is a much better chance that you will get more meaningful answers from this site.

How to continuously send and receive packets?

I am programming a server / client communication system where a client requests to log in to the server and can request to view other client's online status. I can make the client log in fine, but when I try to log in (successfully) and then send another packet requesting another client's information, the server does not receive that packet.
the main part of the server, not the technical connection stuff starting from bind Server:
Users client[2]; //I intialized them already
//Bind
bind(WelcomeSocket, (sockaddr*)&SvrAddr, sizeof(SvrAddr));
//listening
listen(WelcomeSocket, 5);
//temp users
Users temp;
//while loop for the connection
while (1) {
ConnectionSocket = accept(WelcomeSocket, NULL, NULL);
if (recv(ConnectionSocket, RxBuffer, sizeof(RxBuffer), 0))
cout << "WORKEDDDDDDDD" << endl;
memcpy(&temp, RxBuffer, sizeof(struct Users));
cout << temp.message << temp.userName << endl << endl;
//check which message type is being sent
switch(temp.message) {
//if message type 1
case 1 :
for (int i = 0; i < 2; i++) {
//if receieved username matches with any username in the database
if (strcmp(temp.userName, client[i].userName) == 0) {
//assign the recieved users information to the matched one in database
strcpy(client[i].userName, temp.userName);
client[i].online = true;
client[i].message = 2;
cout << client[i].userName << endl << client[i].online << endl;
//send the acknowledgement packet
send(ConnectionSocket, (char *)&client[i], sizeof(struct Users), 0);
}
}
closesocket(ConnectionSocket);
break;
//if message type 3
case 3 :
cout << "3";
break;
default :
break;
}
}
closesocket(ConnectionSocket);
WSACleanup();
}
Client:
connect(ClientSocket, (sockaddr*)&SvrAddr, sizeof(SvrAddr));
//cout << "Name: ";
//cin >> login;
//Send request to login
int log;
char * name = new char[128];
char * request = new char[128];
Users client;
Users talkto;
cout << "To login press (1). ";
cin >> log;
flushall();
if (log == 1) {
cout << "Username : ";
cin.getline(name, 128, '\n');
flushall();
//Set client login info
strcpy(client.userName, name);
client.message = 1;
send(ClientSocket, (char *)&client, sizeof(struct Users), 0);
//Recieve acknowledgement
recv(ClientSocket, RxBuffer, sizeof(RxBuffer), 0);
//create temp users
Users temp;
memcpy(&temp, RxBuffer, sizeof(struct Users));
if (temp.message == 2) {
cout << "Enter user for user information: ";
cin.getline(talkto.userName, 128, '\n');
flushall();
talkto.message = 3;
//send request for user information packet
if (send(ClientSocket, (char *)&talkto, sizeof(struct Users), 0))
cout << "SENDT" << endl;
}
//cout << temp.userName << endl << temp.online << endl << temp.message;
closesocket(ClientSocket);
WSACleanup();
}
The structure for users
struct Users {
int message;
char userName[50];
char ipAddress[50];
int PortNumber;
bool online;
};
Not sure why it's not receiving information more than one time
ConnectionSocket = accept(WelcomeSocket, NULL, NULL);
You should put that before the loop, not inside, because the accept function is waiting for another client, blocking you from receiving another packet.
Also, when your recv call returns 0, it means that the connection has closed, and it's the end of the stream, so you should break from your loop when recv returns 0, or some unexpected SOCKET_ERROR.
For each client connection, your server performs accept-recv-send-closesocket. Period.
A connection to client is closed after the server processes the first packet. You have either to establish a new connection before sending each packet at client side (but that would probably make your login procedure useless) or to make server be able to maintain several concurrent client connections polling them in an infinite loop, maybe in a separate thread(s).

Example Code for MemCached in C

I am looking for some sample C code for using memcache to set a value
Connecting to Server/Port
using multiple memcache_set
Close
I have the app running in PHP in 5 lines of code but can not find any good memcache samples in C which I need to port to.
This is a great memcached sample in C
#include <libmemcached/memcached.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
//memcached_servers_parse (char *server_strings);
memcached_server_st *servers = NULL;
memcached_st *memc;
memcached_return rc;
char *key = "keystring";
char *value = "keyvalue";
char *retrieved_value;
size_t value_length;
uint32_t flags;
memc = memcached_create(NULL);
servers = memcached_server_list_append(servers, "localhost", 11211, &rc);
rc = memcached_server_push(memc, servers);
if (rc == MEMCACHED_SUCCESS)
fprintf(stderr, "Added server successfully\n");
else
fprintf(stderr, "Couldn't add server: %s\n", memcached_strerror(memc, rc));
rc = memcached_set(memc, key, strlen(key), value, strlen(value), (time_t)0, (uint32_t)0);
if (rc == MEMCACHED_SUCCESS)
fprintf(stderr, "Key stored successfully\n");
else
fprintf(stderr, "Couldn't store key: %s\n", memcached_strerror(memc, rc));
retrieved_value = memcached_get(memc, key, strlen(key), &value_length, &flags, &rc);
printf("Yay!\n");
if (rc == MEMCACHED_SUCCESS) {
fprintf(stderr, "Key retrieved successfully\n");
printf("The key '%s' returned value '%s'.\n", key, retrieved_value);
free(retrieved_value);
}
else
fprintf(stderr, "Couldn't retrieve key: %s\n", memcached_strerror(memc, rc));
return 0;
}
Here is a slightly more complete answer, which also shows how to get and delete from a memcached server. The code is technically in C++, but doesn't use any features that would be unfamiliar to most C programmers.
To compile: g++ sample.cc -o sample -ggdb -O3 -lmemcached
To run: ./sample -s localhost -p 11211
The functions whose names begin with mcd_ show the basics of inserting, deleting, and getting, as well as connecting and disconnecting from memcached. The remaining code makes use of those functions to insert 50 key/value pairs, verify they all are in the server, remove half the pairs, and then verify that the right keys remain / are gone.
#include <iostream>
#include <string>
#include <unistd.h>
#include <vector>
#include <libmemcached/memcached.h>
// NB: I know that `using namespace std` at global scope is bad form, and that
// global variables are bad form. However, they help keep this answer under
// 200 lines.
using namespace std;
/// The connection to memcached
memcached_st *mcd;
/// A global set of key/value pairs that we manufacture for the sake of this
/// example
vector<pair<string, string>> kv_pairs;
/// Put a key/value pair into memcached with expiration 0, no special flags
bool mcd_set(const string &key, const string &val) {
auto rc = memcached_set(mcd, key.c_str(), key.length(), val.c_str(),
val.length(), (time_t)0, (uint32_t)0);
if (rc == MEMCACHED_SUCCESS)
return true;
cout << "Error in mcd_set(): " << memcached_strerror(mcd, rc) << endl;
return false;
}
/// Delete a key/value pair from memcached. return true on success
/// NB: `(time_t)0` is an expiration of `0`, i.e., immediately
bool mcd_delete(const string &key) {
auto rc = memcached_delete(mcd, key.c_str(), key.length(), (time_t)0);
if (rc == MEMCACHED_SUCCESS)
return true;
cout << "Error in mcd_delete(): " << memcached_strerror(mcd, rc) << endl;
return false;
}
/// Get a value from the kv store, using its key to do the lookup. return
/// true on success. On success, the by-ref out parameter `val` will be set.
bool mcd_get(const string &key, string &val) {
memcached_return rc;
size_t len;
uint32_t flags = 0;
char *res = memcached_get(mcd, key.c_str(), key.length(), &len, &flags, &rc);
if (rc == MEMCACHED_SUCCESS) {
val = string(res, len);
free(res);
return true;
}
// NB: skip next line, because we don't want error messages on a failed get:
//
// cout << "Error in mcd_get(): " << memcached_strerror(mcd, rc) << endl;
return false;
}
/// Connect to a single memcached server on the provided port
bool mcd_connect(const string &servername, const int port) {
mcd = memcached_create(nullptr);
memcached_return rc;
memcached_server_st *servers = nullptr;
servers =
memcached_server_list_append(servers, servername.c_str(), port, &rc);
rc = memcached_server_push(mcd, servers);
if (rc == MEMCACHED_SUCCESS) {
cout << " Successfully connected to " << servername << ":" << port << endl;
return true;
}
cout << "Error in mcd_connect(): " << memcached_strerror(mcd, rc) << endl;
return false;
}
/// Close the connection to memcached
void mcd_shutdown() {
memcached_free(mcd);
cout << " Successfully disconnected\n";
}
/// Create a bunch of key/value pairs
void build_kv_pairs(int howmany) {
for (int i = 0; i < howmany; ++i) {
string key = "key" + to_string(i) + "_______";
string val = "val" + to_string(i);
for (int i = 0; i < 100; ++i)
val += ("_" + to_string(i));
kv_pairs.push_back({key, val});
}
}
/// insert a bunch of k/v pairs into memcached
bool put_kv_pairs(int howmany) {
for (int i = 0; i < howmany; ++i) {
if (!mcd_set(kv_pairs[i].first, kv_pairs[i].second)) {
cout << "Error inserting key `" << kv_pairs[i].first << "`\n";
return false;
}
}
cout << " put_kv_pairs(" << howmany << ") completed successfully\n";
return true;
}
/// Remove a sequence of keys from memcached
///
/// NB: Here and below, we use first/last/stride so that we can vary wich
/// key/value pairs are operated on
bool delete_kv_pairs(int first, int last, int stride) {
for (int i = first; i <= last; i += stride) {
if (!mcd_delete(kv_pairs[i].first)) {
cout << "Error removing key `" << kv_pairs[i].first << "`\n";
return false;
}
}
cout << " delete_kv_pairs(" << first << ", " << last << ", " << stride
<< ") completed successfully\n";
return true;
}
/// Verify that a sequence of keys is in memcached, with the right expected
/// values
bool check_present_pairs(int first, int last, int stride) {
for (int i = first; i <= last; i += stride) {
string value;
if (!mcd_get(kv_pairs[i].first, value)) {
cout << "Error getting key `" << kv_pairs[i].first
<< "`: key not found\n";
return false;
}
if (value != kv_pairs[i].second) {
cout << "Value error while getting key `" << kv_pairs[i].first << "`\n";
cout << " Expected: `" << kv_pairs[i].second << "`\n";
cout << " Found: `" << value << "`\n";
return false;
}
}
cout << " check_present_pairs(" << first << ", " << last << ", " << stride
<< ") completed successfully\n";
return true;
}
/// Verify that a sequence of keys is *not* in memcached
bool check_missing_pairs(int first, int last, int stride) {
for (int i = first; i <= last; i += stride) {
string value;
if (mcd_get(kv_pairs[i].first, value)) {
cout << "Error getting key `" << kv_pairs[i].first
<< "`: key unexpectedly found\n";
return false;
}
}
cout << " check_missing_pairs(" << first << ", " << last << ", " << stride
<< ") completed successfully\n";
return true;
}
int main(int argc, char **argv) {
// Parse args to get server name (-s) and port (-p)
string servername = "";
int port;
long opt;
while ((opt = getopt(argc, argv, "s:p:")) != -1) {
switch (opt) {
case 's':
servername = string(optarg);
break;
case 'p':
port = atoi(optarg);
break;
}
}
// Create a bunch of key/value pairs to use for the experiments
int howmany = 50;
build_kv_pairs(howmany);
// Connect to memcached
mcd_connect(servername, port);
// insert all the pairs, make sure they are all present
put_kv_pairs(howmany);
check_present_pairs(0, howmany - 1, 1);
// Delete the even pairs
delete_kv_pairs(0, howmany - 2, 2);
// ensure the odd pairs are present
check_present_pairs(1, howmany - 1, 2);
// ensure the even pairs are not present
check_missing_pairs(0, howmany, 2);
mcd_shutdown();
}
The code should produce output like the following:
Successfully connected to localhost:11211
put_kv_pairs(50) completed successfully
check_present_pairs(0, 49, 1) completed successfully
delete_kv_pairs(0, 48, 2) completed successfully
check_present_pairs(1, 49, 2) completed successfully
check_missing_pairs(0, 50, 2) completed successfully
Successfully disconnected

Resources