0%

7.03网络编程

2017年7月3日 上午8:00

TCP:传输控制协议
UDP:数据报文协议

端口 :16位

三类

  1. 公认端口:0~1023
  2. 注册端口:1024-49151
  3. 动态和、私有端口:49151~65535

常用端口:

Telent:23
Stmp:25
Ftp:21
Http:80

常用对象:

ServerSocket

  1. ServerSocket(port):创想serverSocket实例
  2. accept()等待客户端连接
  3. Public InetAddress getInetAddress()返回服务器的Ip地址
  4. Public boolean isCloseed();赶回ServerSocket的关闭状态
  5. Public void close();关闭ServerSockets

Socket

  1. Socket(String host,int port):构造socket对象,同时,指定要连接服务器的主机名和端口号
  2. public inputStream getInputStream:返回套接字的输入流
  3. pubilc OutputStream .getOutputStream()返回套接字的输入流
  4. Public boolean isClosed()判断套接字是否关闭
  5. Public void close():关闭socket

InetAddress

  1. public static InetAddress(String host):通过主机名或IP地址得到一个InetAddress对象
  2. String getHostName();获取ip地址对应的主机名
  3. String getHostAddress();返回ip地址字符串

注:他们处理的都是字节流

使用PrintWriter的例程

网络编程的服务端步骤比较单一:
1. ServerSocket对象
2. 创建Socket对象
3. inputStream对象
4. BufferedReader对象
5. outputStream对象
6. printWriter对象
7. 读
8. 写
注:他们都是一条流水线,前一个对象产生下一个对象
例程:

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
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server1 {

public static void main(String[] args) throws IOException {

//创建服务端实例对象,需要指定端口

ServerSocket ss = new ServerSocket(7799);

//接受客户端连接,创建socket对象

Socket socket= ss.accept();

//获取输入流,读取客户端传过来的数据
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));

//输出流 ,向客户端返回数据
PrintWriter pw= new PrintWriter(socket.getOutputStream());

//服务端读
String content = br.readLine();
if(content != null){
System.out.println("获取到的数据是:"+content);
}

//服务端写
pw.write("服务端收到数据");

//倒序关闭
pw.close();
br.close();
is.close();
socket.close();
ss.close();
}

}

网络编程的客户端步骤比较单一:
1. 创建Socket对象
2. outputStream对象
3. printWriter对象
4. inputStream对象
5. BufferedReader对象
6. 写
7. 读
例程:

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
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {

public static void main(String[] args) throws UnknownHostException, IOException {
//创建socket对象,连接服务端
Socket socket = new Socket("127.0.0.1",7799);

//写对象
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);

//读对象
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));

//客户端写
pw.write("我要访问服务端");
pw.flush();
socket.shutdownOutput();//确保数据传输到服务端


//客户端读
String result = br.readLine();
if(result != null){
System.out.println("获取到的数据是:"+result);
}
br.close();
socket.close();

}

}

注:

  1. 他们都是一条流水线,前一个对象产生下一个对象
  2. 客户端和服务的不能同时创建输入流
  3. 这里socket.shutdownOutput的作用
    1. 【TCP】s.shutdownOutput();这行代码的牛逼之处 - 闲着没事_玩玩JAVA的专栏 - 博客频道 - CSDN.NET
    2. 第一种方法close流,相当于给流中加入一个结束标记-1
    3. 第二种方法:s.shutdownOutput();关闭客户端的输出流。相当于给流中加入一个结束标记-1.
    4. 就像字符串的结尾一样
    5. 服务端没写socket.shutdownOutput,用pw.close代替了

错误:没有打开服务端就开启客户端

扩展:
以上代码使用
DateOutputStream 和 DateInputStream也可以

使用对象流的例程:

客户端:

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
package package1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {

public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1",7799);

//pet对象
Pet pet = new Pet("狗",1);
//写对象
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
//读对象
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//写(对象流不用shutdown,不知道为啥)
oos.writeObject(pet);
oos.flush();

//读
String string = br.readLine();
if(string != null){
System.out.println("接收到的信息是:"+string);
}

br.close();
is.close();
oos.close();
os.close();
socket.close();


}

}

服务端:

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
package package1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {

public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1",7799);

//pet对象
Pet pet = new Pet("狗",1);
//写对象
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
//读对象
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//写(对象流不用shutdown,不知道为啥)
oos.writeObject(pet);
oos.flush();

//读
String string = br.readLine();
if(string != null){
System.out.println("接收到的信息是:"+string);
}

br.close();
is.close();
oos.close();
os.close();
socket.close();
}
}

Pet类:

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
package package1;

import java.io.Serializable;

public class Pet implements Serializable{
private String name;
private int id;

public Pet(String name, int id) {
super();
this.name = name;
this.id = id;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}

错误:Pet类没有序列号接口: implements Serializable

服务器常开例程(死循环方式)

客户端

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
package package2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {

public static void main(String[] args) throws UnknownHostException, IOException {
// Socket socket = new Socket("192.168.12.35",8800);
Socket socket = new Socket("127.0.0.1",7799);

//pet对象
Pet pet = new Pet("狗","狗","男");
//写对象
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
//读对象
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//写(对象流不用shutdown,不知道为啥)
oos.writeObject(pet);
oos.flush();

//读
String string = br.readLine();
if(string != null){
System.out.println("接收到的信息是:"+string);
}

br.close();
is.close();
oos.close();
os.close();
socket.close();


}
}

pet类

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
package package2;

import java.io.Serializable;

public class Pet implements Serializable{
/*
*
*/
private static final long serialVersionUID = 1L;
private String name;
private String type;
private String sex;


public Pet(String name, String type, String sex) {
super();
this.name = name;
this.type = type;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}


}

服务端:

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
53
54
55
56
package package2;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

public static void main(String[] args) throws IOException, ClassNotFoundException {


while(true){
ServerSocket ss = new ServerSocket(7799);
Socket socket = ss.accept();

//输入流
InputStream is = socket.getInputStream();
ObjectInputStream br = new ObjectInputStream(is);

//输出流
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);

//输入
Pet pet = (Pet) br.readObject();

if(pet != null){
System.out.println("name : "+pet.getName()+"id :"+pet.getSex());
//文件写入
FileWriter fw = new FileWriter("hello_pet.txt");
BufferedWriter bw = new BufferedWriter(fw);
bw.write("name : "+pet.getName()+"id :"+pet.getSex());
bw.flush();
bw.close();
}
//输出
pw.write("客户端已得到pet对象");


pw.close();
os.close();
br.close();
is.close();
socket.close();
ss.close();
}
}

}

注:
1. 通过换host可以实现局域网下访问,要有以下两个条件
1. 但是需要有相同的序列号这里使用的是IL
2. Pet所在的包要相同

服务器常开例程2(死循环方式+Thread)

服务端:

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
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/*
* 宠物店 服务器端
* */
public class PetServer {
public static void main(String[] args) {
try {
//1.建立一个服务器Socket(ServerSocket)绑定指定端口并开始监听
ServerSocket serverSocket=new ServerSocket(8800);
//2.使用accept()方法阻塞等待监听,获得新的连接
Socket socket=null;
//记录注册宠物的数量
int num=0;
//一直处于监听状态
while(true){
socket=serverSocket.accept();
ServerThread serverThread=new ServerThread(socket);
serverThread.start();
num++;
System.out.println("有"+num+"只宠物在本店注册!\n");

//获得客户(宠物主人)的IP信息
InetAddress ia=socket.getInetAddress();
//获得ip
String ip=ia.getHostAddress();
System.out.println("本宠物主人的IP地址为:"+ip);
//获得主机名
String name=ia.getHostName();
System.out.println("本宠物主人的主机名为:"+name);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

Thread类

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
53
54
55
56
57
58
59
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

/*
* 专门的线程类
* */
public class ServerThread extends Thread {
//和本线程相关的Socket
Socket socket=null;

public ServerThread(Socket socket){
this.socket=socket;
}

//线程启动:响应客户请求
public void run(){
try {
//获得输入流
InputStream is=socket.getInputStream();
//获得流:可以对对象进行反序列化
ObjectInputStream ois=new ObjectInputStream(is);
//获得输出流
OutputStream os=socket.getOutputStream();
PrintWriter pw=new PrintWriter(os);
BufferedWriter writer = new BufferedWriter(new FileWriter(new File("pet.txt"), true));
//4.读取宠物信息,将宠物信息保存到文件中去
Pet pet=(Pet)ois.readObject();
writer.write("----宠物注册信息----");
writer.write("姓名:"+pet.getName());
writer.write("品种:"+pet.getType());
writer.write("性别:"+pet.getGender());
writer.flush();

//给宠物主人一个响应
String reply="恭喜,您的宠物已经在本店注册成功!";
pw.write(reply);
pw.flush();
//关闭资源
pw.close();
os.close();
ois.close();
is.close();
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

比较两种服务器常开的:

1.     第一种的思路是:
    1. 服务器整体写一个循环,这个循环中包含全部的内容,ServerSocket对象和以及想的close()
    2. 这个种方法操作流程是:一个客户端访问完,这个循环走到accept()方法处,等待下一次客户端的访问(server就是一个苦力,真可怜个)。
2. 第二种的思路是:
    1. 首先是写一个循环,循环的作用是让服务器不停止运行,然后,写一个Thread类
    2. 这个类的入口是:accpet()方法产生的socket对象
    3. 执行的内容是:获取客户端内容,然后给一个回馈    
    4. Thread类写完之后,最后在while()循环中开启Thread的一个线程,并计数
    5. 这种方法执行的流程是:整个过程可以看成是一个生活场景。Server.class就像一个前台人员,她的主要任务有两个:一个是迎接来宾(client.class的访问),一个是命令他的小弟Thread的对象去完成当前用户的要求,然后她接着笑脸迎接下一个用户(boss就是boss,美女会笑就行,她其实也没干啥)

3. 区别:
    1. 在每次一个client访问模式下,这两种是没有区别的
    2. 但是,当多个client并行访问时,第一种是无法实现的,因为所有的任务都是他一个人来执行,干完一个才能干下一个。第二种是可以实现的,在这种情况下有一个团伙,美女boss管理者一堆小弟

两种客户端和服务端交互方式,其实都行,不过习惯使用第二种:

第一种:BufferedWriter

1
2
3
4
5
6
OutputStream  is2 = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(is2));

bw.write("name : "+pet.getName()+"id :"+pet.getSex());
bw.flush();
bw.close();

第二种:使用PrintWriter

1
2
3
4
5
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);

pw.write("客户端已得到pet对象");
pw.close();

注:细节中应该还是有区别的,没有仔细的测试!