Introduction to Socket communication
There are two main communication modes between Android and the server:
- Http communication
- The Socket communication
The biggest difference between the two is:
Http connections use the request-response mode, that is, the connection channel is established during the request. After the client sends a request to the server, the server can return data to the client.
Socket communication is after the two parties establish a connection, data can be directly transmitted, information can be actively pushed during the connection, without the need for the client to send a request to the server.
So, what is a socket?
Socket, also known as a socket, provides a port for the program to communicate with the outside world.
A socket connection provides a channel for data transmission between communication parties. The main features of socket are low data loss rate, easy to use and easy to transplant.
What is a Socket
Socket is an abstraction layer through which applications send and receive data. Using sockets, applications can be added to the network and communicate with other applications in the same network.
Simply put, Socket provides a port for the program to communicate with the outside world and provides a data transmission channel for the communication parties.
2. Socket classification
Socket implementation is diversified according to different underlying protocols. The main Socket types in the TCP/IP protocol cluster are StreamSocket and Datagramsocket.
Streaming sockets use TCP as their end-to-end protocol, providing a reliable byte stream service.
Datagram nesting uses the UDP protocol to provide data packaging and send data.
Second, Socket basic communication model
1. TCP communication model
2. UDP communication model
Three, the basic implementation principle of Socket
1. Tcp-based sockets
The server first declares a ServerSocket object and specifies a port number, and then calls the ServerSocket accept() method to accept data from the client.
The Accept () method is blocked when there is no data to accept. (Socket Socket = serversocket.accept())), once received, read the received data through inputStream.
The client creates a Socket object and executes the IP address and port number of the server (Socket Socket = new Socket(“172.168.10.108”, 8080);) OutputStream = socket.getOutputStream(); , and finally write the data to outputStream for TCP socket data transmission.
2. Data transmission based on UDP protocol
The server first creates a DatagramSocket object and specifies the listening port. Next, create an empty DatagramSocket object to receive data (Byte data[] = new byte[1024]; DatagramSocket packet = new DatagramSocket(data, data.length);) Receive (), similar to serverSocket accept(), is blocked when there is no data to accept.
The client also creates a DatagramSocket object and specifies the port to listen on. Next create an InetAddress object that is similar to the sending address of a network (InetAddress ServerAddress = inetAddress.getByName (“172.168.1.120”)). Define a string to send, create a DatagramPacket object, specify the address and port number to send the packet to the network, and finally send the data using the send() object of the DatagramSocket.
(String STR = “hello”; byte data[] = str.getByte(); DatagramPacket packet = new DatagramPacket(data, data.length, ServerAddress, 4567); Socket. The send (packet);)
4. Android implements simple socket communication
1. Use TCP to communicate
- Android terminal implementation:
protected void connectServerWithTCPSocket() { Socket socket; // Create a Socket object and specify the server IP address and port number. Socket = new Socket("192.168.1.32", 1989); // Create an InputStream user to read the file to be sent. InputStream inputStream = new FileInputStream("e://a.txt"); // Get the Socket's OutputStream object for sending data. OutputStream outputStream = socket.getOutputStream(); Byte buffer[] = new byte[4 * 1024]; byte buffer[] = new byte[4 * 1024]; int temp = 0; // loop through the filewhile((temp = inputStream.read(buffer)) ! Outputstream. write(buffer, 0, temp); outputStream.write(buffer, 0, temp); } // Send read data to server outputstream.flush (); /** or create a packet and write it using BufferedWriter, depending on your needs **/ / String socketData ="[2143213;21343fjks;213]";
// BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
// socket.getOutputStream()));
// writer.write(socketData.replace("\n"."") + "\n"); // writer.flush(); /************************************************/ } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }}Copy the code
- Server side simple implementation
public void ServerReceviedByTcp{// declare a ServerSocket object ServerSocket ServerSocket = null; ServerSocket = new ServerSocket(1989); // Create a ServerSocket object and let the Socket listen on port 1989. Socket Socket = serversocket.accept (); // Call the accept() method of the ServerSocket to accept the request sent by the client. InputStream InputStream = socket.getinputStream (); byte buffer[] = new byte[1024 * 4]; int temp = 0; // Read the data sent by the client from InputStreamwhile ((temp = inputStream.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, temp));
}
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Copy the code
2. Use UDP protocol for communication
- The client sends data:
protected void connectServerWithUDPSocket() { DatagramSocket socket; Try {// Create a DatagramSocket object and specify a port number. Note that if the client needs to receive data from the server, it also needs to use this port number to receive data. So remember socket = new DatagramSocket(1985); InetAddress serverAddress = inetaddress.getByName (InetAddress = inetaddress.getByName ("192.168.1.32");
//Inet4Address serverAddress = (Inet4Address) Inet4Address.getByName("192.168.1.32");
String str = "[2143213;21343fjks;213]"; Byte data[] = str.getbytes (); // Set the packet to be sent. // Create a DatagramPacket object, which is used to send data. // Parameter 1: data to be sent parameter 2: data length parameter 3: network address of the server Parameter 4: DatagramPacket packet = new DatagramPacket(data, data.length,serverAddress,10025); DatagramPacket = new DatagramPacket(data, data.length,serverAddress,10025); socket.send(packet); // Send the data to the server. } catch (SocketException e) { e.printStackTrace(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }}Copy the code
- The client receives the data returned by the server
public void ReceiveServerSocketData() { DatagramSocket socket; Data socket = new DatagramSocket(1985); DatagramSocket = new DatagramSocket(1985); byte data[] = new byte[4 * 1024]; DatagramPacket packet = new DatagramPacket(data, data.length); socket.receive(packet); String result = new String(packet.getData(), packet.getoffset (), packet.getLength()); String result = new String(packet.getData(), packet.getLength()); socket.close(); Println (system.out.println);"the number of reveived Socket is :" + flag
+ "udpData:"+ result); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }}Copy the code
- Server receiving client implementation:
public void ServerReceviedByUdp(){// Create a DatagramSocket object and specify a listening port. (UDP uses DatagramSocket.) DatagramSocket Socket; try { socket = new DatagramSocket(10025); Byte data[] = new byte[4*1024]; // Create an array of bytes to store the received data. // Create a DatagramPacket and specify the size of the DatagramPacket object DatagramPacket packet = new DatagramPacket(data,data.length); Socket. Receive (packet); // Convert the data sent by the client to a string. // Use the String method with three arguments. Parameter 1: packet parameter 2: start position Parameter 3: packet length String result = new String(packet.getData(), packet.getoffset (), packet.getLength()); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }}Copy the code
Five, the summary
In UDP mode, the Android terminal and the server terminal receive can be seen, in fact, the Android terminal and the server terminal send and receive are very different, as long as the port number is correct, there is no problem with mutual communication, TCP uses the flow mode to send, UDP is sent in the form of packets.
supplement
1, Serversocket.accept () method underlying source code
The main reason to look at this section of code is to see how the underlying accept() source code implements blocking wait.
public Socket accept() throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
if(! isBound()) throw new SocketException("Socket is not bound yet");
Socket s = new Socket((SocketImpl) null);
implAccept(s);
return s;
}
Copy the code
Call the implAccept() method in the Accept() method
protected final void implAccept(Socket s) throws IOException {
SocketImpl si = null;
try {
if (s.impl == null)
s.setImpl();
else{ s.impl.reset(); } si = s.impl; s.impl = null; si.address = new InetAddress(); si.fd = new FileDescriptor(); GetImpl ().accept(si); . } catch (IOException e) { ...... } s.impl = si; s.postAccept(); }Copy the code
The Accept () method in the PlainSocketImpl class is then called
protected synchronized void accept(SocketImpl s) throws IOException {
if (s instanceof PlainSocketImpl) {
// pass inthe real impl not the wrapper. SocketImpl delegate = ((PlainSocketImpl)s).impl; delegate.address = new InetAddress(); delegate.fd = new FileDescriptor(); // Impl. Accept (delegate); //set fd to delegate's fd to be compatible with older releases s.fd = delegate.fd; } else {// corresponding code imp.accept (s); }}Copy the code
Call the Accept () method in the abstracPlainSocketImpl class again
protected void accept(SocketImpl s) throws IOException { acquireFD(); try { socketAccept(s); } finally { releaseFD(); }}Copy the code
AcquireFD () ¶
/ * *"Acquires" and returns the FileDescriptor for this impl
*
* A corresponding releaseFD is required to "release"The * FileDescriptor. */ // "get" and return the FileDescriptor of this impl requires a corresponding releaseFD to "free" the FileDescriptor. FileDescriptoracquireFD() {
synchronized (fdLock) {
fdUseCount++;
returnfd; }}Copy the code
The socketAccept() method is then executed
void socketAccept(SocketImpl s) throws IOException {
int nativefd = checkAndReturnNativeFD();
if (s == null)
throw new NullPointerException("socket is null"); int newfd = -1; InetSocketAddress[] isaa = new InetSocketAddress[1]; // Wait for blocking codeif (timeout <= 0) {
newfd = accept0(nativefd, isaa);
} else {
configureBlocking(nativefd, false);
try {
waitForNewConnection(nativefd, timeout);
newfd = accept0(nativefd, isaa);
if(newfd ! = -1) { configureBlocking(newfd,true);
}
} finally {
configureBlocking(nativefd, true);
}
}
/* Update (SocketImpl)s' fd '*/
fdAccess.set(s.fd, newfd);
/* Update socketImpls remote port, address and localport */
InetSocketAddress isa = isaa[0];
s.port = isa.getPort();
s.address = isa.getAddress();
s.localport = localport;
}
Copy the code
This part is the blocking code block when there is no request. After checking accetP0 () method and configureBlocking() method one by one, it is found that these codes are implemented using native code to improve efficiency.
Therefore, the blocking mechanism of accept() method is not found specifically
Personally, the configureBlocking() method waits for a blocking call, while the accept0() method responds to the request.
(This part is purely personal speculation, may be wrong, so I hope you can comment)
Outputstream.flush (
The flush() method is simple enough to flush the output stream and force out any buffered output bytes
/**
* Flushes this output stream and forces any buffered output bytes
* to be written out. The general contract of <code>flush</code> is
* that calling it is an indication that, if any bytes previously
* written have been buffered by the implementation of the output
* stream, such bytes should immediately be written to their
* intended destination.
* <p>
* If the intended destination of this stream is an abstraction provided by
* the underlying operating system, for example a file, then flushing the
* stream guarantees only that bytes previously written to the stream are
* passed to the operating system for writing; it does not guarantee that
* they are actually written to a physical device such as a disk drive.
* <p>
* The <code>flush</code> method of <code>OutputStream</code> does nothing.
*
* @exception IOException if an I/O error occurs.
*/
public void flush() throws IOException {
}
Copy the code
Finally, attach the Java test socket to check its underlying source code implementation mechanism. Because the network request has been unsuccessful, only DEBUG can check the underlying implementation mechanism step by step.
You can try it yourself and see how it works.
import java.io.*;
import java.net.*;
public class socketTest {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = setSocket();
if(socket == null) System.out.println("socket is null");
InputStream inputStream = new FileInputStream("d://haha.txt");
OutputStream outputStream = socket.getOutputStream();
byte buffer[] = new byte[4 * 1024];
int temp = 0;
while((temp = inputStream.read(buffer)) ! = -1) { outputStream.write(buffer, 0, temp); } outputStream.flush(); listener(); } public static SocketsetSocket() throws IOException{
String ip = "127.0.0.5"; int port = 8080; Socket socket = new Socket(); // Set the maximum waiting time socket.setsotimeout (8000); Socket.connect (new InetSocketAddress(IP, port));returnsocket; } // Server listening method public static void Listener () throws IOException{// Normally, this parameter is set here"9999"The port number in the client is the same as the port number in the client to complete the network request, // because the test failed, but want to see the implementation principle, so I do this!! ServerSocket server = new ServerSocket(9999); Socket socket = null; int i = 0;while(true){ i++; socket = server.accept(); // This is also a blocking method to system.out.println ("有" + i + "One user connected to the server.");
new Thread(new socketTest().new ServerDoThread(socket)).start();
}
}
class ServerDoThread implements Runnable {
Socket socket;
InputStream inputStream;
public ServerDoThread(Socket socket) {
this.socket = socket;
try {
this.inputStream = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
byte buffer[] = new byte[1024 * 4];
int temp = 0;
try {
while((temp = inputStream.read(buffer)) ! = -1) { System.out.println(new String(buffer, 0, temp)); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }}}}Copy the code
Source: Socket communication principle (The Android client communicates with the server through TCP&&UDP)