Amritha
Amritha

Reputation: 21

Network programming issue

I have written a client server program in which the server sends a program to the client, and the client executes the received program. In this case it is a line drawing program in OpenGL. The problem is that on running the server and client the whole program i.e. sending of the program to server and client execution takes place at times, but at times the execution does not take place. The client gets connected and then its stuck. The program doesn't get stopped either.The same works in my friends system,but in mine it works only sometimes(most of the times it wont). What could be the reason? Am I missing something?

My server code:

from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL import *
import sys
import threading
import os
import socket

class ClientThread ( threading.Thread ):
 # Override Thread's __init__ method to accept the parameters needed:
 def __init__ ( self, channel, details ):
  self.channel = channel
  self.details = details
  threading.Thread.__init__ ( self )

 #Codes to be executed when thread is executed:
 def run ( self ):
  a1=self.channel.recv(1024)
  print "client says:"+a1
  print 'Received connection:', self.details [ 0 ]
  finp = open("stringcheck.py","r")
  #self.channel.send("#start\n")
  info = finp.readlines() 
  for record in info:
   self.channel.send(record)
  #self.channel.send(info)
  self.channel.send("#p") 


server = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
server.bind ( ( '127.0.0.1', 4500) )
#Listens for connections made to socket.
#Specifies maximum number of queued connection.
server.listen ( 5 )
while True:
 channel, details = server.accept()
 #Create an instance of thread class and call its start method.
 ClientThread ( channel, details ).start()

My client code:

from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL import *
OpenGL.ERROR_CHECKING=False
import os
import socket
import threading
import thread
import subprocess

class ConnectionThread( threading.Thread ):

 def run ( self ):

  client = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
  client.connect( ( '127.0.0.1', 9000) )
  client.send("connected")
  a=client.recv(1024)
  b=a
  f=1 
  while f:
   print 'sdf'
   a = client.recv(1024)
   print 'qwe'
   if a=="#p":
    f=0
    break
   b+=a
  print b
  exec(b)

  client.close()
ConnectionThread().start()
 from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL import *
OpenGL.ERROR_CHECKING=False
import os
import socket
import threading
import thread
import subprocess

class ConnectionThread( threading.Thread ):

 def run ( self ):

  client = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
  client.connect( ( '127.0.0.1', 9000) )
  client.send("connected")
  a=client.recv(1024)
  b=a
  f=1 
  while f:

   a = client.recv(1024)
   print 'qwe'
   if a=="#p":
    f=0
    break
   b+=a
  print b
  exec(b)

  client.close()
ConnectionThread().start()

Upvotes: 2

Views: 493

Answers (1)

Adrien Plisson
Adrien Plisson

Reputation: 23293

1. a TCP socket is a stream (packets are an implementation detail)

the network stack is optimized in order to limit the number of packets sent. thus, when you call send() multiple times on a socket, the network stack is free to split (or not split) the data between multiple packets.

on a computer running on a real network, which is much slower than a loopback connection, if you still have a packet waiting to be sent at the time you are call send(), then the new data is appended to the waiting packet. on the receiving side, the network stack is free to merge multiple packets (data received from a socket is buffered) to present you the data at once.

so, in the server, you write:

for record in info:
    self.channel.send(record)
self.channel.send("#p") 

the last packet may contain the end of the program AND the terminator, joined together. in the client:

a = client.recv(1024)
if a=="#p":
    break

the terminator may not be at the beginning of the received packet, and may not be the only characters existing in the packet. if this is the case, then you do not detect the terminator, do not exit the loop, call recv() again, and stall here since recv() is a blocking call and the server will never send any data anymore.

so, you have to choose another way of knowing when the server has finished sending the data. there are many possibilities:

  • the first obvious way is on the client, instead of if a=="#p":, write if a.endswith("#p"):. this corrects the problem existing in your code
  • you can also choose to first send the length of the program before sending it. the client reads the length, then reads that number of characters and stops
  • the server can simply close the connection when done sending the program. in the client you detect that the connection is closed when recv() returns an empty string, you then know the program was received. unfortunately, if the server crashes before sending the whole program, you will execute an incomplete program on the client

there is countless other possibilities to correct this problem...

2. your receiving logic is wrong

now, have a closer look at the receiving logic (code edited to remove unrelevant lines):

1. a=client.recv(1024)
2. b=a
3. f=1 
4. while f:
5.     a = client.recv(1024)
6.     if a=="#p":
7.         f=0
8.         break
9.     b+=a

here, you first wait for some data (line 1). then you enter your while loop (line 4), and again wait for some data (line 5) !

recv() is a blocking call: this means that it will not return until there are some data to return or the connection is closed. if the server sends less than 1024 bytes including the terminator, you receive everything on line 1 (including the terminator), but still wait for more data on line 5...

note that there is still a bug in your test: if the separator is split between 2 recv() calls (that happens if the program is exactly 1023 bytes long), a.endswith('#p') will never evaluate to True.

so here is a correct receiving logic, which tests for the terminator as soon as some data is received:

a = client.recv(1024)    
b = a
f = 1 
while f:
    if b.endswith("#p"):
        f=0
        break
    a = client.recv(1024)
    b += a

note that this can be greatly simplified, by removing unnecessary variables:

b = client.recv(1024)
while not b.endswith("#p"):
    b += client.recv(1024)

3. properly release the resources used in your code

i did not stress this point at first, but you should always properly close your connection !

the python language is made so that the connection will be implicitly closed as soon as the variable holding it is not referenced anymore (in your case, when it goes out of scope). however, closing it explicitely will make clear at which point you expect the connection to close.

some implementation may decide to postpone the release of the resource to a later time (Jython users may have encountered this problem), leaving the connection open... this is no problem, but may results in strange behaviours later when your program grows to a more feature-complete product.

Upvotes: 4

Related Questions