网络编程问题

发布于 2024-10-11 16:49:12 字数 2538 浏览 0 评论 0原文

我写了一个客户端服务器程序,其中服务器向客户端发送程序,客户端执行接收到的程序。在本例中,它是 OpenGL 中的线条绘制程序。问题是,在运行服务器和客户端时,整个程序(即,将程序发送到服务器和客户端)有时会执行,但有时不会执行。客户端连接后就卡住了。该程序也不会停止。在我朋友的系统中同样有效,但在我的系统中它仅有时有效(大多数时候它不会)。可能是什么原因?我错过了什么吗?

我的服务器代码:

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()

我的客户端代码:

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()

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()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

抹茶夏天i‖ 2024-10-18 16:49:13

1. TCP 套接字是一个流 (数据包是实现细节)

网络堆栈经过优化以限制发送的数据包数量。因此,当您在套接字上多次调用 send() 时,网络堆栈可以自由地拆分(或不拆分)多个数据包之间的数据。

在运行于真实网络上的计算机上,这比环回连接慢得多,如果在调用 send() 时仍有数据包等待发送,则新数据为附加到等待数据包。在接收端,网络堆栈可以自由合并多个数据包(从套接字接收的数据被缓冲)以立即向您呈现数据。

因此,在服务器中,您可以编写:

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

最后一个数据包可能包含程序的结尾和终止符,它们连接在一起。
在客户端中:

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

终止符可能不在接收到的数据包的开头,也可能不是数据包中存在的唯一字符。如果是这种情况,那么您不会检测到终止符,不会退出循环,再次调用 recv() ,并在此停止,因为 recv() 是一个阻塞调用,服务器将不再发送任何数据。

因此,您必须选择另一种方式来了解服务器何时完成发送数据。有很多可能性:

  • 第一个明显的方法是在客户端,而不是 if a=="#p":,编写 if a.endswith("#p"):代码>.这可以纠正代码中存在的问题,
  • 您也可以选择先发送程序的长度,然后再发送。客户端读取长度,然后读取该数量的字符并停止
  • 服务器可以在发送程序完成后简单地关闭连接。在客户端中,当 recv() 返回空字符串时,您检测到连接已关闭,然后您就知道程序已收到。不幸的是,如果服务器在发送整个程序之前崩溃,您将在客户端上执行不完整的程序,

还有无数其他可能性来纠正此问题...

2。你的接收逻辑是错误的

现在,仔细看看接收逻辑(编辑代码以删除不相关的行)

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

在这里,你首先等待一些数据(第1行)。然后输入 while 循环(第 4 行),并再次等待一些数据(第 5 行)!

recv() 是一个阻塞调用:这意味着它不会返回,直到有一些数据要返回或连接关闭。如果服务器发送的字节数少于 1024 个字节(包括终止符),您将收到第 1 行的所有内容(包括终止符),但仍需等待第 5 行的更多数据...

请注意,您的测试中仍然存在一个错误:如果分隔符被分割为 2 个 recv() 调用(如果程序长度恰好为 1023 字节,则会发生这种情况),a.endswith('#p') 永远不会计算为 正确

所以这里是一个正确的接收逻辑,它在收到一些数据后立即测试终止符:

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

请注意,通过删除不必要的变量可以大大简化这一点:

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

3。正确释放代码中使用的资源

我一开始并没有强调这一点,但你应该始终正确关闭你的连接!

python 语言是这样设计的,一旦保存连接的变量不再被引用(在您的情况下,当它超出范围时),连接就会隐式关闭。但是,显式关闭它会明确您期望连接在哪个点关闭。

某些实现可能会决定推迟资源的释放(Jython 用户可能遇到过这个问题),使连接保持打开状态...这没问题,但是当您的程序增长到产品功能更加齐全。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文