Socket,你需要知道的事兒
what is socket
socket作為一種抽象層,應用程序通過它來發送和接收數據,使用socket可以將應用程序與處于同一網絡中的其他應用程序進行通信交互。簡而言之,socket提供了應用程序內部與外界通信的端口以及為通信雙方提供了數據傳輸的通道。
對比
android與服務器通信主要有兩種方式:Http通信與Socket通信。
差異:
Http通信
- 請求—響應方式”
- 請求時建立連接通道,當客戶端向服務器發送請求后,服務器端才能向客戶端返回數據
Socket通信
- 雙方建立起連接后就可以直接進行數據的傳輸,在連接時可實現信息的主動推送,而不需要每次由客戶端想服務器發送請求
- 數據丟失率低,使用簡單,易于移植
1132780-4e0c4064e76528b6.jpg
模型
Socket基本通信模式如下:
3.jpg
從上面模型可以看出Socket可以是基于Tcp協議的Socket通信或基于Udp協議的Socket通信,今天筆者就講基于Tcp的Socket通信。
Tcp通信模型如下:
2.jpg
了解模型后,優先需要理解Tcp Socket原理,重點來啦~
1132780-2d7d1306d1b86b65.jpg
原理
服務器端首先聲明一個ServerSocket對象并且指定端口號,然后調用Serversocket的accept()方法接收客戶端的數據。accept()方法在沒有數據進行接收的處于堵塞狀態。(Socketsocket=serversocket.accept()),一旦接收到數據,通過inputstream讀取接收的數據。 客戶端創建一個Socket對象,指定服務器端的ip地址和端口號(Socket socket=new Socket("172.17.30.12",9999);),通過inputstream讀取數據,獲取服務器發出的數據(OutputStream outputstream = socket.getOutputStream()),最后將要發送的數據寫入到outputstream即可進行TCP協議的socket數據傳輸。
實踐
原理懂了,則需實踐,毛主席說過“實踐是檢驗真理的唯一標準”。
1132780-c9a67cd151670731.jpg
客戶端:
package cn.jianke.socket.tcp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
/**
- @className: TcpSocketClient
- @classDescription: tcp套接字客戶端
- @author: leibing
@createTime: 2016/10/06
*/
public class TcpSocketClient {
// 服務端地址
private String serverIp = "172.17.30.12";
// 服務端端口號
private int serverPort = 9999;
// 套接字
private Socket mSocket = null;
// 緩沖區讀取
private BufferedReader in = null;
// 字符打印流
private PrintWriter out = null;
// tcp套接字監聽
private TcpSocketListener mTcpSocketListener;
// 內容
private String content = "";
/**
- 構造函數
- @author leibing
- @createTime 2016/10/06
- @lastModify 2016/10/06
- @param mTcpSocketListener tcp套接字監聽
@return
*/
public TcpSocketClient(TcpSocketListener mTcpSocketListener){
this.mTcpSocketListener = mTcpSocketListener;
}
/**
- 構造函數
- @author leibing
- @createTime 2016/10/06
- @lastModify 2016/10/06
- @param serverIp = 服務端地址
- @param serverPort 服務端口號
- @param mTcpSocketListener tcp套接字監聽
@return
*/
public TcpSocketClient(String serverIp, int serverPort , TcpSocketListener mTcpSocketListener){
this.serverIp = serverIp;
this.serverPort = serverPort;
this.mTcpSocketListener = mTcpSocketListener;
}
/**
- 啟動tcp套接字連接
- @author leibing
- @createTime 2016/10/06
- @lastModify 2016/10/06
- @param
@return
*/
public void startTcpSocketConnect(){
// 開啟一個線程啟動tcp socket
new Thread(new Runnable() {
@Override
public void run() {
try {
mSocket = new Socket(serverIp, serverPort);
in = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
mSocket.getOutputStream())), true);
while (true) {
if (mSocket.isConnected()) {
if (!mSocket.isInputShutdown()) {
if ((content = in.readLine()) != null) {
content += "\n";
if (mTcpSocketListener != null)
mTcpSocketListener.callBackContent(content);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
/**
- 通過tcp套接字發送消息
- @author leibing
- @createTime 2016/10/06
- @lastModify 2016/10/06
- @param
@return
*/
public void sendMessageByTcpSocket(String msg){
if (mSocket != null && mSocket.isConnected()){
if (!mSocket.isOutputShutdown() && out != null){
out.println(msg);
if (mTcpSocketListener != null)
mTcpSocketListener.clearInputContent();
}
}
}
/**
- @interfaceName:
- @interfaceDescription: tcp套接字監聽
- @author: leibing
- @createTime: 2016/10/06
*/
public interface TcpSocketListener{
// 回調內容
void callBackContent(String content);
// 清除輸入框內容
void clearInputContent();
}
}</code></pre>
封裝一個TcpSocket客戶端類,寫構造函數,設置服務端ip地址、端口號并設置監聽(用于回調從Socket服務端接收的數據),寫啟動tcp套接字連接方法,其中BufferedReader in用于接收服務端數據,PrintWriter out用于向服務端發送數據。
package cn.jianke.socket.module;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import cn.jianke.socket.R;
import cn.jianke.socket.tcp.TcpSocketClient;
/**
- @className:MainActivity
- @classDescription: tcp套接字客戶端頁面
- @author: leibing
@createTime: 2016/10/06
*/
public class MainActivity extends AppCompatActivity {
// 服務端地址
private final static String serverIp = "172.17.30.12";
// 服務端口號
private final static int serverPort = 9999;
// 控件
private TextView showTv;
private EditText contentEdt;
private Button sendBtn;
// tcp套接字客戶端
private TcpSocketClient mTcpSocketClient;
// 自定義Handler,用于更新Ui
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// findView
showTv = (TextView) findViewById(R.id.tv_show);
contentEdt = (EditText) findViewById(R.id.edt_content);
// 初始化tcp套接字客戶端
mTcpSocketClient = new TcpSocketClient(serverIp, serverPort,
new TcpSocketClient.TcpSocketListener() {
@Override
public void callBackContent(final String content) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (showTv != null)
showTv.setText(showTv.getText().toString() + content);
}
});
}
@Override
public void clearInputContent() {
if (contentEdt != null)
contentEdt.setText("");
}
});
// 啟動tcp套接字連接
mTcpSocketClient.startTcpSocketConnect();
// onClick
findViewById(R.id.btn_send).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String msg = contentEdt.getText().toString().trim();
mTcpSocketClient.sendMessageByTcpSocket(msg);
}
});
}
@Override
protected void onDestroy() {
// 斷開tcp鏈接
if (mTcpSocketClient != null)
mTcpSocketClient.sendMessageByTcpSocket("exit");
super.onDestroy();
}
}</code></pre>
在頁面初始TcpSocketClient類(設置服務端ip地址、端口以及設置監聽),并啟動啟動tcp套接字連接。
服務端:
package cn.jianke.socket.tcp;
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.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
- @className: TcpSocketServer
- @classDescription: tcp套接字服務端
- @author: leibing
@createTime: 2016/10/06
*/
public class TcpSocketServer {
// 端口號
private final static int serverPort = 9999;
// tcp套接字列表
private List<Socket> mList = new ArrayList<Socket>();
// 套接字服務
private ServerSocket server = null;
// 線程池
private ExecutorService mExecutorService = null;
/**
- 主函數入口
- @author leibing
- @createTime 2016/10/06
- @lastModify 2016/10/06
- @param args
@return
*/
public static void main(String[] args) {
// 啟動tcp套接字服務
new TcpSocketServer();
}
/**
- 啟動tcp套接字服務
- @author leibing
- @createTime 2016/10/06
- @lastModify 2016/10/06
- @param
@return
*/
public TcpSocketServer() {
try {
server = new ServerSocket(serverPort);
System.out.print("server start ...");
Socket client = null;
//create a thread pool
mExecutorService = Executors.newCachedThreadPool();
while(true) {
client = server.accept();
mList.add(client);
//start a new thread to handle the connection
mExecutorService.execute(new TcpSocketService(client));
}
}catch (Exception e) {
e.printStackTrace();
}
}
/**
- @className: TcpSocketService
- @classDescription: tcp套接字服務
- @author: leibing
@createTime: 2016/10/06
*/
class TcpSocketService implements Runnable {
// 套接字
private Socket socket;
// 緩沖區讀取
private BufferedReader in = null;
// 消息
private String msg = "";
/**
* 構造函數
* @author leibing
* @createTime 2016/10/06
* @lastModify 2016/10/06
* @param socket 套接字
* @return
*/
public TcpSocketService(Socket socket) {
this.socket = socket;
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
msg = "tips: user" +this.socket.getInetAddress() + " come";
this.sendmsg();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
while(true) {
if((msg = in.readLine())!= null) {
if(msg.equals("exit")) {
mList.remove(socket);
in.close();
msg = "tips: user" +this.socket.getInetAddress() + " exit";
socket.close();
this.sendmsg();
break;
} else {
msg = socket.getInetAddress() + ":" + msg;
this.sendmsg();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
- 發送消息
- @author leibing
- @createTime 2016/10/06
- @lastModify 2016/10/06
- @param
- @return
*/
public void sendmsg() {
System.out.println(msg);
int num =mList.size();
for (int index = 0; index < num; index ++) {
Socket mSocket = mList.get(index);
PrintWriter pout = null;
try {
pout = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(mSocket.getOutputStream())),true);
pout.println(msg);
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
}</code></pre> 初始化ServerSocket指定端口,將tcp套接字服務放入線程池與客戶端進行交互,BufferedReader in用于接收客戶端發來的數據,PrintWriter pout 用于向客戶端發送數據。
效果圖
服務端:
server start ...tips: user/172.17.30.15 come
/172.17.30.15:夠么
/172.17.30.15:不夠
/172.17.30.15:哈根
/172.17.30.15:里咯哦圖
/172.17.30.15:諾魔圖
/172.17.30.15:這是一個socket
/172.17.30.15:hgdh
客戶端:

TcpSocket.gif
來自:http://www.jianshu.com/p/1001a48750c6