Android 面试Socket与WebSocket
Android 面试Socket与WebSocket
文章目录
- Android 面试Socket与WebSocket
- Socket
- 什么是Socket
- Socket通讯的过程
- TCP协议Socket代码示例
- UDP协议Socket代码示例
- Socket实现Android跨进程通信
- WebSocket
- 什么是websocket
Socket
什么是Socket
- 网络上的两个程序通过以一个双向通讯的链接来实现数据交换,这是一个双端链路,可以实现android的**跨进程通信,**Socket通常用来实现客户端和服务端的连接,一个Socket由一个IP地址和一个端口号唯一确定。
- 支持TCP/UDP协议栈,是长链接,一旦建立起连接将不会主动断掉,除非网络挂掉
- Socket是一种编程接口,不是协议**,Socket是应用层与TCP/IP协议族通信的中间软件抽象层,**它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
Socket通讯的过程
- 基于TCP:服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。
- 基于UDP:UDP 协议是用户数据报协议的简称,也用于网络数据的传输。虽然 UDP 协议是一种不太可靠的协议,但有时在需要较快地接收数据并且可以忍受较小错误的情况下,UDP 就会表现出更大的优势。我客户端只需要发送,服务端能不能接收的到我不管
TCP协议Socket代码示例
1·服务端:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
//TCP协议Socket使用BIO进行通行:服务端
public class BIOServer {
// 在main线程中执行下面这些代码
public static void main(String[] args) {
//1单线程服务
ServerSocket server = null;
Socket socket = null;
InputStream in = null;
OutputStream out = null;
try {
server = new ServerSocket(8000);
System.out.println("服务端启动成功,监听端口为8000,等待客户端连接...");
while (true){
socket = server.accept(); //等待客户端连接
System.out.println("客户连接成功,客户信息为:" + socket.getRemoteSocketAddress());
in = socket.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
//读取客户端的数据
while ((len = in.read(buffer)) > 0) {
System.out.println(new String(buffer, 0, len));
}
//向客户端写数据
out = socket.getOutputStream();
out.write("hello!".getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2·客户端
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
//TCP协议Socket:客户端
public class Client01 {
public static void main(String[] args) throws IOException {
//创建套接字对象socket并封装ip与port
Socket socket = new Socket("127.0.0.1", 8000);
//根据创建的socket对象获得一个输出流
OutputStream outputStream = socket.getOutputStream();
//控制台输入以IO的形式发送到服务器
System.out.println("TCP连接成功 \n请输入:");
while(true){
byte[] car = new Scanner(System.in).nextLine().getBytes();
outputStream.write(car);
System.out.println("TCP协议的Socket发送成功");
//刷新缓冲区
outputStream.flush();
}
}
}
先运行服务端,在运行客户端。测试结果发送成功:
UDP协议Socket代码示例
1·服务器
//UDP协议Socket:服务端
public class Server1 {
public static void main(String[] args) {
try {
//DatagramSocket代表声明一个UDP协议的Socket
DatagramSocket socket = new DatagramSocket(8888);
//byte数组用于数据存储。
byte[] car = new byte[1024];
//DatagramPacket 类用来表示数据报包DatagramPacket
DatagramPacket packet = new DatagramPacket(car, car.length);
// //创建DatagramPacket的receive()方法来进行数据的接收,等待接收一个socket请求后才执行后续操作;
System.out.println("等待UDP协议传输数据");
socket.receive(packet);
//packet.getLength返回将要发送或者接收的数据的长度。
int length = packet.getLength();
System.out.println("啥东西来了:" + new String(car, 0, length));
socket.close();
System.out.println("UDP协议Socket接受成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2·客户端
//UDP协议Socket:客户端
public class Client1 {
public static void main(String[] args) {
try {
//DatagramSocket代表声明一个UDP协议的Socket
DatagramSocket socket = new DatagramSocket(2468);
//字符串存储人Byte数组
byte[] car = "UDP协议的Socket请求,有可能失败哟".getBytes();
//InetSocketAddress类主要作用是封装端口
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8888);
//DatagramPacket 类用来表示数据报包DatagramPacket
DatagramPacket packet = new DatagramPacket(car, car.length, address);
//send() 方法发送数据包。
socket.send(packet);
System.out.println("UDP协议的Socket发送成功");
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Socket实现Android跨进程通信
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.socketdemo">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SocketDemo"
tools:targetApi="31">
<activity
android:name=".TCPClientActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".TCPServerService"
android:process=":remote" />
</application>
</manifest>
package com.example.socketdemo;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.Context;
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
public class MyUtils {
public static String getProcessName(Context cxt, int pid) {
ActivityManager am = (ActivityManager) cxt
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return null;
}
for (RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName;
}
}
return null;
}
public static void close(Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void executeInThread(Runnable runnable) {
new Thread(runnable).start();
}
}
package com.example.socketdemo;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.sql.Date;
import java.text.SimpleDateFormat;
public class TCPClientActivity extends Activity implements OnClickListener {
private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
private static final int MESSAGE_SOCKET_CONNECTED = 2;
private Button mSendButton;
private TextView mMessageTextView;
private EditText mMessageEditText;
private PrintWriter mPrintWriter;
private Socket mClientSocket;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_RECEIVE_NEW_MSG: {
mMessageTextView.setText(mMessageTextView.getText()
+ (String) msg.obj);
break;
}
case MESSAGE_SOCKET_CONNECTED: {
mSendButton.setEnabled(true);
break;
}
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tcpclient);
mMessageTextView = (TextView) findViewById(R.id.msg_container);
mSendButton = (Button) findViewById(R.id.send);
mSendButton.setOnClickListener(this);
mMessageEditText = (EditText) findViewById(R.id.msg);
Intent service = new Intent(this, TCPServerService.class);
startService(service);
new Thread() {
@Override
public void run() {
connectTCPServer();
}
}.start();
}
@Override
protected void onDestroy() {
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
@Override
public void onClick(View v) {
if (v == mSendButton) {
final String msg = mMessageEditText.getText().toString();
if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
new Thread(new Runnable() {
@Override
public void run() {
mPrintWriter.println(msg);
}
}).start();
mMessageEditText.setText("");
String time = formatDateTime(System.currentTimeMillis());
final String showedMsg = "self " + time + ":" + msg + "\n";
mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
}
}
}
@SuppressLint("SimpleDateFormat")
private String formatDateTime(long time) {
return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
}
private void connectTCPServer() {
Socket socket = null;
while (socket == null) {
try {
socket = new Socket("localhost", 8688);
mClientSocket = socket;
mPrintWriter = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
System.out.println("connect server success");
} catch (IOException e) {
SystemClock.sleep(1000);
System.out.println("connect tcp server failed, retry...");
}
}
try {
// 接收服务器端的消息
BufferedReader br = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
while (!TCPClientActivity.this.isFinishing()) {
String msg = br.readLine();
System.out.println("receive :" + msg);
if (msg != null) {
String time = formatDateTime(System.currentTimeMillis());
final String showedMsg = "server " + time + ":" + msg
+ "\n";
mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg)
.sendToTarget();
}
}
System.out.println("quit...");
MyUtils.close(mPrintWriter);
MyUtils.close(br);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
<LinearLayout 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"
android:background="#ffffff"
android:orientation="vertical"
android:padding="5dp"
tools:context="${relativePackage}.${activityClass}" >
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/msg_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<EditText
android:id="@+id/msg"
android:layout_width="0dp"
android:layout_height="35dp"
android:layout_weight="1"
android:background="@drawable/edit"
android:ems="10"
android:padding="5dp" />
<Button
android:id="@+id/send"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:enabled="false"
android:text="发送" />
</LinearLayout>
</LinearLayout>
package com.example.socketdemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
public class TCPServerService extends Service {
private boolean mIsServiceDestoryed = false;
private String[] mDefinedMessages = new String[] {
"你好啊,哈哈",
"请问你叫什么名字呀?",
"今天北京天气不错啊,shy",
"你知道吗?我可是可以和多个人同时聊天的哦",
"给你讲个笑话吧:据说爱笑的人运气不会太差,不知道真假。"
};
@Override
public void onCreate() {
new Thread(new TcpServer()).start();
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
mIsServiceDestoryed = true;
super.onDestroy();
}
private class TcpServer implements Runnable {
@SuppressWarnings("resource")
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
System.err.println("establish tcp server failed, port:8688");
e.printStackTrace();
return;
}
while (!mIsServiceDestoryed) {
try {
// 接受客户端请求
final Socket client = serverSocket.accept();
System.out.println("accept");
new Thread() {
@Override
public void run() {
try {
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
};
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void responseClient(Socket client) throws IOException {
// 用于接收客户端消息
BufferedReader in = new BufferedReader(new InputStreamReader(
client.getInputStream()));
// 用于向客户端发送消息
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(client.getOutputStream())), true);
out.println("欢迎来到聊天室!");
while (!mIsServiceDestoryed) {
String str = in.readLine();
System.out.println("msg from client:" + str);
if (str == null) {
break;
}
int i = new Random().nextInt(mDefinedMessages.length);
String msg = mDefinedMessages[i];
out.println(msg);
System.out.println("send :" + msg);
}
System.out.println("client quit.");
// 关闭流
MyUtils.close(out);
MyUtils.close(in);
client.close();
}
}
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="rectangle" >
<solid android:color="#0ac39e" />
</shape>
</item>
<item android:bottom="6dp">
<shape android:shape="rectangle" >
<solid android:color="#ffffff" />
</shape>
</item>
<item
android:bottom="1dp"
android:left="1dp"
android:right="1dp">
<shape android:shape="rectangle" >
<solid android:color="#ffffff" />
</shape>
</item>
</layer-list>
WebSocket
什么是websocket
- websocket是一种在单个TCP连接上进行的全双工通信息,websocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据
- 浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并且进行双向数据传输
- websocket是一种计算机应用层协议,弥补了http协议在通信持久能力上的不足。
- 它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
- 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
- 数据格式比较轻量,,性能开销小,通信高效。可以发送文本,也可以发送二进制数据。
- 没有同源限制,客户端可以与任意服务器通信。
- 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
ws://example.com:80/some/path
推荐阅读:一文吃透 WebSocket 原理 刚面试完,趁热赶紧整理