先来介绍下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...\n").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+"\n").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("\n"+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()+"\r\n").getBytes("utf-8")); //这地方后面一定得加上\n因为服务端那边readLine()读取的是以换行为结束标识。之前发不出去数据,代码调试了好久,头都大了。
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
Looper.loop();
} catch (IOException e) {
e.printStackTrace();
} }
}
ok。