先来介绍下socket,网上摘抄点资料,免得自己打字了
网络中进程之间如何通信?
本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类:
-
1、消息传递(管道、FIFO、消息队列)
-
2、同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)
-
3、共享内存(匿名的和具名的)
-
4、远程过程调用(Solaris门和Sun RPC)
但这些都不是本文的主题!我们要讨论的是网络中进程之间如何通信?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。
使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(已经被淘汰),来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是我为什么说“一切皆socket”。
2、什么是Socket?
上面我们已经知道网络中的进程是通过socket来通信的,那什么是socket呢?socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。
socket一词的起源:在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的,撰写者为Stephen Carr、Steve Crocker和Vint Cerf。根据美国计算机历史博物馆的记载,Croker写道:“命名空间的元素都可称为套接字接口。一个套接字接口构成一个连接的一端,而一个连接可完全由一对套接字接口规定。”计算机历史博物馆补充道:“这比BSD的套接字接口定义早了大约12年。”
TCP/IP协议基础
Ip协议是internet上使用的一个关键协议,它的全称是Intentnet Protocol ,即Internet协议,通常简称IP协议。通过IP协议,使Internet成为一个允许连接不同类型的计算机和不同操作系统的网络。
要使两台计算机彼此之间进行通信,必须使两台计算机使用一种“语言” ,IP协议只保证计算机能发送和接收分组数据。IP协议负责将消息从一个主机传送到另一个主机,消息在传送的过程中被分割成一个个小包。
尽管计算机通过安装IP软件,保证了计算机之间可以发送和接收数据,但IP协议还不能解决数据分组在传输过程中可能出现的问题。因此,若要解决可能出现的问题,连接上Internet的计算机还需要安装TCP协议来提供可靠并且无差错的通信服务。
TCP协议被称为一种端对端协议。这是因为它为两台计算机之间的连接起了重要作用:当一台计算机需要与另一台远程计算机连接时,TCP协议会让他们建立一个连接:用于发送和接收数据的虚拟链路。
TCP协议负责收集这些信息包。并将其按照适当的次序放好传送,在接收端收到后再将其正确的还原。TCP协议保证了数据包在传送中准确无误。TCP协议使用重发机制:当一个通信实体发送一个消息给另一个通信实体后,需要收到另一个通信实体的确认消息,如果没有收到另一个通信实体的确认消息,则会再次重发刚才发送的消息。
通过这种重发机制。TCP协议向应用程序提供可靠的通信连接,使他能够自动适应网上的各种变化。即使在Internet暂时出现堵塞的情况下,TCP也能保证通信的可靠。
综上所述,虽然IP和TCP协议的功能不同,也可以分开单独使用,但它们是在同一时期作为一个协议来设计的,并且在功能上也是互补的。只有两者结合,才能保证Internet在复杂的环境下正常运行。凡是要连接Internet的计算机,都必须同时安装和使用这两个协议,因此在实际中常把这两个协议统称为TCP/IP协议。
使用ServerSocket创建TCP服务端
上图中我们可以看出Tcp通信的两个通信实体是没有区分服务端和客户端的,但是实在他们建立起连接后的状态。如果没有建立起连接的话,必须需要有一端释放出“主动”的姿态,等待连接,那么这一端就是服务端,连接的一端就是客户端。
先来看看效果:
服务端:pc上
客户端:真机和模拟器
目的:建立一个多客户端的简易聊天室。(下图你也能看到,模拟器连接上了,pc服务端会打印一个连接数1,真机连接上服务端了,pc端会打印一个连接数2)
PC服务端代码编写:
import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; public class MyServer { public static ArrayList<Socket> socketList = new ArrayList<>(); public static void main(String[] args) { try { //建立服务端 InetAddress inetAddress = InetAddress.getLocalHost(); //打印本地的ip System.out.println("ip:"+inetAddress.getHostAddress()); ServerSocket serverSocket = new ServerSocket(30000); while(true){ //循环接收socket Socket socket = serverSocket.accept(); OutputStream os = socket.getOutputStream(); os.write(("欢迎连接Mdm服务端socket... ").getBytes("utf-8")); // os.close(); socketList.add(socket); //join in socketList new Thread(new ServerThread(socket)).start(); //new thread Deal with this matter. System.out.println("当前连接数"+socketList.size()); } //that end java code written. } catch (IOException e) { e.printStackTrace(); } } }
ServerThread.java
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.Socket; public class ServerThread implements Runnable { BufferedReader br = null; Socket s; public ServerThread(Socket s){ try { this.s = s; br = new BufferedReader(new InputStreamReader(s.getInputStream(),"utf-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { String content = null; //循环判断client发送过来的数据不为null while((content = readFromClient()) != null){ //遍历每一列表中的每一个socket给他们发送消息类似聊天室的感觉每个人都收到了消息 for (Socket otherClient : MyServer.socketList) { try { //输出流进行输出 OutputStream os = otherClient.getOutputStream(); os.write((content+" ").getBytes("utf-8")); // os.close();//关闭流 } catch (IOException e) { e.printStackTrace(); } } } } /** * 获取从clientSocket中发过来的消息 * @return */ private String readFromClient(){ try { return br.readLine(); } catch (IOException e) { e.printStackTrace(); //if hava excption remove this socket in socketList. MyServer.socketList.remove(s); } return null; } }
Android 客户端代码
activity_multi_thread_client.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <!--用来发送给服务端的数据--> <EditText android:id="@+id/input" android:layout_weight="1" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="send" /> </LinearLayout> <!--用来获取显示服务端发送过来的数据--> <TextView android:id="@+id/show" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="top" android:background="#ffff" android:textSize="14dp"/> </LinearLayout>
MultiThreadClient.java
public class MultiThreadClient extends AppCompatActivity { private EditText input; private TextView show; private Handler handler; private ClientThread clientThread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_multi_thread_client); input = (EditText) findViewById(R.id.input); show = (TextView) findViewById(R.id.show); handler = new Handler(){ @Override public void handleMessage(Message msg) { //receive ServerSocket data. if(msg.what == 0x123) show.append(" "+msg.obj.toString()); } }; clientThread = new ClientThread(handler); new Thread(clientThread).start(); Button send = (Button) findViewById(R.id.send); send.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Message msg = Message.obtain(); msg.what = 0x345; msg.obj = input.getText().toString(); clientThread.sendHandler.sendMessage(msg); input.setText(""); } }); } }
ClientThread.java
public class ClientThread implements Runnable { //接收服务端发送的数据 private Handler handler; //send client data. public Handler sendHandler; private BufferedReader br; private OutputStream os; public ClientThread(Handler handler){ this.handler = handler; } @Override public void run() { try { //connection server Socket Socket socket = new Socket("192.168.1.110",30000); br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"utf-8")); os = socket.getOutputStream(); //print server data in TextView new Thread(){ @Override public void run() { try { String content = null; while ((content = br.readLine()) != null) { Message msg = Message.obtain(); msg.what = 0x123; msg.obj = content; handler.sendMessage(msg); } }catch (IOException ex){ ex.printStackTrace(); } } }.start(); //send client data to server Looper.prepare(); sendHandler = new Handler(){ @Override public void handleMessage(Message msg) { try { if(msg.what == 0x345) { os.write((msg.obj.toString()+" ").getBytes("utf-8")); //这地方后面一定得加上 因为服务端那边readLine()读取的是以换行为结束标识。之前发不出去数据,代码调试了好久,头都大了。 } } catch (IOException e) { e.printStackTrace(); } } }; Looper.loop(); } catch (IOException e) { e.printStackTrace(); } } }
ok。