网络上的两个程序经过一个双向的通信链接实现数据的交换,这个双向链路的一端称为一个Socket。Socket一般用来实现客户方和服务方的链接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号惟一肯定java
可是,Socket所支持的协议种类不只TCP/IP一种,所以二者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程git
PS:虽然凑字数这种技能早就点满了,但关于更多Socket及TCP/IP相关概念,还请各位看官自行/先行了解,这里再也不多作赘述github
Eclipse(若你没有Eclipse也没事儿,后边告诉你用命令行编译运行!)编程
AndroidStudio(若你自己就是用Eclipse开发安卓程序,那Eclipse就够了)网络
OK,话很少说,开干socket
首先在Eclipse新建一个Java项目,就叫SocketDemo吧ide
接下来我们要监听是否有客户端发送链接请求,若是有,则链接并处理工具
SocketDemo.java:
布局
public class SocketDemo { /** * 端口号 注意:0~1023为系统所保留端口号,选择端口号时应大于1023,具体随便你取 */ public static int PORT = 2345; public static void main(String[] args) { try { //serverSocket用于监听是否有客户端发送链接请求 ServerSocket serverSocket = new ServerSocket(PORT); System.out.println("服务启动..."); //serverSocket.accept():若是有客户端发送链接请求, //则返回一个socket供处理与客户端的链接,不然一直阻塞监听 Socket socket = serverSocket.accept(); System.out.println("与客户端链接成功..."); //这个MySocket是啥呢?是一个对socket的封装,方便操做 MySocket mySocket = new MySocket(socket); //因为MySocket继承于Thread,因此须要start()一下 //致于为啥要继承于Thread来封装socket,请看下方 MySocket类 mySocket.start(); } catch (IOException e) { e.printStackTrace(); } } }
注释中的两个问题,很好理解,很少说,直接看看MySocket是怎么写的吧:测试
MySocket.java
public class MySocket extends Thread { Socket mSocket; BufferedWriter mWriter; BufferedReader mReader; public MySocket(Socket socket) { this.mSocket = socket; try { mWriter = new BufferedWriter(new OutputStreamWriter( socket.getOutputStream(), "utf-8")); mReader = new BufferedReader(new InputStreamReader( socket.getInputStream(), "utf-8")); } catch (IOException e) { e.printStackTrace(); } } /** * 向客户端发送消息 * msg 发送消息内容 **/ public void send(String msg) { try { // 客户端按行(readLine)读取消息,因此每条消息最后必须加换行符 \n,不然读取不到 mWriter.write(msg + "\n"); mWriter.flush(); } catch (IOException e) { e.printStackTrace(); } } /** * * 不断读取来自客户端的消息,一旦收到消息,则自动回复 **/ @Override public void run() { super.run(); try { String line; //服务端按行读取消息 //不断按行读取,得到来自客户端的消息 while ((line = mReader.readLine()) != null) { System.out.println("客户端消息:" + line); //收到客户端消息后,自动回复 send("已经收到你发送的\"" + line + "\""); } mReader.close(); } catch (IOException e) { e.printStackTrace(); } System.out.println("end"); } }
看完MySocket以后豁然开朗,原来将读取客户端消息的操做是阻塞的,要放在子线程来作,因此继承于Thread会方便一点
那么至此,服务端的程序已经写完了
什么?你问怎么这么简单?!缘由有两个:
这只是一个基础的Socket服务端程序,不用考虑那么多其余状况,天然几行代码就搞定了
没错,Socket就是这么简单!
接下来你会发现,客户端特么更简单!
第一步新建一个安卓项目,也叫SocketDemo吧,毕竟,凑字数这个技能我比较熟练
简单一点,布局中就一个按钮(id=btn_send),用来发送消息,初窥嘛,简单就是王道,布局代码就不上了
接下来看看MainActivity的代码:
不行,在看MainActivity以前还有一些话要交代清楚:
若是你将安卓程序跑在电脑的虚拟机上,则你访问的IP地址为:10.0.2.2(虚拟机只能经过这个IP访问电脑)
若是你将安卓程序跑在真机上,那么你须要在CMD中输入ipconfig获取到IPv4地址,而且确保手机和电脑在同一个网络下(链接了同一个WIFI)
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); connectServer(); findViewById(R.id.btn_send).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { sendMsg("2333"); } }); } private Socket mSocket; private BufferedWriter mWriter; private BufferedReader mReader; //这个IP上面解释过了噢,要理解一下 private static String IP = "10.0.2.2"; //切记端口号必定要和服务端保持一致! private static int PORT = 2345; private void connectServer() { new Thread(new Runnable() { @Override public void run() { try { mSocket = new Socket(IP, PORT); mWriter = new BufferedWriter(new OutputStreamWriter( mSocket.getOutputStream(), "utf-8")); mReader = new BufferedReader(new InputStreamReader( mSocket.getInputStream(), "utf-8")); Log.i(TAG, "链接服务端成功"); } catch (IOException e) { Log.i(TAG, "链接服务端失败"); e.printStackTrace(); return; } try { String line; while ((line = mReader.readLine()) != null) { Log.i(TAG, "服务端消息: " + line); } } catch (IOException e) { e.printStackTrace(); Log.i(TAG, "服务端:已中止服务"); } } }).start(); } private void sendMsg(String msg) { // 若是mSocket为null有可能两种状况: // 1.还在尝试链接服务端 // 2.链接失败 if (mSocket == null){ Toast.makeText(this,"链接未完成或链接失败,没法发送消息!",Toast.LENGTH_SHORT).show(); return; } try { //服务端是按行读取消息,因此每条消息最后必须加换行符 \n mWriter.write(msg + "\n"); mWriter.flush(); } catch (IOException e) { Toast.makeText(this,"发送失败:服务端已关闭服务!",Toast.LENGTH_SHORT).show(); e.printStackTrace(); } } }
这就写完了客户端??对,这就写完了...那你别问为啥Socket咋就这么点内容,Socket原本也不是啥难点~
而且,这只是一个很是很是基础的Demo
OK,到这里就能够来跑一下程序试一试了
若是你有Eclipse,那么直接在Eclipse内跑起来就好了!
若是很不巧,你没有Eclipse
新建本文章服务端部分的SocketDemo.java
和MySocket.java
两个文件,而且放在同一个文件夹下,上面代码没有写出import包,不能直接copy进文件内用,文末我会放出全部源代码,到文末copy一下放在两个文件内就好了(固然你得确保你有JDK环境!虽然做为安卓狗,这是必要的,但仍是提醒一下!)
打开CMD,切换进入上述两个文件所在的目录
执行
javac *.java java SocketDemo
就将程序跑起来了(ctrl+c退出程序)
注意事项:
在Eclipse内运行的程序,切记:若是修改内容后要从新启动程序,请先将正在运行的程序关闭,不然将一直占用端口!没法再以此端口再次启用一次程序!
若是用CMD运行的程序,提示编码错误,请将全部中文替换成英文,或者将两个.java文件内容转换成GBK编码(建议换成英文!英文好的哥们儿,上!)
直接跑安卓程序就好了!
在Eclipse跑服务端的图已经在文首放出,这里放一个CMD下跑服务端的图片:
注:不知为何发送消息的时候,命令行及LogCat不会即时显示出内容,在我ctrl+c退出程序以后才会一次全出来,如有知道的朋友,还望指教!万分感谢!
想一下,服务端程序只响应一个客户端,若是又有客户端发出链接请求,那岂不是没法响应了!
再想一下以为不对,也就是我本身测试,哪来的第二个客户端发出链接请求
再再想一下,若是你改了一下安卓端的代码,又一次点了运行,那谁来响应你?!这样的话,由于修改安卓端代码,又得去把服务端的程序停了,再启动一下,多麻烦!
好吧,既然分析了确实有这个麻烦,那就把它解决掉:
public class SocketDemo { public static int PORT = 2745; public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(PORT); System.out.println("服务启动..."); //写一个死循环,若是有一个客户端链接成功,那么继续让serverSocket.accept()阻塞住 //等待下一个客户端请求,这样不论有多少个客户端请求过来,均可以响应到, //结束调试的时候再关闭服务端程序 while (true) { Socket socket = serverSocket.accept(); System.out.println("客户端链接成功..."); MySocket mySocket = new MySocket(socket); mySocket.start(); } } catch (IOException e) { e.printStackTrace(); } } }
so easy~不解释了~
至此整个SocketDemo就完成了,对Socket的第一步已经迈出了,那么赶忙理解好,而后再深刻Socket吧!
SocketDemo.java:
import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class SocketDemo { public static int PORT = 2745; public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(PORT); System.out.println("服务启动..."); while (true) { Socket socket = serverSocket.accept(); System.out.println("客户端链接成功..."); MySocket mySocket = new MySocket(socket); mySocket.start(); } } catch (IOException e) { e.printStackTrace(); } } }
MySocket.java:
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; public class MySocket extends Thread { Socket mSocket; BufferedWriter mWriter; BufferedReader mReader; public MySocket(Socket socket) { this.mSocket = socket; try { mWriter = new BufferedWriter(new OutputStreamWriter( socket.getOutputStream(), "utf-8")); mReader = new BufferedReader(new InputStreamReader( socket.getInputStream(), "utf-8")); } catch (IOException e) { e.printStackTrace(); } } public void send(String msg) { try { mWriter.write(msg + "\n"); mWriter.flush(); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { super.run(); try { String line; while ((line = mReader.readLine()) != null) { System.out.println("客户端消息:" + line); send("收到:\"" + line + "\""); } mReader.close(); } catch (IOException e) { e.printStackTrace(); } System.out.println("end"); } }
客户端(安卓端)的我就不放了!