-
-
Save marcom04/22860f1168330605cac3c448982b0393 to your computer and use it in GitHub Desktop.
Basic echo between Python3 client and C server, and vice versa, via socket using Python ctypes |
/* client.c */ | |
#include <sys/socket.h> | |
#include <arpa/inet.h> //inet_addr | |
#include <unistd.h> //write | |
#include <time.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#pragma pack(1) | |
typedef struct payload_t { | |
uint32_t id; | |
uint32_t counter; | |
float temp; | |
} payload; | |
#pragma pack() | |
void sendMsg(int sock, void* msg, uint32_t msgsize) | |
{ | |
if (write(sock, msg, msgsize) < 0) | |
{ | |
printf("Can't send message.\n"); | |
close(sock); | |
exit(1); | |
} | |
printf("Message sent (%d bytes).\n", msgsize); | |
return; | |
} | |
int main() | |
{ | |
const int PORT = 2300; | |
const char* SERVERNAME = "localhost"; | |
int BUFFSIZE = sizeof(payload); | |
char buff[BUFFSIZE]; | |
int sock; | |
int nread; | |
float mintemp = -10.0; | |
float maxtemp = 30.0; | |
time_t t; | |
srand((unsigned) time(&t)); | |
struct sockaddr_in server_address; | |
memset(&server_address, 0, sizeof(server_address)); | |
server_address.sin_family = AF_INET; | |
inet_pton(AF_INET, SERVERNAME, &server_address.sin_addr); | |
server_address.sin_port = htons(PORT); | |
if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { | |
printf("ERROR: Socket creation failed\n"); | |
return 1; | |
} | |
if (connect(sock, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) { | |
printf("ERROR: Unable to connect to server\n"); | |
return 1; | |
} | |
printf("Connected to %s\n", SERVERNAME); | |
payload data; | |
for(int i = 0; i < 5; i++) { | |
data.id = 1; | |
data.counter = i; | |
data.temp = mintemp + rand() / (RAND_MAX / (maxtemp - mintemp + 1.0) + 1.0); | |
printf("\nSending id=%d, counter=%d, temp=%f\n", data.id, data.counter, data.temp); | |
sendMsg(sock, &data, sizeof(payload)); | |
bzero(buff, BUFFSIZE); | |
nread = read(sock, buff, BUFFSIZE); | |
printf("Received %d bytes\n", nread); | |
payload *p = (payload*) buff; | |
printf("Received id=%d, counter=%d, temp=%f\n", | |
p->id, p->counter, p->temp); | |
} | |
// close the socket | |
close(sock); | |
return 0; | |
} |
#!/usr/bin/env python3 | |
""" client.py - Echo client for sending/receiving C-like structs via socket | |
References: | |
- Ctypes: https://docs.python.org/3/library/ctypes.html | |
- Sockets: https://docs.python.org/3/library/socket.html | |
""" | |
import socket | |
import sys | |
import random | |
from ctypes import * | |
""" This class defines a C-like struct """ | |
class Payload(Structure): | |
_fields_ = [("id", c_uint32), | |
("counter", c_uint32), | |
("temp", c_float)] | |
def main(): | |
server_addr = ('localhost', 2300) | |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
try: | |
s.connect(server_addr) | |
print("Connected to {:s}".format(repr(server_addr))) | |
for i in range(5): | |
print("") | |
payload_out = Payload(1, i, random.uniform(-10, 30)) | |
print("Sending id={:d}, counter={:d}, temp={:f}".format(payload_out.id, | |
payload_out.counter, | |
payload_out.temp)) | |
nsent = s.send(payload_out) | |
# Alternative: s.sendall(...): coontinues to send data until either | |
# all data has been sent or an error occurs. No return value. | |
print("Sent {:d} bytes".format(nsent)) | |
buff = s.recv(sizeof(Payload)) | |
payload_in = Payload.from_buffer_copy(buff) | |
print("Received id={:d}, counter={:d}, temp={:f}".format(payload_in.id, | |
payload_in.counter, | |
payload_in.temp)) | |
except AttributeError as ae: | |
print("Error creating the socket: {}".format(ae)) | |
except socket.error as se: | |
print("Exception on socket: {}".format(se)) | |
finally: | |
print("Closing socket") | |
s.close() | |
if __name__ == "__main__": | |
main() |
/* server.c */ | |
#include <sys/socket.h> | |
#include <arpa/inet.h> //inet_addr | |
#include <unistd.h> //write | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#pragma pack(1) | |
typedef struct payload_t { | |
uint32_t id; | |
uint32_t counter; | |
float temp; | |
} payload; | |
#pragma pack() | |
int createSocket(int port) | |
{ | |
int sock, err; | |
struct sockaddr_in server; | |
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) | |
{ | |
printf("ERROR: Socket creation failed\n"); | |
exit(1); | |
} | |
printf("Socket created\n"); | |
bzero((char *) &server, sizeof(server)); | |
server.sin_family = AF_INET; | |
server.sin_addr.s_addr = INADDR_ANY; | |
server.sin_port = htons(port); | |
if (bind(sock, (struct sockaddr *)&server , sizeof(server)) < 0) | |
{ | |
printf("ERROR: Bind failed\n"); | |
exit(1); | |
} | |
printf("Bind done\n"); | |
listen(sock , 3); | |
return sock; | |
} | |
void closeSocket(int sock) | |
{ | |
close(sock); | |
return; | |
} | |
void sendMsg(int sock, void* msg, uint32_t msgsize) | |
{ | |
if (write(sock, msg, msgsize) < 0) | |
{ | |
printf("Can't send message.\n"); | |
closeSocket(sock); | |
exit(1); | |
} | |
printf("Message sent (%d bytes).\n", msgsize); | |
return; | |
} | |
int main() | |
{ | |
int PORT = 2300; | |
int BUFFSIZE = 512; | |
char buff[BUFFSIZE]; | |
int ssock, csock; | |
int nread; | |
struct sockaddr_in client; | |
int clilen = sizeof(client); | |
ssock = createSocket(PORT); | |
printf("Server listening on port %d\n", PORT); | |
while (1) | |
{ | |
csock = accept(ssock, (struct sockaddr *)&client, &clilen); | |
if (csock < 0) | |
{ | |
printf("Error: accept() failed\n"); | |
continue; | |
} | |
printf("Accepted connection from %s\n", inet_ntoa(client.sin_addr)); | |
bzero(buff, BUFFSIZE); | |
while ((nread=read(csock, buff, BUFFSIZE)) > 0) | |
{ | |
printf("\nReceived %d bytes\n", nread); | |
payload *p = (payload*) buff; | |
printf("Received contents: id=%d, counter=%d, temp=%f\n", | |
p->id, p->counter, p->temp); | |
printf("Sending it back.. "); | |
sendMsg(csock, p, sizeof(payload)); | |
} | |
printf("Closing connection to client\n"); | |
printf("----------------------------\n"); | |
closeSocket(csock); | |
} | |
closeSocket(ssock); | |
printf("bye"); | |
return 0; | |
} |
#!/usr/bin/env python3 | |
""" server.py - Echo server for sending/receiving C-like structs via socket | |
References: | |
- Ctypes: https://docs.python.org/3/library/ctypes.html | |
- Sockets: https://docs.python.org/3/library/socket.html | |
""" | |
import socket | |
import sys | |
import random | |
from ctypes import * | |
""" This class defines a C-like struct """ | |
class Payload(Structure): | |
_fields_ = [("id", c_uint32), | |
("counter", c_uint32), | |
("temp", c_float)] | |
def main(): | |
PORT = 2300 | |
server_addr = ('localhost', PORT) | |
ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
print("Socket created") | |
try: | |
# bind the server socket and listen | |
ssock.bind(server_addr) | |
print("Bind done") | |
ssock.listen(3) | |
print("Server listening on port {:d}".format(PORT)) | |
while True: | |
csock, client_address = ssock.accept() | |
print("Accepted connection from {:s}".format(client_address[0])) | |
buff = csock.recv(512) | |
while buff: | |
print("\nReceived {:d} bytes".format(len(buff))) | |
payload_in = Payload.from_buffer_copy(buff) | |
print("Received contents id={:d}, counter={:d}, temp={:f}".format(payload_in.id, | |
payload_in.counter, | |
payload_in.temp)) | |
print("Sending it back.. ", end='') | |
nsent = csock.send(payload_in) | |
print("Sent {:d} bytes".format(nsent)) | |
buff = csock.recv(512) | |
print("Closing connection to client") | |
print("----------------------------") | |
csock.close() | |
except AttributeError as ae: | |
print("Error creating the socket: {}".format(ae)) | |
except socket.error as se: | |
print("Exception on socket: {}".format(se)) | |
except KeyboardInterrupt: | |
ssock.close() | |
finally: | |
print("Closing socket") | |
ssock.close() | |
if __name__ == "__main__": | |
main() |
Hi,
if you need to send just an array of uint32, let's say pylist
, you could declare it like this in Python:
# Python list of integers to send
pylist = [1, 2, 3, 4]
# C-type array of c_uint32
array_len = 4
Int32Array = c_uint32 * array_len
then send/receive it this way:
print ""
payload_array = Int32Array(*pylist) # can also give the values directly: Int32Array(1, 2, 3, 4)
print "Sending an array of uint32 values: "
for i in payload_array:
print i,
nsent = s.send(payload_array)
print "\nSent %d bytes" % nsent
buff = s.recv(sizeof(payload_array))
payload_in = Int32Array.from_buffer_copy(buff)
print "Received an array of uint32 values: "
for i in payload_in:
print i,
On the server side you can handle it like this:
while ((nread=read(csock, buff, BUFFSIZE)) > 0)
{
printf("\nReceived %d bytes\n", nread);
uint32_t *p;
p = calloc(arrayLen, sizeof(uint32_t)); // you need to know arrayLen beforehand
p = (uint32_t*) buff;
printf("Received array of int: ");
for(int i=0; i<arrayLen; i++)
{
printf("%d ", *(p + i));
}
printf("\n");
printf("Sending it back.. ");
sendMsg(csock, p, arrayLen*sizeof(uint32_t));
}
Thanks, works like a charm. I've adapted it to send the arrayLen with another s.send() beforehand to make it more dynamic.
I'm wondering though whether this could get more neat by including the uint32 array in the Payload/payload_t structs. Unless we are always bound from knowing the arrayLen?
Yes sure, you can include the uint32 array inside a Payload structure, along with other data. Not sure how to handle dynamic size array in that case, though
Hello, I'm trying to sent a str from client to server using the ctype structure but I couldn't, could you help me?
@Mario-RC Hello, I am trying the same but to no result. Have you been able to do it?
@Mario-RC Hello, I am trying the same but to no result. Have you been able to do it?
@xicocana Hi! I finally decided not to use ctype structure, instead I encoded the message and sent it at a byte level.
You can check my code here: https://github.com/Mario-RC/UDP-sockets
@Mario-RC thanks for the fast reply! Yesterday I ended up doing the same. I'll still star your rep it looks useful for the future! thanks again
@xicocana It is a pleasure, thank you very much!
Hey, thanks for the gist. Can you guide me on the vice-versa process? C client to Python server?
Hi @pra-dan, the payload handling is quite similar, just need to switch roles. I added the files to show an example of C client and Python server.
Awesome code! Thanks mate!
Thanks for the example. I'm trying to send over an array of c_uint32 values, and unpack them in C but without any luck so far. Any chance you could show how to do that?