一、Java Socket的概述
1、Socket套接字方便了开发网络应用程序。TCP面向连接的可靠传输协议、具有数据确认和数据重传机制。保证了发送数据一定能到达通信的对方。UPD协议无连接,不可靠的传输协议。不具有数据确认和数据重传机制。socket是套接字的意思,一般用来描述IP地址和端口,是一个通信链的句柄。应用程序通常通过套接字向网络发出请求或者应答网络请求。摘一段比喻,有助于理解。socket非常类似于电话插座。以一个国家级电话网为例。电话的通话双方相当于相互通信的2个进程,区号是它的网络地址:区内一个单位的交换机相当于一台主机,主机分配给每个用户的局内号码相当于socket号。任何用户在通话之前,首先要占有一部电话机。相当于申请一个socket;同时要知道对方的号码,相当于对方有一个固定的socket。然后向对方拨号呼叫,相当于发出连接请求。
2、 Socket是著名的网络应用编程接口(API)之一,而Java语言是网络编程的主要语言,提供了强大和独特的网络通讯支持机制和能力。Socket机制成功的解决了两台主机不同进程之间的通信问题。Socket通信机制它采用客户服务器模式,由服务器方先建立自己的半相关(建立Socket并将Socket联编到某个端口上),并进入监听状态,同时监听是否有与自己端口相对应的连接请求。连接是客户方发送的。客户方在建立自己的半相关后,向服务器发起连接(调用方法conmect(),服务器在检测到连接后,接受连接(调用方法accept()),这样就建立起来一个完整的连接。
二、ServerSocket类和Socket类的常用方法
Socket类
Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定IP地址的指定端口号。
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
Socket(InetAddress address, int port, InetAddress localAddr, int localPort) 创建一个套接字并将其连接到指定远程地址上的指定远程端口。
Socket(String host, int port, InetAddress localAddr, int localPort) 创建一个套接字并将其连接到指定远程主机上的指定远程端口。
close() 关闭此套接字。
connect(SocketAddress endpoint) 将此套接字连接到服务器。
connect(SocketAddress endpoint, int timeout) 将此套接字连接到服务器,并指定一个超时值。
getInetAddress() 返回套接字连接的地址。
getInputStream() 返回此套接字的输入流。
getLocalPort() 返回此套接字绑定到的本地端口。
getOutputStream() 返回此套接字的输出流。
getPort() 返回此套接字连接到的远程端口。
ServerSocket类
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
accept() 侦听并接受到此套接字的连接。
getInetAddress() 返回此服务器套接字的本地地址。
三、TCP通信
服务器端步骤:
1、创建ServerSocket对象,绑定监听端口。
2、通过accept()方法监听客户端请求。
3、连接建立后,通过输入流读取客户端发送的请求信息。
4、通过输出流向客户端发送响应信息。
5、关闭响应的资源。
客户端步骤:
1、创建Socket对象,指明需要连接的服务器的地址和端口号。
2、连接建立后,通过输出流向服务器发送请求信息。
3、通过输入流获取服务器响应的信息。
4、关闭相应资源。
四、代码实现
服务端:
package test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws IOException { //建立tcp的服务端 ServerSocket serverSocket = new ServerSocket(9090); //接受客户端的连接,产生一个Socket Socket socket = serverSocket.accept(); //获取到Socket的输入流对象 BufferedReader socketReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //获取到Socket输出流对象 OutputStreamWriter socketOut = new OutputStreamWriter(socket.getOutputStream()); //获取键盘的输入流对象 BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in)); //读取客户端的数据 String line = null; while((line = socketReader.readLine())!=null){ System.out.println("客户端:"+ line); System.out.println("服务端:"); line = keyReader.readLine(); socketOut.write(line+"\r\n"); socketOut.flush(); } serverSocket.close(); } }
客户端:
package test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.InetAddress; import java.net.Socket; //聊天的客户端 public class Client { public static void main(String[] args) throws IOException { //建立tcp的客户端服务 Socket socket = new Socket(InetAddress.getLocalHost(),9090); //获取socket的输出流对象。 OutputStreamWriter socketOut = new OutputStreamWriter(socket.getOutputStream()); //获取socket的输入流对象 BufferedReader socketReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //获取键盘的输入流对象,读取数据 BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in)); String line = null; //不断的读取键盘录入的数据,然后把数据写出 System.out.print("客户端:"); while((line = keyReader.readLine())!=null){ socketOut.write(line+"\r\n"); socketOut.flush(); //读取服务端回送的数据 line = socketReader.readLine(); System.out.println("服务端:"+line); } socket.close(); } }
五、实验结果
六、Linux与网络编程相关的函数
int socket(int domain, int type,int protocol) domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX和AF_INET等)。 AF_UNIX只能够用于单一的Unix系统进程间通信,而AF_INET是针对Internet的,因而可以允许在远程
主机之间通信(当我们 man socket时发现 domain可选项是 PF_*而不是AF_*,因为glibc是posix的实现 所以用PF代替了AF,不过我们都可以使用的)。 type:我们网络程序所采用的通讯协议(SOCK_STREAM, SOCK_DGRAM等) SOCK_STREAM表明我们用的是TCP协议,这样会提供按顺序的,可靠,双向,面向连接的比特流。
SOCK_DGRAM 表明我们用的是UDP协议,这样只会提供定长的,不可靠,无连接的通信。 protocol:由于我们指定了type,所以这个地方我们一般只要用0来代替就可以了 socket为网络通讯做基本的准备。成功时返回文件描述符,失败时返回-1,看errno可知道出错的详细情。
int bind(int sockfd, struct sockaddr *my_addr, int addrlen) sockfd:是由socket调用返回的文件描述符。
addrlen:是sockaddr结构的长度。my_addr:是一个指向sockaddr的指针。 在其中有 sockaddr的定义: struct sockaddr{ unisgned short as_family; char sa_data[14]; };
int listen(int sockfd,int backlog) sockfd:是bind后的文件描述符。 backlog:设置请求排队的最大长度。当有多个客户端程序和服务端相连时,使用这个表示可以介绍的排队长度。listen函数将bind的文件描述符变为监听套接字。返回的情况和bind一样。
int accept(int sockfd, struct sockaddr *addr,int *addrlen) sockfd:是listen后的文件描述符。 addr, addrlen是用来给客户端的程序填写的,服务器端只要传递指针就可以了。 bind,listen和accept是服务器端用的函数,accept调用时,服务器端的程序会一直阻塞到有一个
客户程序发出了连接。
accept成功时返回最后的服务器端的文件描述符,这个时候服务器端可以向该描述符写信息了。失败时返回-1。
int connect(int sockfd, struct sockaddr * serv_addr,int addrlen) sockfd:socket返回的文件描述符。 serv_addr:储存了服务器端的连接信息。其中sin_add是服务端的地址 addrlen:serv_addr的长度 connect函数是客户端用来同服务端连接的。成功时返回0,sockfd是同服务端通讯的文件描述符 失败时返回-1。