From C file descriptor to socket?

I'm doing a project where I want a server to stream ncurses data (ascii) to a client.

The ncurses newterm() function takes a C style file descriptor, which is where it sends the generated ascii codes (default fd is stdout). I'm stuck on how to get from here to sending to the client.

I can think of two possibilities:

  1. Let a byte buffer emulate a file descriptor and give newterm() this "fake" file descriptor. If this works, it should then be easy (I hope) to send the contents of the buffer to the client. But I don't know if it's possible to emulate a file descriptor in this way. (Googling didn't help me.)

  2. Connect the file descriptor in newterm() directly to a socket in some way, if this is possible.

I'm totally new to Swift, but know some C (so I'm ok with the C types if I could just get them to work with Swift...). Also, I understand that there are different socket libraries available -- is Swift NIO the best one to use for me? I'm using Swift 4.2 so I'm limited to Swift NIO 1 -- is this a problem? I could upgrade to 10.14 in order to get Swift 5 + Swift NIO 2 if that would help.

Any help would be much appreciated!

While I love that you’re excited about using SwiftNIO, it seems like it might be overkill here. If you need to pass a C file descriptor to your library, then you can just use the socket Layer directly, as sockets are file descriptors.

This will likely be easier than almost any other alternative.

lukasa wrote:

This will likely be easier than almost any other alternative.

Assuming your don’t care about security. IMO data like this should be going over TLS, and at that point SwiftNIO would be a good option.

Given that ncurses requires a file descriptor, you can call socketpair (or pipe), pass one end to ncurses, and monitor the other end for data that you send down the network connection.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

The newterm function takes two FILE *, not a file descriptor, according to this man page.

On Apple platforms, you can use funopen to create a FILE * that calls callbacks provided by you to send/receive data. On Linux, you can use fopencookie for the same effect (with a slightly different API).

1 Like

Thanks everyone for replies. Sorry for late answer (Easter holidays and travelling...)

I should say I also want the clients to return data, in the form of keys pressed (the clients are the players in the game), in case that changes the optimal solution.

The system keeps blocking my account when I post something (I guess because I'm new), hence my irregular answers...

My impression after googling was that there is no native socket implementation in Swift but that you must use external libraries, like Swift NIO or BlueSocket. Is this not so, or are you saying that I'm better off using C socket functions in Swift instead? (I thought this was an "inelegant" solution...)

(I'm not really "excited" to use any particular library, I just want to find the best tool for the job -- although Swift NIO does seem good. :slight_smile: )

I don't care about security (right now), so I will postpone thinking about TLS. TLS in C/C++ seems to be a hassle, but perhaps it's easier in Swift?

Thanks for the suggestion -- what's the difference between giving a socket fd to newterm() and using pipe (or socketpair)? (Remember, I'm a rookie...) And these are C implementations only, right?

Thanks for clearing that up, I'm obviously confused... I understand that a file descriptor can be wrapped in a FILE*. It's only the newterm output that I'm worrying about (won't be using ncurses input functions).

So, I need the FILE* to point to (1) a socket, or (2) a data array (which will be sent to a socket). I take it you are suggesting a way to sending the data to a data array? Is there an advantage to this, rather than feeding newterm() directly to a socket?

I thought you were using SwiftNIO.

If you're using SwiftNIO, you want to use funopen or fopencookie to create the FILE * for newterm to write to, and have the callback functions pass the data to your SwiftNIO pipeline.

If you don't want to use SwiftNIO, then you can create your socket yourself using the standard Unix system calls (socket and whichever of bind, listen, accept, and connect you need), then wrap it in a FILE * using fdopen.

2 Likes

There isn’t really any such thing as a “native” socket implementation in any language: a socket is fundamentally a kernel construct, and so it’s language agnostic. What there are are interfaces to the various system calls that make up the socket interface.

In Swift the lowest overhead way of getting to this is to just use libc. However, you can use a wrapper library: if the wrapper library gives you a way to extract a file descriptor then you can use that to set a socket up. SwiftNIO does not do this by design, so to use that you’d have to use a callback based solution as suggested above with FILE *.

I think some confusion in this thread has resulted from mismatches in understanding of the word “socket”. Happily @mayoff suggests what you want is actually a FILE * (which is a libc stream), and that’s an interface you can shim to do whatever you want.

1 Like

I don't care about security (right now) …

You always need to care about security. Even if you defer tackling the problem while getting a prototype working, it’s important that you not wall yourself off from creating a secure solution down the path.

Fortunately, now that mayoff has corrected our various misconceptions about newterm, this isn’t going to be a problem. Setting up a FILE * to talk to either SwiftNIO (or Network framework directly, if you only care about Apple platforms) should be fairly straightforward, and once you do that you get TLS for ‘free’. Well, you have to enable it, and you have to worry about digital identity provisioning, and server trust evaluation, but the key point is that any decision to enable TLS in the future won’t require you to revisit this part of the problem.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

2 Likes

@mayoff
Thanks for the very hands-on advice!!

@lukasa
With "native" I was referring to functions using Swift data types rather than libc. But the info about sockets being a kernel construct is well taken -- now I understand that it's not such a big deal to mix libc and Swift libraries (other than data type conversions). I guess it's high time for me to walk through Beej's Guide to Network Programming...

Thanks for all the help!

1 Like

@eskimo
The reason security isn't needed (now) is because this is an university assignment, and I have some pretty severe time constraints. But I agree fully with your point, and it's good to get the hints on how to include TLS for future projects -- thanks!

(Note: The questions I have asked here are not part of the assignment -- it's because I wanted to do the implementation in a, for me, new language.)