在一个宠物项目上工作,从一个Android设备为我的电脑创建一个可用的鼠标,我在PC上运行一个python服务器,Android应用程序向它发送所需的数据。
我创建此鼠标的想法是使用Motion_Event UP和MOVE在设备屏幕上记录您的手指移动,计算您的最后和新的x,y坐标之间的差异,并将差异发送到PC,以便它移动鼠标采用相同的模式
现在我从发送到计算中遇到了一些问题,我真的希望你们的观点是我应该如何更好地完成这个算法,或者只是对我的代码执行不好的实现。
爪哇:
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN ){
oldX = (int) event.getX();
oldY = (int) event.getY();
}
else if(event.getAction() == MotionEvent.ACTION_MOVE ){
newX = (int) event.getX();
newY = (int) event.getY();
xDiff = newX - oldX;
yDiff = newY - oldY;
Log.v("xdiff", ""+xDiff);
Log.v("ydiff", ""+yDiff);
oldX = newX;
oldY = newY;
a.mouse(xDiff, yDiff);
}
else if(event.getAction() == MotionEvent.ACTION_UP){
}
return true;
}
插口:
@Override
protected Void doInBackground(Void... params) {
try {
this.sock = new Socket("10.0.0.2", 9871);
outToServer = new DataOutputStream(this.sock.getOutputStream());
}
catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public void mouse(int x, int y) {
try {
this.outToServer.writeBytes(""+x+","+y);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
python套接字:
def __init__(self, IP, port):
try:
self.acceptor = socket.socket()
self.acceptor.bind((IP, port))
self.acceptor.listen(1)
except socket.error, e:
print e
def start_server(self):
try:
print "serv up"
self.sock, self.connecting_ip = self.acceptor.accept()
print self.connecting_ip[0] + " connected!"
except socket.error, e:
print e
数据处理本身:
def mouse(self):
try:
while True:
data = self.sock.recv(1024)
if data:
coords = data.split(",")
x = int(coords[0])
y = int(coords[1])
current_x = win32api.GetCursorPos()[0]
current_y = win32apo.GetCursorPos()[1]
win32api.SetCursorPos((current_x+x,current_y+y))
else:
self.sock.close()
except socket.error, e:
print e
我得到的问题是:
1)不是像我发送的那样获取数据(“+”+ x +“,”+ y),而是将更多的数据加载到缓冲区中,然后输出为x,y,x,y,x,y, X,Y,X,Y,X,Y ... 我试过搞乱缓冲区大小,但没有做太多 (请注意,关于这一点的奇怪之处在于,当我在我的模拟器上运行应用程序时,我没有得到这个错误,我得到了我想要的缓冲区,x,y,它们彼此分开但是当我运行时它在我的Android设备上(SGS2,如果它在所有问题上)数据进入掩体并一次性发送所有内容)
2)我得到的另一件事是数据被“损坏”或丢失,如发送''而不是数字或4和-5发送为4-5而不是4,-5
总而言之,我很确定我在这里主要搞砸了,我希望有人能说清楚我应该怎么做。
UDP实施:
@Override
protected Void doInBackground(Void... params) {
try {
this.clientSocket = new DatagramSocket();
this.IPAddress = InetAddress.getByName("10.0.0.2");
this.sendData = new byte[1024];
}
catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public void mouse(int x, int y) {
try {
String sentence = "{" + x + "," + y + "}";
this.sendData = sentence.getBytes();
DatagramPacket sendPacket = new DatagramPacket(this.sendData, this.sendData.length, this.IPAddress, 9871);
this.clientSocket.send(sendPacket);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
python udp serv:
import socket
PORT = 9871
IP = socket.gethostbyname(socket.gethostname())
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind((IP, PORT))
print "Server %s" % IP
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
if data:
print "received message:", data
else:
print "dead"
答案 0 :(得分:1)
TCP是一种流协议。这意味着,当您send()
(或在您的情况下为.writeBytes()
)多次时,不会保留不同调用的参数之间的边界。
,例如,当你发送:
send("1,2");
send("-3,4");
send("5,6");
send("-7,8");
然后这可以在另一端到达:
"1,2-3,45,6-7,8"
如果要使用TCP并保留消息之间的边界,则需要在协议中添加一个层来提供此功能。有两种传统方法可以实现这一目标:
在您的情况下,通过简单地添加换行来获取第一个选项可能会起到作用:
send("1,2\n");
send("-3,4\n");
send("5,6\n");
send("-7,8\n");
这将在另一端到达:
"1,2\n-3,4\n5,6\n-7,8"
阅读时,您可以使用换行符重建原始邮件。
警惕不完整的线条。当我们说数据必然以"1,2\n-3,4\n5,6\n-7,8"
到达时,我们正在简化一点。它也可以一次到达:
"1,2\n-3," // from the first read call
"4\n5,6\n-7,8" // from the second read call
当您从TCP流中读取消息时,您需要将读取的内容放入缓冲区。在处理完整条消息之前(或直到某些事情导致您想要完全放弃连接),您才能丢弃数据。例如,如果缓冲区以不完整的消息"-3,"
结束,那么您需要阅读,直到您收到消息的其余部分:"4\n"
。
另一种可能合适的解决方案是使用UDP等数据报协议,其中发送方的消息确实对应于接收方的消息。 (请注意,TCP和UDP之间还存在其他非常显着的差异!请勿基于此单独切换。)
答案 1 :(得分:0)
我注意到的第一件事是,在你打电话
后你没有冲洗流this.outToServer.writeBytes(""+x+","+y);
如果你打电话
out.flush();
之后,数据将立即发送(描述符将被刷新)。
我认为第二号问题应该是第一号问题的结果。 除此之外,您应该考虑使用适当的协议。
答案 2 :(得分:0)
您的实施中存在两个主要问题。
首先,数据在没有缓冲的情况下在流上发送,这意味着当发送坐标开始或结束时,接收端无法进行。
在包完成后,您应该使用刷新向输出流添加缓冲。这将在接收方提供最佳行为。发送文本时,还可以在将文本添加到流时使用Writer。但最重要的是,您需要一种方法来为一次鼠标更新包装所有传出信息(例如添加前缀和后缀字符,如{x,y})。
您的第二个问题是您不使用onInterceptTouchEvent(在移动时返回true)以在开始监视onTouchData()之前获得移动数据的独占权限。我建议检查至少有一个移动
ViewConfiguration.get(getContext()).getScaledTouchSlop
在拦截移动之前。这样可以避免在鼠标光标上轻微抖动/搅拌。并且您可以避免其他应用程序干扰警示检测等。当您收到关闭消息时,您可以暂时保存在截取delta计算后使用的起始值。