Socket,又称为套接字,Socket是计算机网络通信的基本的技术之一。
BlockingIO编程,同步阻塞的编程方式。
BIO编程方式通常是JDK1.4之前的编程方式。首先在服务端启动一个ServerSocket 来监听网络请求,客户端启动Socket发起网络请求,默认情况下ServerSocket 会建立一个线程来处理此请求,如果服务端没有线程可以使用,客户端会阻塞或者遭到拒绝。
建立完成的连接,在通讯过程中,是同步的。并发处理上效率比较低。
首先我们的服务器先启动,客户端向服务端发起请求,服务器收到请求会创建一个socket套接字,来处理该请求,创建完socket后,客户端和服务端的socket就可以进行通信。如果有其他的客户端请求,服务器再创建一个socket线程和客户端通信,让每一个线程的服务端和客户端一一对应,操作完毕后,有一方断开,该线程结束。
接下来,我们用一个实例来说明单线程的客户端和服务端如何通信。
服务端的代码:
package networkprogram.bio;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args){
int port = getPort(args);
ServerSocket server = null;
try {
server = new ServerSocket(port);
System.out.println("server started!");
while (true){
Socket socket = server.accept();
new Thread(new Handler(socket)).start();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(server != null){
try {
server.close();
}catch (IOException e){
e.printStackTrace();
}
}
server = null;
}
}
static class Handler implements Runnable{
Socket socket = null;
public Handler(Socket socket){
this.socket = socket;
}
@Override
public void run() {
BufferedReader reader = null;
PrintWriter writer = null;
try {
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
writer = new PrintWriter( new OutputStreamWriter(socket.getOutputStream(),"UTF-8"));
String readMessage = null;
while (true){
System.out.println("server start...");
if((readMessage = reader.readLine()) == null){
break;
}
System.out.println(readMessage);
writer.println("server recive : " + readMessage);
writer.flush();
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(socket != null){
try {
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
socket = null;
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
reader = null;
if(writer != null){
writer.close();
}
writer = null;
}
}
}
private static int getPort(String[] args){
if(args.length > 0){
try {
return Integer.parseInt(args[0]);
}catch (NumberFormatException e){
return 9999;
}
}else {
return 9999;
}
}
}
客户端的代码:
package networkprogram.bio;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args){
String host = null;
int port = 0;
if(args.length > 2){
host = args[0];
port = Integer.parseInt(args[1]);
}else {
host = "127.0.0.1";
port = 9999;
}
Socket socket = null;
BufferedReader reader = null;
PrintWriter writer = null;
Scanner s = new Scanner(System.in);
try {
socket = new Socket(host,port);
String message = null;
reader = new BufferedReader( new InputStreamReader(socket.getInputStream(), "UTF-8"));
writer = new PrintWriter(socket.getOutputStream(),true);
while (true){
message = s.nextLine();
if(message.equals("exit")){
break;
}
writer.println(message);
writer.flush();
System.out.println(reader.readLine());
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(socket != null){
try {
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
socket = null;
if(reader != null){
try {
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}
reader = null;
if(writer != null){
writer.close();
}
writer = null;
}
}
}
启动客户端,和服务端代码后就可以进行通信了。
客户端向服务器端发送连接请求后,就被动地等待服务器的响应。典型的TCP客户端要经过下面三步操作:
1.创建一个Socket实例:构造函数向指定的远程主机和端口建立一个TCP连接;
2.通过套接字的I/O流与服务端通信;
3.使用Socket类的close方法关闭连接。
服务端的工作是建立一个通信终端,并被动地等待客户端的连接。典型的TCP服务端执行如下两步操作:
1、创建一个ServerSocket实例并指定本地端口,用来监听客户端在该端口发送的TCP连接请求;
2、重复执行:
1)调用ServerSocket的accept()方法以获取客户端连接,并通过其返回值创建一个Socket实例;
2)为返回的Socket实例开启新的线程,并使用返回的Socket实例的I/O流与客户端通信;
3)通信完成后,使用Socket类的close()方法关闭该客户端的套接字连接。