服务端测试代码:
package com.my.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerSocketTest {
public static void main(String agrs[]) {
try{
ServerSocket socket=new ServerSocket(8088);
int i=1;
while(true){
Socket cSocket=socket.accept();
System.out.println("线程数:"+i);
System.out.println(cSocket.getPort());
new Thread(new ServerThread(cSocket)).start();
i++;
}
}catch(Exception e){
e.printStackTrace();
}
}
static class ServerThread implements Runnable {
private Socket socket;
public ServerThread(Socket socket){
this.socket=socket;
}
public void run() {
try{
InputStream input=socket.getInputStream();
BufferedReader reader=new BufferedReader(new InputStreamReader(input));
while(true){
String line=reader.readLine();
if(line!=null){
System.out.println(line);
}
if(line!=null && line.equals("quit")){
System.out.println(line);
socket.close();
break;
}
}
}catch(Exception e){
e.printStackTrace();
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
客户端测试代码:
package com.my.socket;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerSocketClient {
public static void main(String agrs[]) {
ByteArrayOutputStream output=null;
try{
int i=1;
while(true){
Socket socket=new Socket();
System.out.println("start new Thread");
new Thread(new ClientThread(socket,i)).start();
i++;
Thread.sleep(2000);
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(output!=null){
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
static class ClientThread implements Runnable{
private Socket cSocket;
private int i;
public ClientThread(Socket cSocket, int i) {
super();
this.cSocket = cSocket;
this.i = i;
}
@Override
public void run() {
try {
cSocket.connect(new InetSocketAddress(8088));
OutputStream out=cSocket.getOutputStream();
String con="thread"+i+"\r\n";
out.write(con.getBytes());
Thread.sleep(1000);
out.write("quit".getBytes());
out.close();
cSocket.close();
} catch (Exception e) {
try {
cSocket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
}
}
需要注意的几个地方:
1.客户端与服务端需要协商好相互通信的格式, 上面代码的格式(写入一句内容然后换行,结束用字符 'quit' 标识)比较简单,
2.服务端线程类ServerThread的run()方法如果没有break语句, 子线程永远不会结束,单个jvm的线程数会飙升,导致堆内存溢出,如下 jvisualvm图
3.测试代码是客户端主动关闭socket, 然后通知服务端关闭相应的socket,如果服务端不关闭socket, 服务端socket状态进入CLOSE_WATE, 客户端socket状态进入FIN_WAIT_2状态,如下图,服务端将可以继续向客户端的socket写数据,,客户端也能接受到数据.
只有客户端,服务端都关闭了socket, 服务端socket状态才会进入CLOSE,客户端进入 TIME_WAIT. 涉及到tcp状态转移部分知识,推荐看下<linux高性能网络编程>这边书
上面的socket通信是JAVA中用的较多的阻塞式通信(BIO),缺点是显而易见的,客户端的线程数与服务端线程数成正比. 客户端上万的并发量,服务端线程数也会上万,单个jvm肯定会挂的.