基于java的socket编程及API解析

一、socket通讯过程

1、socket与socket编程简介:

socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。

我们所说的socket 编程,是站在传输层的基础上,所以可以使用 TCP/UDP 协议,但是不能进行访问网页,因为访问网页所需要的 http 协议位于应用层。作为一个应用程序是能实现该层以下的内容,而不能实现在该层之上的内容。

2、socket通讯过程:

(1)创建ServerSocket和Socket

(2)打开连接到Socket的输入/输出流

(3)按照协议对Socket进行读/写操作

(4)关闭输入输出流、关闭Socket

基于java的socket编程及API解析_第1张图片

 

2、linux中的socket与windows中socket文件之间的区别。

socket作为套接字,能够对数据进行包装后进行发送,socket本质是一个文件。

Linux中是将socket文件视为普通文件,而在windows中将socket文件看成是一个网络连接来对待,因此需要调用专门针对 socket 而设计的数据传输函数。

 

二、基于java的socket编程

客户端与服务端的使用在控制台的使用代码如下所示。基于java 的Socket编程实践主要调用了java的Socket和ServerSocketAPI,针对服务端需要绑定ServerSocket类,接口绑定后进行监听accept方法,而针对客户端,绑定接口之后采用发送数据的方式。

1、客户端通讯过程

(1)创建Socket对象,指明需要连接的服务器的地址和端口号

(2)连接建立后,通过输出流想服务器端发送请求信息

(3)通过输入流获取服务器响应的信息

(4)关闭响应资源 ​​​​​​​

 

import java.io.IOException;

import java.io.PrintWriter;

import java.net.InetAddress;

import java.net.Socket;

import java.net.UnknownHostException;

import java.util.Scanner;

 

public class Client{

    public static void testClient(){

       System.out.println("正在向服务器请求连接。。。");

       Socket socket = null;

       Scanner keybordscanner = null;

       Scanner inScanner = null;

       PrintWriter pwtoserver = null;

       try {

//使用的是InetAddress类获取连接到所有Socket编程的

           socket = new Socket(InetAddress.getLocalHost(),6668);

           inScanner = new Scanner(socket.getInputStream());

           System.out.println(inScanner.nextLine());

           pwtoserver = new PrintWriter(socket.getOutputStream());

           System.out.print("我(客户端):");

           keybordscanner = new Scanner(System.in);

           while(keybordscanner.hasNextLine()){

              String keyborddata = keybordscanner.nextLine();

              //客户端发送的消息

              System.out.println("我(客户端):"+keyborddata);

              //写到服务端的的控制台

              pwtoserver.println(keyborddata);

              pwtoserver.flush();

              //阻塞等待接收服务端的消息

              String indata = inScanner.nextLine();

              System.out.println("服务端:"+indata);

              System.out.print("我(客户端):");

           }

       } catch (UnknownHostException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       } catch (IOException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }finally {

           keybordscanner.close();

           pwtoserver.close();

           inScanner.close();

           try {

              socket.close();

           } catch (IOException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

           }

       }

      

    }

    public static void main(String[] args) {

       testClient();

    }

}

 

 

 

 

2、服务端:

(1)创建ServerSocket对象,绑定监听端口

(2)通过accept()方法监听客户端请求

(3)连接建立后,通过输入流读取客户端发送的请求信息

(4)通过输出流向客户端发送乡音信息

(5)关闭相关资源

 

import java.io.IOException;

import java.io.PrintWriter;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.Scanner;

 

public class Server{

    //服务器

    public static void testServer(){

       //创建一个服务器

       System.out.println("等待客户端连接。。。");

       PrintWriter pwtoclien = null;

       Scanner keybordscanner = null;

       Scanner inScanner = null;

       ServerSocket ss = null;

       try {

           ss = new ServerSocket(6667);

           //创建一个接收连接客户端的对象

           Socket socket = ss.accept();

           System.out.println(socket.getInetAddress()+"已成功连接到此台服务器上。");

           //字符输出流

           pwtoclien = new PrintWriter(socket.getOutputStream());

           pwtoclien.println("已成功连接到远程服务器!"+"\t"+"请您先发言。");

           pwtoclien.flush();

           keybordscanner = new Scanner(System.in);

           inScanner = new Scanner(socket.getInputStream());

           //阻塞等待客户端发送消息过来

           while(inScanner.hasNextLine()){

              String indata = inScanner.nextLine();

              System.out.println("客户端:"+indata);

              System.out.print("我(服务端):");

              String keyborddata = keybordscanner.nextLine();

              System.out.println("我(服务端):"+keyborddata);

              pwtoclien.println(keyborddata);

              pwtoclien.flush();

           }

            socket.shutdownInput();

            socket.shutdownOutput();

       } catch (IOException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }finally {

           pwtoclien.close();

           keybordscanner.close();

           inScanner.close();

           try {

              ss.close();

           } catch (IOException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

           }

       }

    }

    public static void main(String[] args) {

       testServer();

    }

}

 

 

这里采用的是单线程通讯。并且采用了阻塞printWriter.flush的方式控制通讯的顺序:客户端发送消息之后服务端才能回复消息,只有当服务端对客户端进行回应客户端才能再次发送消息,当双方都不进行发送文件时形成了死锁,只有发送文件之后才能解除死锁。

服务端的控制台程序如图所示,使用的是InetAddress类进行获取发送文件的Address。除此之外,编写程序过程中,在两个类中容易弄混淆的是GetInputSream与GetOutputStream,前者是用来读,后者是用来写的。

 基于java的socket编程及API解析_第2张图片

客户端:客户端在控制台的显示如下所示。

 基于java的socket编程及API解析_第3张图片

 

 

在linux中观察java函数服务端调用的情况

使用的是strace 命令对调用进行追踪

基于java的socket编程及API解析_第4张图片

 

 基于java的socket编程及API解析_第5张图片

 

 三、linux中网络接口简介

linux中建立连接

站在更贴近系统的层级去看,两个机器间的通信方式,无非是要通过运输层的TCP/UDP,网络层IP,因此socket本质是编程接口(API),对TCP/UDP/IP的封装,TCP/UDP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。
Socket的创建:

int socket (int domain, int type, int protocol);

创建一个服务器端的socket

int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

我们套接字已经创建好了,地址结构也已经了解了,接下来就是要将套接字和地址进行关联,关联的方法则是通过bind函数。

 

int bind(int sockfd, const struct sockaddr *addr, socklen_t len);

 

我们的socket已经创建出来了,当我们不再使用的时候,我们可以调用close函数来将其关闭,释放该文件描述符,这样便可以得到重新的使用。
套接字通信是双向的,但是,我们可以采用shutdown函数来禁止一个套接字的I/O

 
进行连接

#include int connect(int sockfd, const struct sockaddr *addr, socklen_t len);

关闭连接

int shutdown(int sockfd, int how);

 四、TCP与UDP在linux中关键API的区别。

TCP使用的SOCK_STREAM。

UDP使用的是socket(AF_INET,SOCK_DGRAM,0);

在linux中比较容易混淆的是recv与recvfrom,send 与sendto的API,前两者用于TCP的编程,后者用于UDP编程。

send与sendto

  int send( SOCKET s,    const char FAR *buf,    int len,    int flags );


  int PASCAL FAR sendto( SOCKET s, const char FAR* buf, int len, int flags,
  const struct sockaddr FAR* to, int tolen);

  s:一个标识套接口的描述字。
  buf:包含待发送数据的缓冲区。
  len:buf缓冲区中数据的长度。
  flags:调用方式标志位。
  to:(可选)指针,指向目的套接口的地址。
  tolen:to所指地址的长度。

 

 

 

  recv与recvfrom

  int recv( SOCKET s,   char FAR *buf,    int len,   int flags   );

  int PASCAL FAR recvfrom( SOCKET s, char FAR* buf, int len, int flags,
  struct sockaddr FAR* from, int FAR* fromlen);

  s:标识一个已连接套接口的描述字。
  buf:接收数据缓冲区。
  len:缓冲区长度。
  flags:调用操作方式。
  from:(可选)指针,指向装有源地址的缓冲区。
  fromlen:(可选)指针,指向from缓冲区长度值。

 

 

你可能感兴趣的:(基于java的socket编程及API解析)