Skip to content

Instantly share code, notes, and snippets.

@marcom04
Last active April 20, 2024 18:14
Show Gist options
  • Save marcom04/22860f1168330605cac3c448982b0393 to your computer and use it in GitHub Desktop.
Save marcom04/22860f1168330605cac3c448982b0393 to your computer and use it in GitHub Desktop.
Basic echo between Python client and C server via socket using Python ctypes
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()
@eandroul
Copy link

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?

@marcom04
Copy link
Author

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));
}

@eandroul
Copy link

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?

@marcom04
Copy link
Author

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

@Mario-RC
Copy link

Hello, I'm trying to sent a str from client to server using the ctype structure but I couldn't, could you help me?

@xicocana
Copy link

@Mario-RC Hello, I am trying the same but to no result. Have you been able to do it?

@Mario-RC
Copy link

@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

@xicocana
Copy link

@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

@Mario-RC
Copy link

@xicocana It is a pleasure, thank you very much!

@pra-dan
Copy link

pra-dan commented Feb 5, 2021

Hey, thanks for the gist. Can you guide me on the vice-versa process? C client to Python server?

@marcom04
Copy link
Author

marcom04 commented Feb 7, 2021

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.

@waelmas
Copy link

waelmas commented Sep 29, 2021

Any ideas on how to modify the above to communicate with FastAPI Websockets either directly from the C client or from the Python server?

Something like this:
C client code ----> Python server.py <----> FastAPI
OR
C client code ----> FastAPI

@marcom04 @Mario-RC

@guzminho
Copy link

guzminho commented Apr 7, 2022

Awesome code! Thanks mate!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment