Created
May 25, 2013 13:47
-
-
Save mike-neck/5649095 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.mikeneck.multithreads; | |
import java.net.InetAddress; | |
import java.net.InetSocketAddress; | |
import java.util.ArrayList; | |
import java.util.Iterator; | |
import java.util.List; | |
/** | |
* @author mike | |
*/ | |
public class AddressUtility { | |
private final InetAddress address; | |
public AddressUtility(InetSocketAddress socketAddress) { | |
this(socketAddress.getAddress()); | |
} | |
public AddressUtility(InetAddress address) { | |
this.address = address; | |
} | |
public void connectionLog() { | |
System.out.println("connection from [" + asString() + "]."); | |
} | |
public void messageLog (String message) { | |
System.out.println("message from [" + asString() + "] : " + message); | |
} | |
public String asString () { | |
StringBuilder builder = new StringBuilder(); | |
for (Iterator<Integer> iterator = asIntegerList().iterator(); | |
iterator.hasNext();) { | |
int value = iterator.next(); | |
builder.append(value < 0 ? 256 + value : value); | |
if (iterator.hasNext()) | |
builder.append('.'); | |
} | |
return builder.toString(); | |
} | |
private List<Integer> asIntegerList () { | |
byte[] bytes = address.getAddress(); | |
List<Integer> integers = new ArrayList<>(bytes.length); | |
for (byte b : bytes) { | |
integers.add((int) b); | |
} | |
return integers; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.mikeneck.multithreads; | |
import java.io.BufferedReader; | |
import java.io.DataOutputStream; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.net.Socket; | |
/** | |
* @author mike | |
*/ | |
public class SimpleClient implements AutoCloseable { | |
private final Socket socket; | |
private DataOutputStream output; | |
private BufferedReader input; | |
public SimpleClient(String hostname, int port) throws IOException { | |
socket = new Socket(hostname, port); | |
} | |
public SimpleClient open () throws IOException { | |
output = new DataOutputStream(socket.getOutputStream()); | |
input = new BufferedReader(new InputStreamReader(socket.getInputStream())); | |
return this; | |
} | |
public String sendMessage(String message) throws IOException { | |
output.writeBytes(message + '\n'); | |
String line; | |
if ((line = input.readLine()) != null) { | |
return line; | |
} else { | |
return ""; | |
} | |
} | |
public void bye () throws IOException { | |
output.writeBytes("BYE\n"); | |
} | |
@Override | |
public void close() throws Exception { | |
if (!socket.isClosed()) { | |
socket.close(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.mikeneck.multithreads; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.io.PrintStream; | |
import java.net.InetSocketAddress; | |
import java.net.ServerSocket; | |
import java.net.Socket; | |
/** | |
* @author mike | |
*/ | |
public class SimpleServer implements Runnable { | |
private final ServerSocket serverSocket; | |
private boolean quietMode = false; | |
public SimpleServer(int port) throws IOException { | |
serverSocket = new ServerSocket(port); | |
} | |
public SimpleServer withQuietMode () { | |
this.quietMode = true; | |
return this; | |
} | |
@Override | |
public void run() { | |
while(true) { | |
try (Socket socket = serverSocket.accept(); | |
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); | |
PrintStream writer = new PrintStream(socket.getOutputStream()) | |
) { | |
if (!quietMode) | |
new AddressUtility((InetSocketAddress) socket.getRemoteSocketAddress()) | |
.connectionLog(); | |
String line = reader.readLine(); | |
if (!quietMode) | |
new AddressUtility((InetSocketAddress) socket.getRemoteSocketAddress()) | |
.messageLog(line); | |
writer.println(line); | |
if (line.equals("BYE")) { | |
break; | |
} | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.mikeneck.multithreads; | |
import org.junit.*; | |
import org.junit.experimental.runners.Enclosed; | |
import org.junit.rules.TestName; | |
import org.junit.runner.RunWith; | |
import java.io.IOException; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.concurrent.*; | |
import static org.hamcrest.Matchers.is; | |
import static org.junit.Assert.assertThat; | |
/** | |
* @author mike | |
*/ | |
@RunWith(Enclosed.class) | |
public class SimpleSocketTest { | |
public static class SingleClient { | |
private static final ExecutorService SERVICE = Executors.newFixedThreadPool(1); | |
private static final int PORT = 12521; | |
private static final String LOCALHOST = "localhost"; | |
private SimpleClient client; | |
@Rule | |
public TestName testName = new TestName(); | |
@BeforeClass | |
public static void start () throws IOException { | |
SERVICE.execute(new SimpleServer(PORT)); | |
} | |
@Before | |
public void setup () throws IOException { | |
client = new SimpleClient(LOCALHOST, PORT); | |
} | |
@After | |
public void tearDown () throws Exception { | |
System.out.println(testName.getMethodName() + " is closing"); | |
client.close(); | |
} | |
@AfterClass | |
public static void end () throws IOException { | |
new SimpleClient(LOCALHOST, PORT).open().bye(); | |
} | |
@Test | |
public void socketProcessing () throws IOException { | |
client.open(); | |
String message = client.sendMessage("Hello"); | |
System.out.println("Message from Server [" + message + "]"); | |
assertThat(message, is("Hello")); | |
System.out.println("Assertion ends."); | |
} | |
} | |
public static class SendManyMessages { | |
private static final ExecutorService SERVICE = Executors.newFixedThreadPool(1); | |
private static final int PORT = 14541; | |
private static final String LOCALHOST = "localhost"; | |
@Rule | |
public TestName testName = new TestName(); | |
@BeforeClass | |
public static void start () throws IOException { | |
SERVICE.execute(new SimpleServer(PORT)); | |
} | |
@AfterClass | |
public static void end () throws IOException { | |
new SimpleClient(LOCALHOST, PORT).open().bye(); | |
} | |
@ThisTestWillFail | |
@Test | |
public void send2times () throws IOException { | |
SimpleClient client = new SimpleClient(LOCALHOST, PORT).open(); | |
assertThat(client.sendMessage("hello"), is("hello")); | |
assertThat(client.sendMessage("good-bye"), is("good-bye")); | |
} | |
} | |
public static class ManyThreadClient { | |
private static final ExecutorService SERVICE = Executors.newFixedThreadPool(1); | |
private static final int PORT = 11511; | |
private static final String LOCALHOST = "localhost"; | |
@Rule | |
public TestName testName = new TestName(); | |
@BeforeClass | |
public static void start () throws IOException { | |
SERVICE.execute(new SimpleServer(PORT).withQuietMode()); | |
} | |
@AfterClass | |
public static void end () throws IOException { | |
new SimpleClient(LOCALHOST, PORT).open().bye(); | |
} | |
@Test | |
public void testTwoClient () throws IOException, ExecutionException, InterruptedException { | |
ExecutorService service = Executors.newFixedThreadPool(2); | |
List<ExpectsAndResult> list = Arrays.asList( | |
new ExpectsAndResult("1st", LOCALHOST, PORT), | |
new ExpectsAndResult("2nd", LOCALHOST, PORT) | |
); | |
for (ExpectsAndResult item : list) { | |
item.setFuture(service.submit(item.createClient())); | |
} | |
for (ExpectsAndResult item : list) { | |
assertThat(item.real().endsWith(item.expect()), is(true)); | |
} | |
} | |
@Test | |
public void testManyAccessWith4Pool () throws IOException, ExecutionException, InterruptedException { | |
int size = 600; | |
ExecutorService service = Executors.newFixedThreadPool(4); | |
List<ExpectsAndResult> list = new ArrayList<>(size); | |
for (int i = 0; i < size; i++) { | |
list.add(new ExpectsAndResult("message-" + i, LOCALHOST, PORT)); | |
} | |
for (ExpectsAndResult item : list) { | |
item.setFuture(service.submit(item.createClient())); | |
} | |
for (ExpectsAndResult item : list) { | |
assertThat(item.real().endsWith(item.expect()), is(true)); | |
} | |
} | |
} | |
static class ExpectsAndResult { | |
final String message; | |
final String hostname; | |
final int port; | |
Future<String> future; | |
ExpectsAndResult(String message, String hostname, int port) { | |
this.message = message; | |
this.hostname = hostname; | |
this.port = port; | |
} | |
String expect() { | |
return message; | |
} | |
Client createClient () throws IOException { | |
return new Client(message, hostname, port); | |
} | |
void setFuture(Future<String> future) { | |
this.future = future; | |
} | |
String real () throws ExecutionException, InterruptedException { | |
return future.get(); | |
} | |
} | |
static class Client implements Callable<String> { | |
private final String message; | |
private final SimpleClient client; | |
Client(String message, String hostname, int port) throws IOException { | |
this.message = message; | |
client = new SimpleClient(hostname, port); | |
} | |
@Override | |
public String call() throws Exception { | |
client.open(); | |
String name = Thread.currentThread().getName(); | |
return client.sendMessage("Thread [" + name + "] - " + message); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment