preface
Purpose of this Code: as a door lock, raspberry pie has an id that uniquely identifies the door lock. In order to enable raspberry pie and app to communicate effectively with each other, tcp sockets are used. In order to easily identify the content of communication, the codes of the three are utf-8
java Server
theory
Write the server code in eclipse, package it into jar s, transfer it to Alibaba cloud server, and run it on the server.
A server first creates a main socket to accept and respond to client connection requests. After each client requests a connection, the server will generate a corresponding socket and create a thread to serve the client separately and send it to the corresponding client through the corresponding socket.
How to determine which socket corresponds to which client is the problem I encounter
The actual operation is: create a list to store a user class set by yourself. The class includes socket, socket input stream, socket output stream, and the id and type of lock required in this experiment. The id of the door lock user is the id of the door lock, the type is door, the id of the mobile phone user is the id of the login at that time (that is, the corresponding door lock id), and the type is man. This depends on the needs of your own application
Each time a client is added, a user is created and added to the list. The formal parameters of the thread are the user of the client and the list created earlier. When sending a message to a specified user, you can find it through the get function of the user class
Practical operation
user class:
class User { private String id; private String type; private Socket socket; private BufferedReader br; private PrintWriter pw; public User(String id, final Socket socket, String type) throws IOException { this.id = id; this.socket = socket; this.type = type; this.br = new BufferedReader(new InputStreamReader(socket.getInputStream())); this.pw = new PrintWriter(socket.getOutputStream()); } @Override public String toString() { return "User [id=" + id + ", type=" + type + ", socket=" + socket + ", br=" + br + ", pw=" + pw + "]"; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getType() { return type; } public void setType(String type) { this.type = type; } public Socket getSocket() { return socket; } public void setSocket(Socket socket) { this.socket = socket; } public BufferedReader getBr() { return br; } public void setBr(BufferedReader br) { this.br = br; } public PrintWriter getPw() { return pw; } public void setPw(PrintWriter pw) { this.pw = pw; } }
Class of the server. The server uses the main function of serr as the entry of the program:
public class serr { public static void main(String[] args) throws Exception { // Instantiate a list to save all users List<User> list = new ArrayList<User>(); list.clear(); ServerSocket serverSocket = new ServerSocket(10001); while (true) { Socket socket = serverSocket.accept(); //First create a user, id and type as the default values. After connecting, the client must log in and send its own id and type, otherwise it will be regarded as illegal and the user will disconnect User user = new User("id", socket, "type"); list.add(user); System.out.println(socket); // Create a new thread, receive information and forward it. The thread needs user (can know the corresponding socket) and list (can know all sockets) as parameters ServerThread thread = new ServerThread(user, list); thread.start(); } } }
Thread:
class ServerThread extends Thread { private User user; private List<User> list; private boolean fristtime = true; //Used to determine whether to send a message for the first time private boolean findother = true; //Used to output how many sockets forwarded messages //constructor public ServerThread(User user, List<User> list) { this.user = user; this.list = list; } public void run() { try { // Gets the printwriter of the client socket, which is used to send a successful connection message to the client PrintWriter pw = user.getPw(); pw.write("hello" + "\n"); pw.flush(); InputStream in = user.getSocket().getInputStream(); // Get the message from the client // Read by byte because there are not only java clients, but also python clients. The clients do not send the operation of line conversion termination, so they cannot read a line with br readline int len = 0; byte[] buf = new byte[1024]; while ((len = in.read(buf)) != -1) { // Format of information: login,id,type(door/man) // exit // say,totype(man,door),thing(open,add,abnormal) String msg = new String(buf, 0, len); // Convert to string String[] str = msg.split(",");//Separated by commas if (fristtime) { //Connect to the server for the first time if (str[0].equals("login")) { //Modify the id and type of the current user user.setId(str[1]); user.setType(str[2]); System.out.println("login seuccess"); System.out.println("login" + user); fristtime = false; } else { //The first time you connect to the server, you don't report your id and type System.out.println("Illegal user!!"); user.getBr().close(); user.getSocket().close(); break; } } System.out.println("client:" + user.getId() + "--" + user.getType() + ": " + msg); switch (str[0]) { case "login": System.out.println("ok,logined"); break; case "exit": remove(user);// Remove User break; case "say": sendToClient(user.getId(), str[1], str[2]); // Forwarding information to specific users can only be forwarded to users with the same id break; default: System.out.println("Illegal instruction:" + msg.toString()); break; } } } catch (Exception e) { System.out.println("Accept instruction exception"); } finally { try { user.getBr().close(); user.getSocket().close(); } catch (IOException e) { e.printStackTrace(); } } } private void sendToClient(String userid, String type, String instruction) { // Look it up from the beginning int number=0; for (User user : list) { if (user.getId().equals(userid) && user.getType().equals(type)) { findother=false; try { System.out.println("send to user" + user.getId() + "--" + user.getType() + ":" + instruction+". "); PrintWriter pw = user.getPw(); //There are line transitions, so the client should use br readline to read by line pw.write(instruction + "\n"); pw.flush(); number++; } catch (Exception e) { e.printStackTrace(); } } } if(findother) { System.out.println("no con can send"); } else { System.out.println("send tatal "+number+"clients!"); } } private void remove(User user2) { list.remove(user2); } }
Put the above three classes in a package and import the corresponding package
Finally, it is packaged into jar
Right click the project - Export - java - Runnable JAR file - next - select the location of the Export and the class where the program starts - finish
client
Android app
Grant application permission
It is very important to give the app networking permission before socket communication can be carried out. In androidmainfest Add in XML
<uses-permission android:name="android.permission.INTERNET"/>
Interface layout
You are free to copy this. There is no need to copy it. Here I randomly set two buttons to send messages and exit.
Remember to set the id for each button and other controls
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="send" tools:layout_editor_absoluteX="87dp" tools:layout_editor_absoluteY="122dp" tools:ignore="MissingConstraints" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="exit" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" tools:ignore="MissingConstraints" /> <Button android:id="@+id/go" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="52dp" android:layout_marginLeft="52dp" android:layout_marginBottom="156dp" android:text="go" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
activity
Main
Declare required properties
private String ip; private Socket socket; public static OutputStream os; public static BufferedReader br; Button one; Button exit; Button go; TextView tv; final int CONNECTING=1; final int CONNECT=2; int i=1;
In the current version, socket creation and connection need to be performed in asynchronous or Thread, not in the main Thread. Therefore, create a Thread class named TCPClient, which is a subclass of Thread.
If you want to update the ui of the interface, you need to modify it with handler
And start the service in the thread to receive information at any time
Thread establishing socket
class tcpClient extends Thread { public void run() { Message message=handler.obtainMessage(); message.what = CONNECTING; handler.sendMessage(message); Log.d("TAG", "handleMessage: try to connect"); ip ="59.110.216.125" ; try { socket = new Socket(InetAddress.getByName(ip),10001); } catch (IOException e) { e.printStackTrace(); } if (socket.isConnected()) { Message message1=handler.obtainMessage(); message1.what = CONNECT; handler.sendMessage(message1); Log.d("TAG", "handleMessage:connect"); try { os = socket.getOutputStream(); br=new BufferedReader(new InputStreamReader(socket.getInputStream())); } catch (IOException e) { e.printStackTrace(); } try { sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } try { String mine="login,lie1,man"; os.write(mine.getBytes()); os.flush(); System.out.println("send"+mine); Intent startser=new Intent(MainActivity.this,recvservice.class); startService(startser); } catch (IOException e) { e.printStackTrace(); } } } }
handler
final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == CONNECTING) { tv.setText("conning"); } if (msg.what == CONNECT) { tv.setText("connect!"); } } };
onCreate
In the onCreate function, you need to create and run a TCPClient thread and listen to the button click event:
It's better to put the content of the button click event outside the onCreate function, so that the code is more readable. I'm just testing here
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tcpClient tcp = new tcpClient(); tcp.start(); one=findViewById(R.id.button); exit=findViewById(R.id.button2); tv=findViewById(R.id.text); go=findViewById(R.id.go); go.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent=new Intent(); intent.setClass(MainActivity.this,testintentusesocket.class); startActivity(intent); } }); exit.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (socket.isConnected()) { Thread sendthread=new sThread("exit"); sendthread.start(); finish(); } } }); one.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (socket.isConnected()) { tv.setText("SEND OPEN"+i); i++; Thread sendthread=new sThread("say,door,open"); sendthread.start(); } else{ tv.setText("NO CONNECT SOCKET"); Toast.makeText(MainActivity.this,"socket duankai",Toast.LENGTH_LONG); } } }); } @Override protected void onDestroy() { super.onDestroy(); try { os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } }
sThread
Among them, once the send or exit button is pressed, a thread is created to send data. It is easy to know that the thread sThread is used to send data. This thread is placed outside the MainActivity, so that other activities can also create sThread to send data
class sThread extends Thread { String msg; public sThread(String msg1) { msg=msg1; } public void run() { try { MainActivity.os.write(msg.getBytes()); MainActivity.os.flush(); } catch (IOException e) { e.printStackTrace(); } }}
In my test, I found that the button listening event can not directly send information using the socket. A thread must be created to let the thread send information
Sum of main activities:
package com.example.servertest; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; class sThread extends Thread { String msg; public sThread(String msg1) { msg=msg1; } public void run() { try { MainActivity.os.write(msg.getBytes()); MainActivity.os.flush(); System.out.println("send open "); } catch (IOException e) { e.printStackTrace(); } }} public class MainActivity extends AppCompatActivity { private String ip; private Socket socket; public static OutputStream os; public static BufferedReader br; Button one; Button exit; Button go; TextView tv; final int CONNECTING=1; final int CONNECT=2; int i=1; final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == CONNECTING) { tv.setText("conning"); } if (msg.what == CONNECT) { tv.setText("connect!"); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tcpClient tcp = new tcpClient(); tcp.start(); one=findViewById(R.id.button); exit=findViewById(R.id.button2); tv=findViewById(R.id.text); go=findViewById(R.id.go); go.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent=new Intent(); intent.setClass(MainActivity.this,testintentusesocket.class); startActivity(intent); } }); exit.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (socket.isConnected()) { Thread sendthread=new sThread("exit"); sendthread.start(); finish(); } } }); one.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (socket.isConnected()) { tv.setText("SEND OPEN"+i); i++; Thread sendthread=new sThread("say,door,open"); sendthread.start(); } else{ tv.setText("NO CONNECT SOCKET"); Toast.makeText(MainActivity.this,"socket duankai",Toast.LENGTH_LONG); } } }); } @Override protected void onDestroy() { super.onDestroy(); try { os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } class tcpClient extends Thread { public void run() { Message message=handler.obtainMessage(); message.what = CONNECTING; handler.sendMessage(message); Log.d("TAG", "handleMessage: try to connect"); ip ="59.110.216.125" ; try { socket = new Socket(InetAddress.getByName(ip),10001); } catch (IOException e) { e.printStackTrace(); } if (socket.isConnected()) { Message message1=handler.obtainMessage(); message1.what = CONNECT; handler.sendMessage(message1); Log.d("TAG", "handleMessage:connect"); try { os = socket.getOutputStream(); br=new BufferedReader(new InputStreamReader(socket.getInputStream())); } catch (IOException e) { e.printStackTrace(); } try { sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } try { String mine="login,lie1,man"; os.write(mine.getBytes()); os.flush(); System.out.println("send"+mine); Intent startser=new Intent(MainActivity.this,recvservice.class); startService(startser); } catch (IOException e) { e.printStackTrace(); } } } } }
testintentusesocket
Used to test whether the jump intent can communicate through the socket
Interface layout
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/buttont" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="send" tools:layout_editor_absoluteX="87dp" tools:layout_editor_absoluteY="122dp" tools:ignore="MissingConstraints" /> </androidx.constraintlayout.widget.ConstraintLayout>
Activity code
package com.example.servertest; import android.os.Bundle; import android.view.View; import android.widget.Button; import androidx.appcompat.app.AppCompatActivity; public class testintentusesocket extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.te); Button bt=findViewById(R.id.buttont); bt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Thread sendthread=new sThread("open"); sendthread.start(); finish(); } }); } }
Configure activities
In Android mainfest Add in XML
<activity android:name=".testintentusesocket"></activity>
service
Create a thread in oncreate to listen for messages at any time
package com.example.servertest; import android.app.Service; import android.content.Intent; import android.os.IBinder; public class recvservice extends Service { @Override public IBinder onBind(Intent intent){ return null; } @Override public void onCreate(){ super.onCreate(); new Thread((new Runnable() { @Override public void run() { try { System.out.println("lisen......"); while(true){ String read=MainActivity.br.readLine(); System.out.println(read); if(read.equals("abnormal")){ System.out.println("recive abnormal! "); } } }catch (Exception e){ System.out.println("read error!"); } } })).start(); } @Override public int onStartCommand(Intent intent,int flags,int startId){ return super.onStartCommand(intent,flags,startId); } @Override public void onDestroy(){ super.onDestroy(); System.out.println("oncreate service"); } }
In mainfest The service is declared in XML
<service android:name=".recvservice"/>
Raspberry pie
Create a thread to respond to messages in real time, do your own things in the main thread and send messages
import socket import time import threading #tcp connection before running the program s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host = "59.110.216.125" # Public IP address of alicloud server port = 10001 # The port opened in the ECS console security group is the same as the server s.connect((host, port)) # Create connection showconn=True #The door lock sends a message (abnormal) to the server def send(msg): print(s) s.send(msg.encode("utf-8")) #The door lock is ready to accept the message: open the door def recvthing(): while showconn: data = s.recv(20) //Due to the server forwarding, there is a transfer data = data.decode("utf-8").strip('\n') if data=="hello": print("connect!") if data=="open": print("to opendoordef()") if data=="add": print("to addfgdef()") print(data) s.close() if __name__ == '__main__': #Create a thread and let the thread stand by to receive information t = threading.Thread(target=recvthing) t.start() #Send the id of the door lock to the server first, and let the server store the door lock id, socketnumber and type=2 as the door lock in the database msg = 'login,lie1,door' send(msg) while True: sendthing = input() send(sendthing)