UDP Android client and Rails server
Khi xây dựng các ứng dụng chắc hẳn các bạn gặp rất nhiều trường hợp liên quan đến GPS. Các ứng dụng tương tác với người dung thông qua vị trí và phải cập nhật vị trí của mình liên tục lên server để quản lý. Việc này khá là tốn tài nguyên của server và lượng dữ liệu mà mobile phải sử dụng khi chúng ...
Khi xây dựng các ứng dụng chắc hẳn các bạn gặp rất nhiều trường hợp liên quan đến GPS. Các ứng dụng tương tác với người dung thông qua vị trí và phải cập nhật vị trí của mình liên tục lên server để quản lý. Việc này khá là tốn tài nguyên của server và lượng dữ liệu mà mobile phải sử dụng khi chúng ta cập nhậ no. Hơn nữa, việc cập nhật địa điểm này lại không đòi hỏi việc nhận response trả về mà chỉ đơn giản là gửi dữ liệu lên server. Tính logic ở đây không thực sự quan trọng. Chính vì thế mà chúng ta nên sử dụng giao thức UDP để thay thế TCP thông thường. Bởi vì khi gửi qua UDP dung lượng mà client sử dụng là ít hơn và nhanh hơn sử dụng UDP khá nhiều.
Bài viết này mình sẽ giới thiệu các kết nối giữa client (Android) với server (Ruby on Rails) qua việc sử dụng UDP.
I) Xây dựng server rails
Việc này khá là đơn giản. Chúng ta chỉ cần có một server luôn lắng nghe qua UDP lấy dữ liệu và cập nhật vào trong database. Chính vì thế mà ta không cần phải xây dựng một web có giao diện mà chỉ cần chạy ngầm.
Khởi tạo server
rails new udp_server
Tạo model Location chứa thông tin về location của client gửi lên.
rails g model Location latitude:float longitude:float
Tạo một UDPSocket lắng nghe client. config/initializers/udp_socket.rb
require 'socket' Thread.new do socket = UDPSocket.new socket.bind('0.0.0.0', 1991) while true data = socket.recvfrom(255).first latitude, longitude = data.split ',' Location.create(latitude: latitude, longitude: longitude) end end
File udp_socker.rb này được đặt trong config để khi khởi động server thì nó sẽ được tự động load và mở cổng 1991 để lắng nghe dữ liệu từ client. Khi server bị tắt thì cổng UDP này cũng sẽ tự động bị tắt theo.
Mọi việc đã xong. Giờ chỉ cần khởi dộng server lên là xong.
rails s -b 0.0.0.0
Sử dụng lệnh trên để ta có thể truy cập thông qua ip local .
itachi@itachi:~/git/udp_server$ netstat -tulnp | grep 'udp' (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) udp 0 0 0.0.0.0:52006 0.0.0.0:* - udp 0 0 0.0.0.0:57083 0.0.0.0:* - udp 0 0 127.0.1.1:53 0.0.0.0:* - udp 0 0 127.0.0.1:53 0.0.0.0:* - udp 0 0 0.0.0.0:68 0.0.0.0:* - udp 0 0 0.0.0.0:631 0.0.0.0:* - udp 0 0 0.0.0.0:5353 0.0.0.0:* 2936/chrome udp 0 0 0.0.0.0:5353 0.0.0.0:* - udp 0 0 0.0.0.0:1991 0.0.0.0:* 6638/ruby udp6 0 0 ::1:53 :::* - udp6 0 0 :::57451 :::* - udp6 0 0 :::5353 :::* - udp6 0 0 :::10284 :::* -
Các bạn có thể tìm thấy được cổng 1991 đã được lắng nghe qua udp khi khởi động server trong câu lệnh trên udp 0 0 0.0.0.0:1991 0.0.0.0:* 6638/ruby
II) Tạo ứng dụng Android
Ở bài viết này, mình sẽ giới thiệu cách làm một ứng dụng đơn giản, chỉ có chức năng gửi dữ liệu lên server rails bằng giao thức UDP để cập nhật dữ liệu location hiện tại của mình.
Như nhiều app khác, trước tiên bạn phải cấp quyền cho các ứng dụng hay các API mà mình định sử dụng. Ở đây, chương trình cần phải mở được UDP socket, truy cập internet và dữ liệu location. Chính vì vậy các bạn cần phải thêm các quyền sau vào file AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.INTERNET"/>
Chúng ta lần lượt làm theo các bước như trên mình đã nói:
- Mở ra một UDP Socket để gửi dữ liệu cho server
try { DatagramSocket datagramSocket = new DatagramSocket(1991); InetAddress inetAddress = InetAddress.getByName("192.168.1.40"); } catch (Exception e) { e.printStackTrace(); }
các dòng lệnh trên có nghĩa là bạn đã mở một udp socket ở cổng 1991 (đây là cổng UDP mà mình đã mở ở server rails trên) với server host là 192.168.1.40 đây là ip máy của mình. Khi các bạn chạy thì hãy thay bằng ip local máy của các bạn, hoặc là host thật sự nào đó.
Bạn chỉ cần mở cổng này một lần và sau đó chỉ việc đẩy dữ liệu vào đó chứ ko cần thiết phải mở nhiều lần.
- Cần truy cập vào dữ liệu location để lấy thông tin địa điểm gửi lên server. Ở đây mình sử dụng LocationManager.
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return; } locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 0, this);
Với hàm requestLocationUpdates bạn có thể truyền tham số vào để cài đặt được thời gian, khoảng cách để chạy sự kiện onLocationChanged của chính LocationListener này. Như trên mình đã cài đặt thời gian chạy sự kiện onLocationChanged là mỗi 5s một lần.
Việc còn lại là gửi dữ liệu location (longitude, latitude) lên server là xong.
@Override public void onLocationChanged(Location location) { txtLat = (TextView) findViewById(R.id.textView2); txtLat.setText(String.format("Latitude:%s, Longitude:%s", location.getLatitude(), location.getLongitude())); if (isSendLocation) { UdpClientTask udpClientTask = new UdpClientTask(); udpClientTask.execute(location); } } class UdpClientTask extends AsyncTask<Location, Void, Void> { @Override protected Void doInBackground(Location... params) { try { String s = String.format("%f,%f", params[0].getLatitude(), params[0].getLongitude()); byte[] sendData = s.getBytes(); DatagramPacket p = new DatagramPacket(sendData, s.length(), inetAddress, 1991); datagramSocket.send(p); } catch (Exception e) { e.printStackTrace(); } return null; } }
Ở đây ta phải sử dụng AsyncTask để gửi dữ liệu, do Android không cho phép, mà chúng ta cũng không nên để việc này chạy ở background chính mà ta nên tạo một AsyncTask khác để chạy ngầm, sao cho luồng hoạt động chính của ta không bị gián doạn do đường truyền mạng ...v.v
Dưới đây là video demo quá trình ứng dụng android gửi dữ liệu lên server để lưu lại qua udp socket.
Toàn bộ code được mình public trên github:
-
Rails server: udp_server
-
Android: UdpAndLocation