关于socket与线程
1、如果你有一个服务端A,端口是8888
2、如果你有5个不同的电脑,同时来访问服务端A,访问端口是8888,ip是本机ip
你会发现你在服务端接收到的每个socket对象都是不同的,因为每个主机的ip地址不同,即使是你用同一台电脑开5个同样的Java程序,去请求服务端A的8888端口,你会发现socket仍然是不同的,因为每个Java程序在访问时提供的自己的端口是不同的,不可能一个端口供2个Java程序使用。
我们为什么要把socket与线程连在一起?
在显示生活中,一个服务端,它肯定是一直开着的,不论何时他都在不断的监听有无客户端访问,因此一般来说服务端A的aceept.serversocket我们是放在while循环中的,目的就是为了让它模拟现实状态,但我们监听后,获得的这些socket对象做什么呢?我们是用来读取数据的,那为什么不直接在while中写它的具体操作呢,何必在与线程绑在一起呢?答案是显而易见的,如果我们在while中去实现每个socket要做的事情,你会遇到一个问题,当一个客户端B1在访问服务端A的时候,如果它连接上去后什么都不做,那么当客户端B2再访问服务端A的时候,就会出现一种类似死锁的状态,即B1占用资源却什么也不做,服务端A无可奈何。
**线程就很好的解决了这一现象,因为线程是可以并发的,我们的服务端A在接受到客户端B1的请求时,生成的socket对象我们可以放在继承了Thread的类的对象中,注意这个socket是服务器的,它的作用是与客户端的socekt连接,当前前提是你要在该类声明一个socket的变量,以及一个构造器。我们就创建了一个线程,就可以让每个客户端在自己的线程内做事,而不必一直占用与服务端连接的那一条线程,服务端的线程仅用来接受每个客户端的socket对象,并把他们放在每个线程中,这个过程是很迅速的。当然,不要忘了要start启动线程,否则就仅仅只是一个socket绑定一个线程,啥事也不做。**在服务器的java程序里,如果你想一直读取客户端发来的数据,就需要在此基础上在run方法中,使用socket的input或者output.
客户端同样是这样,服务端就是一个特殊的客户端。如果你客户端run方法是while语句,就可以做到让socket一直读取服务端的信息和一直接收服务端的信息,说白了就是可以让客户端B1一直与服务端A通信。只要你的B1客户端一直在就能保持一直通信,为啥呢,因为在最开始的时候你已经与服务端建立连接了,服务端那边收到了你的请求,根据你的ip/端口创建了一个socket对象,只要这个对象在,你俩就一直是在连接着的。
客户端B1的socket如果编号是1的话,服务端其中一个socket的编号就是1,它俩一一对应
客户端B2的socket如果编号是2的话,服务端其中一个socket的编号就是2,它俩一一对应
客户端:Socket[addr=TMJIE5200/192.168.129.1,port=7777,localport=62754]
服务端:sockte名为:Socket[addr=/192.168.129.1,port=62754,localport=7777]正在执行!!
可见两者的socket是一一对应的 都是id 62754
run方法,如果是while,只要启动了它就是一直执行的,因为是线程它会与main线程同时执行,所以run方法的作用就是在后台不断的读、写数据。只不过没有输出语句你看不到罢了。
注意:传入的socket不同,因此必须每传入一个就启动一下。因此一般 构建对象,与启动线程是放在while中的
while (true) {
//每个程序生成的client是不同的,即使是同一台电脑会由于不同的端口号而不同
//因为一个端口号被占用后就只能用另外一个了
//你每一个要访问服务端的,都会获得一个不同的进程,因为client不同,而我的构造器需要的恰好是client
//因此每个client的线程是不同的
//因此你可以实现每一个线程绑定一个client对象
/*
Socket[addr=TMJIE5200/192.168.129.1,port=7777,localport=61873]
0
2
Socket[addr=TMJIE5200/192.168.129.1,port=7777,localport=61882]
0
3
Socket[addr=TMJIE5200/192.168.129.1,port=7777,localport=61891]
0
4
Socket[addr=TMJIE5200/192.168.129.1,port=7777,localport=61896]
0
5
Socket[addr=TMJIE5200/192.168.129.1,port=7777,localport=61902]
0
* */
Server001是继承了Thread的类,因此可以直接调用start方法
Socket client = serverSocket.accept();
//-------------程序会一直client卡在那里,没有while执行一次就不接受了---------
Server001 server001 = new Server001(client);
//接受的对象不同,每次都要启动一下,放在后台
server001.start();
System.out.println(i++);
}
但正常来说客户端就就不需要,因为客户端就只有你一个人在使用,你启动一次就行了
服务端是每来一个就启动一个线程放在后台
每个客户端都可以做自己的事情,而不必导致一个占用资源, 其他人只能干等着。
下面是我的继承了Thread类的内容
private Socket client;
public Server001(Socket i) {
client=i;
}
@Override
public void run() {
int i=0;
try {
while (true) {//true 保证一直在后台运行
InputStream inputStream = client.getInputStream();
//下面这是一个转接流 字节转字符
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String s = bufferedReader.readLine();
System.out.println(s);
client.shutdownInput();
System.out.println(i);
client.close();
return;
//System.out.println("哇哇哇哇" + Server001.currentThread());
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
3、 管理线程,如果客户端B1向客户端B2发送信息,它就必须经过服务端,服务端很容易知道是谁发的,但是怎么才能让服务端找到与客户端B2连接的线程呢,因为线程找到了与B2连接的socket就找到了,就可以写数据了,一般是创建一个hashmap来存储。通过传来的id获取对应的线程。
4、关于流
output流是从程序读到程序
input是从程序读到某个地方
就是说它并不是直接写到你的文件夹中或者客户端中,而是写到程序中,你调用input在从程序中读到某个文件夹
在网络编程中
服务端发了一条消息给客户端,客户端是没法直接显示的,因为这条消息此时在程序中,你想要读就要使用inputstream从程序中读,写是同样的,从服务端写,写到客户端的程序中。