JAVA网络通信底层调用LINUX探究
逆凡 人气:1前言:该博客花了我一个下午得心血,全部手打,路过给个赞,拒绝抄袭!!!!!!!!!!!!!!!!!!!!!!!!!
简单的SOCKET通信程序
先从一段简单的JAVA程序性开始写起,这里我们才用半双工的形式,这里的半双工意思是客户端可以给服务端发送数据,发完数据就关闭,而服务端可以一直接受数据
我们使用多线程方式,这个不重要
下面是线程类
1 public class SocketThread implements Runnable { 2 public SocketThread(Socket socket) { 3 this.socket = socket; 4 } 5 6 private Socket socket; 7 @Override 8 public void run() { 9 InputStream inputStream = null; 10 InputStreamReader inputStreamReader = null; 11 BufferedReader bufferedReader = null; 12 try { 13 inputStream = socket.getInputStream(); 14 inputStreamReader = new InputStreamReader(inputStream); 15 bufferedReader = new BufferedReader(inputStreamReader); 16 char buf[] = new char[1024]; 17 int len=0; 18 while ((len=bufferedReader.read(buf))!=-1){ 19 for(int i=0;i<len;i++){ 20 System.out.print(buf[i]); 21 } 22 System.out.println(); 23 } 24 }catch (Exception e){ 25 e.printStackTrace(); 26 }finally { 27 try { 28 inputStream.close(); 29 inputStreamReader.close(); 30 bufferedReader.close(); 31 } catch (IOException e) { 32 e.printStackTrace(); 33 } 34 } 35 } 36 }
说实话,这个博客园的博客丑的一B,为什么不能提交CSDN的博客呢?
服务端代码:
1 public class Server { 2 public static void main(String[] args) { 3 try{ 4 ServerSocket serverSocket = new ServerSocket(8888); 5 System.out.println("服务端已启动,等待连接"); 6 while(true){ 7 Socket socket = serverSocket.accept(); 8 SocketThread socketThread = new SocketThread(socket); 9 new Thread(socketThread).start(); 10 } 11 }catch (Exception e){ 12 e.printStackTrace(); 13 } 14 } 15 }
客户端代码:
1 public class Client { 2 public static void main(String[] args) { 3 try { 4 Socket socket = new Socket("localhost",8888); 5 OutputStream outputStream = socket.getOutputStream(); 6 Scanner scan = new Scanner(System.in); 7 while(scan.hasNext()){ 8 String str = scan.next(); 9 outputStream.write(str.getBytes()); 10 outputStream.flush(); 11 } 12 }catch (Exception e){ 13 e.printStackTrace(); 14 } 15 } 16 }
分析
想要搞明白JAVA和LINUX之间的关系,首先你得明白我们的java代码编译成class文件后是运行在JVM上,这个CLASS字节码文件只有我们JVM认识,你扔给操作系统,操作系统铁定不懂。
JVM也是一个软件,它是由C++和C编写的,我们姑且认为它是C编写的,相信大家都看过JAVA源码,太多方法都是调用了native方法,比如我们的bind()方法
其次,JAVA的很多功能都是通过调用操作系统函数来实现的,比如显示文字
JAVA比如建立Socket是通过告诉虚拟机这个指令,然后虚拟机调用操作系统的Socket()函数来实现的
查看一下LINUX的SOCKET函数
简单说一下,第一个参数是域类型,看到IPV4选它就完事了,第二个参数是协议,我们用SOCK_STRAM表示TCP协议,第三个取0,0会把前两个参数对应起来,写0就完事了
return value是返回一个文件描述符,这个很难明白,你可以把它理解为我们JAVA里面的对象,JAVA当中有句话叫一切皆对象,而在LINUX中相应的我们叫做一切皆文件
只要我们拿到这个文件描述符,就能操作相应的Socket,牛逼吧= =
好的,下面讲一下验证思路
我们写一个JAVA方法,这个方法也调用NATVIE方法,而这个方法就不是JVM提供的C了,而是我们自己写的一个C文件,用这个C文件的函数调用操作系统内核函数来建立Socket连接,这里的StartServer()
方法就相当于我们之前的ServerSocket socket = new ServerSocket();如果能调用成功,是不是说明JAVA就是通过调用LINUX函数实现的呢?
正篇开始
先放代码
在我们JAVA中有socket.bind(),在C语言中怎么写呢?也是一样的,调用bind()函数即可,我们也来瞧瞧
第一个字段就是把我们刚刚的文件描述符传过来,第二个就比较复杂了我的哥,在C语言中我们的参数非常复杂,传入参数,传出参数和传入传出参数
啥意思?其实很简单,传入参数就是传进来修改不会影响外面,类似局部变量,。。。传入传出就是指针啥的嘛,我们这里就是结构体指针,但是如果加了一个const就不一样
const 意思是我们不能修改它即只能传入,不能传出
这个sockaddr 结构体里面有两个参数,一个是IP,一个是端口号PORT,就是IP+端口,最后是你结构体的长度
为什么我这里写的是sockaddr_in呢?参数不是sockaddr嘛?你可以把这个理解为JAVA当中的类型,人家要一个INT结果你传一个字符串
这个其实是在UNIX开发之初,我们的IP协议还没有很稳定的时候是用sockaddr来描述的,但是随着计算机的发展,IP协议越来越复杂,所以它开发了另一个函数叫Sockaddr_in来表示我们的IP和端口号,所以这个方法已经过时了,但是为什么没改掉呢?这个很简单啊,和JAVA一样它在很多地方都有使用,你不能随便改,就像JAVA一样我们都是声明接口,传入实现类等等保障系统的可扩展性。那怎么办?其实很简单,我们传一个参数进来然后把它强转成这个类型不就好了
这个htons是啥?这个叫本地转换成网络字节数,短整型,说白了,你这个8080人家LINUX不认识,你需要通过转换。比如你传一个192.168.。。。人家LINUX不认识,它需要把它转换成123456的整形才认识,这就是提供转换的函数
INADDR_ANY是啥,你也可以换成localhost,这里表示当前本机任何一个可用的IP地址
这个listen是监听你当前Socket当中在同一时刻能够接受的连接数,它默认值是128,无所谓,他不是关键
这是啥?传出来的是C语言类型的数据,不是我们能看懂的IP等,我们需要转换一下,就转到这个数组中
这个就是读数据啊,读到这个字符串数据里,以及它的长度
关键
这里涉及JNI调用,不懂得自己去看吧。关于什么是JNI我就不解释了
首先来段简单代码,这个conn()会调用到我们自己写得C方法
把这个JAVA文件拷贝到LINUX系统里
然后编译这个java文件成.h文件
关键来了,我们查看我们编译好的.h文件
然后这个.h文件可以在我们写的函数里导入进来那我们那个方法怎么命名,参考.h文件,这个.h中生成了一个conn()方法的C语言调用连接
这个JNIEXPORT。。。就是我们得调用函数名称,替换C文件得方法名即可
注意一下,你必须指定你的classpath路径,否则我们得java System.loadLibray是找不到得,很简单得道理,你写JAVA得时候你在当前目录下得文件,你要JAVA怎么运行呢?
哈哈哈,可以看到我们运行JAVA已经调用到LINUX下得内核函数了,下面来测试一哈
牛逼吹完了,看看效果
这个nc是用来模拟连接得,可以直接通信了,哈哈哈
证明成功!
总结:好累啊,再也不想这么认真写实验了= =。写了一个下午,我要去划水
加载全部内容