-4

i have tried to receive data form my connection by this code on python 2.7 :

    server = socket(AF_INET, SOCK_STREAM)
    server.bind(('0.0.0.0', 21))
    server.listen(1)
    client , addr = server.accept()
    data = client.recv(2048)

When i have to print or send to another connection my data it's working , however i want to add those lines :

if(data == "/disconnect") :
     <disconnect blha blha... you know >
else :
     <print the data and send it back blha blha... >

( i have checked without that if statement and " disconnect blha blha .. " works nicely )

it's just pass the code so when my client requests to disconnect the request is sent to the server as the "original" messages .. (the sever don't kick him)

what should i do ? Thanks !

Itay2924
  • 21
  • 3
  • [A TCP socket is just a stream of bytes, not a stream of messages.](http://stupidpythonideas.blogspot.com/2013/05/sockets-are-byte-streams-not-message.html) When you call `client.recv(2048)`, you might get the first two bytes of a `send` from the client, or the whole thing, or the last part of one `send`, two more `send`s, and the first part of a fourth. So, it's very unlikely to equal `/disconnect`. – abarnert Jun 25 '18 at 22:53
  • If you want to handle messages rather than bytes with TCP, you have to come up a message-framing protocol. The simplest one is that each message is a bunch of bytes ending in a `b'\n'`, or a bunch of UTF-8-encoded characters ending in UTF-8-encoded `'\n'`. This doesn't work if your messages can include embedded newlines, but if they can't, it's dead easy, especially because the `makefile` method on `socket` objects does the whole protocol implementation for you. – abarnert Jun 25 '18 at 22:55
  • If you're not sure whether that's the problem, try adding a `print(repr(data))` to the server and see what it prints out. If you're getting `'/disco'` or `'/me is leaving\n/disconnect'` or something like that, it's your problem, and it's a dup of [this question](https://stackoverflow.com/questions/17664544/) (and various others that we could look up and add). If you're getting something different, then you probably have another problem _on top of_ that one, but we can't debug it unless you show us what you get. (It would also help to see an example client, and example user input if relevant.) – abarnert Jun 25 '18 at 22:58
  • Write your Port : 21 Host name : DESKTOP-7G40JN7 Server Listening on : (my_ip) : 21 (phone_ip) : Connected ! (phone_ip) : 'dmdk\r\n' (phone_ip) : 'hey\r\n' # that output for "hey" <<< -- (phone_ip) : '/exit\r\n' – Itay2924 Jun 25 '18 at 23:36
  • That doesn't even contain `/discconnect` anywhere in the stream. So why are you expecting `data == "/disconnect"` to be true? – abarnert Jun 26 '18 at 00:10
  • Sorry sorry i have changed it to "/exit" but there is the same problem – Itay2924 Jun 27 '18 at 11:28
  • You should edit the question to have enough information to solve the problem, instead of just adding comments. But meanwhile: your client is actually sending messages delimited with `/r/n`, which makes solving the first problem easy. But you have a second problem: you aren’t stripped off those newlines to process the message—which is even easier to fix, but still needs fixing. When I get to a computer I can find a good dup if one exists, write an answer if not. – abarnert Jun 27 '18 at 16:24

1 Answers1

0

You have two problems, and you need to fix both of them.


First, a TCP socket is just a stream of bytes. When you do a recv, you're not going to get exactly one message sent by send from the other side, you're going to get whatever's in the buffer at the moment—which could be two messages, or half a message, or anything else. When you're just testing with localhost connections on a computer that isn't heavily loaded, on many platforms, it will do what you're hoping for >99% of the time—but that just makes the problem hard to debug, it doesn't fix it. And as soon as you try to access the same code over the internet, it'll start failing most of the time instead of rarely.

Fortunately, the client appears to be sending messages as text, without any embedded newlines, with a \r\n Windows-style end-of-line between each message. This is a perfectly good protocol; you just have to write the code to handle that protocol on the receive side.

The second problem is that, even if you happen to get exactly one message send, that message includes the \r\n end-of-line. And '/disconnect\r\n' == '/disconnect' is of course going to be false. So, as part of your protocol handler, you need to strip off the newlines.


As it happens, you could solve both problems by using the makefile method to give you a file object that you can iterate, or readline on, etc., just like you do with a file that you open from disk, which you probably already know how to handle.

But it's worth learning how to do this stuff, so I'll show you how to do it manually. The key is that you keep a buffer around, add each recv onto that buffer, and then split it into lines, put any remainder back on the buffer, and process each line as a message. There are more elegant/concise ways to write this, but let's keep it dead simple:

buf = ''
while True:
    data = client.recv(2048)
    buf += data
    lines = buf.split('\r\n')
    buf = lines.pop()
    for line in lines:
        # line is now a single message, with the newline stripped
        if line == "/disconnect":
            # do disconnect stuff
        else:
            # do normal message stuff

That's all you need to get the basics working. But in a real server, you also need some code to handle two other conditions—because clients don't always shut down cleanly. For example, if a client gets disconnected from the internet before it can send a /disconnect message, you don't want to keep spinning and reading nothing forever, you want to treat it as a disconnect.

  • if not data: means the client has done a clean (at the TCP level) shutdown. So, you need to disconnect and break out of the receive loop.
    • Depending on your design, it may be legal to shutdown only the send side and wait for a final reply from the server, so you want to make sure you've finished sending whatever you have. (This is common in many internet protocols.)
    • It may even be legal to not send a final newline before shutting down; if you want to support this, you should check if buf: and if so, treat buf as one last command. (This is not common in many protocol—but is a common bug in clients, so, e.g., many web servers will handle it.)
  • try:/except Exception as e: will catch all kinds of errors. These errors mean the socket is no longer usable (or that there's a serious error in your code, of course), so you want to handle this by throwing away the connection and breaking out of the receive loop, without first sending any final response or reading any final message.
    • It's almost always worth logging that e in some way (maybe just print 'Error from', addr, repr(e)), so if you're getting unexpected exceptions you have something to debug.
abarnert
  • 354,177
  • 51
  • 601
  • 671