一、socket通讯过程
1、socket与socket编程简介:
socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。
我们所说的socket 编程,是站在传输层的基础上,所以可以使用 TCP/UDP 协议,但是不能进行访问网页,因为访问网页所需要的 http 协议位于应用层。作为一个应用程序是能实现该层以下的内容,而不能实现在该层之上的内容。
2、socket通讯过程:
(1)创建ServerSocket和Socket
(2)打开连接到Socket的输入/输出流
(3)按照协议对Socket进行读/写操作
(4)关闭输入输出流、关闭Socket
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,前者是用来读,后者是用来写的。
客户端:客户端在控制台的显示如下所示。
在linux中观察java函数服务端调用的情况
使用的是strace 命令对调用进行追踪
三、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
进行连接 #includeint 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缓冲区长度值。