套接字

1. 套接字

当两个程序需要通信时,它们可以通过使用Socket类建立套接字对象并连接在一起(端口号与IP地址的组合得出一个网络套接字),本节将讲解怎样将客户端和服务器端的套接字对象连接在一起来交互信息。

2. 客户端套接字

客户端的程序使用 Socket类建立负责连接到服务器的套接字对象。

1
2
3
4
5
try {
Socket clientSocket = newSocket("http://192.168.0.78",2010);//[c1]
InputStream inClient = clientSocket.getInputStream(); //[c2]
OutputStream outClient = clientSocket.getOutputStream(); //[c3]
} catch(IOException e) {}

3. ServerSocket 对象与服务器端套接字

1
2
3
4
5
6
7
8
try {
ServerSocket serverForClient = new ServerSocket(); //[s1]
} catch(Exception e) {}
try {
Socket sc = serverForClient.accept(); //[s2]
OutputStream outServer = sc.getOutputStream(); //[s3]
InputStream inServer = sc.getInputStream(); //[s4]
} catch(IOException e) {}

4. 例子

通过一个简单的例子说明上面讲的套接字连接。在例子中,客户端向服务器问了三句话,服务器都一一给出了回答。首先将例子3中服务器端的Server. java编译通过,并运行起来,等待客户的呼叫,然后运行客户端程序

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;

public class Client {
public static void main(String[] args) {
String [] mess = {"2014世界杯在哪举行?", "巴西进入世界杯了吗?", "中国进入世界杯了吗?"};
Socket mysocket;
DataInputStream in = null;
DataOutputStream out = null;
try {
mysocket = new Socket("127.0.0.1", 2010);
in = new DataInputStream(mysocket.getInputStream());
out = new DataOutputStream(mysocket.getOutputStream());
for (int i = 0; i < mess.length; i++) {
out.writeUTF(mess[i]);
// in读取信息, 状态堵塞
String s = in.readUTF();
System.out.println("客户收到服务器的回答:" + s);
Thread.sleep(2000);
}
} catch (Exception e) {
System.out.println("服务器已断开" + e);
}
}
}

Server.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
public static void main(String[] args) {
String[] answer = {"巴西", "进入世界杯了", "哈哈...问题真逗!"};
ServerSocket serverForClient = null;
Socket socketOnServer = null;
DataOutputStream out = null;
DataInputStream in = null;
try {
serverForClient = new ServerSocket(2010);

} catch (IOException e1) {
System.out.println(e1);
}
try {
System.out.println("等待客户呼叫");
//堵塞状态,除非有客户呼叫
socketOnServer = serverForClient.accept();
out = new DataOutputStream(socketOnServer.getOutputStream());
in = new DataInputStream(socketOnServer.getInputStream());
for (int i = 0; i < answer.length; i++) {
// in读取信息,堵塞状态
String s = in.readUTF();
System.out.println("服务器收到客户的提问:" + s);
out.writeUTF(answer[i]);
Thread.sleep(500);
}
}
catch(Exception e) {
System.out.println("客户已断开"+e);
}
}
}

5. 使用多线程技术

  • 服务器端收到一个客户的套接字后,就应该启动一个专门为该客户服务的线程.
  • 使用套接字连接时,可能在另一端数据发送出来之前,就已经开始试着读取了,这时,就会堵塞本线程,直到该读取方法成功读取到信息,本线程才继续执行后续的操作.

例子中,

  • 客户输入圆的半径并发送给服务器。
  • 服务器把计算出的圆的面积返回给客户。
  • 因此可以将计算量大的工作放在服务器端,客户负责计算量小的工作,实现客户-服务器交互计算,来完成某项任务。
  • 首先将例子中服务器端的程序编译通过,并运行起来,等待客户的呼叫。

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.io.*;
import java.net.*;
import java.util.*;
public class Client {
public static void main(String args[]) {
Scanner scanner = new Scanner(System.in);
Socket mysocket=null;
DataInputStream in=null;
DataOutputStream out=null;
Thread readData ;
Read read=null;
try{ mysocket=new Socket();
read = new Read();
readData = new Thread(read);
System.out.print("输入服务器的IP:");
String IP = scanner.nextLine();
System.out.print("输入端口号:");
int port = scanner.nextInt();
if(mysocket.isConnected()){}
else{
InetAddress address=InetAddress.getByName(IP);
InetSocketAddress socketAddress=new InetSocketAddress(address,port);
mysocket.connect(socketAddress);
in =new DataInputStream(mysocket.getInputStream());
out = new DataOutputStream(mysocket.getOutputStream());
read.setDataInputStream(in);
readData.start();
}
}
catch(Exception e) {
System.out.println("服务器已断开"+e);
}
System.out.print("输入园的半径(放弃请输入N):");
while(scanner.hasNext()) {
double radius=0;
try {
radius = scanner.nextDouble();
}
catch(InputMismatchException exp){
System.exit(0);
}
try {
out.writeDouble(radius);
}
catch(Exception e) {}
}
}
}

Server.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import java.io.*;
import java.net.*;
import java.util.*;
public class Server {
public static void main(String args[]) {
ServerSocket server=null;
ServerThread thread;
Socket you=null;
while(true) {
try{ server=new ServerSocket(2010);
}
catch(IOException e1) {
System.out.println("正在监听"); //ServerSocket对象不能重复创建
}
try{ System.out.println(" 等待客户呼叫");
you=server.accept();
System.out.println("客户的地址:"+you.getInetAddress());
}
catch (IOException e) {
System.out.println("正在等待客户");
}
if(you!=null) {
new ServerThread(you).start(); //为每个客户启动一个专门的线程
}
}
}
}
class ServerThread extends Thread {
Socket socket;
DataOutputStream out=null;
DataInputStream in=null;
String s=null;
ServerThread(Socket t) {
socket=t;
try { out=new DataOutputStream(socket.getOutputStream());
in=new DataInputStream(socket.getInputStream());
}
catch (IOException e){}
}
public void run() {
while(true) {
try{ double r=in.readDouble();//堵塞状态,除非读取到信息
double area=Math.PI*r*r;
out.writeDouble(area);
}
catch (IOException e) {
System.out.println("客户离开");
return;
}
}
}
}

Read.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.io.*;
public class Read implements Runnable {
DataInputStream in;
public void setDataInputStream(DataInputStream in) {
this.in = in;
}
public void run() {
double result=0;
while(true) {
try{ result=in.readDouble();
System.out.println("圆的面积:"+result);
System.out.print("输入圆的半径(放弃请输入N):");
}
catch(IOException e) {
System.out.println("与服务器已断开"+e);
break;
}
}
}
}