I\'ve done some research and it seems my issue is that the server accepts only o
ID: 3686446 • Letter: I
Question
I've done some research and it seems my issue is that the server accepts only one client during its lifetime and the test class starts the server only once for all test methods in the test class. So I need to set the accept() in a loop. But I'm not sure how to do that.
Here is my code so far:
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class GuessingServer {
public static void main(String[] args) {
try {
ServerSocket server;
server = new ServerSocket(5150);
System.out.println("awaiting client...");
Socket client = server.accept();
System.out.println("client connected");
Scanner in = new Scanner(client.getInputStream());
PrintWriter out = new PrintWriter(client.getOutputStream(), true);
String first = in.next();
int high = in.nextInt();
int low = Integer.parseInt(first);
boolean serverRun = true;
while (serverRun == true) {
if (!first.equals("SHUT DOWN")) {
System.out.println(low);
System.out.println(high);
int bNum = ((high - low) / 2) + low;
out.println(bNum);
System.out.println(bNum);
String temp = in.next();
if (temp.equals("high")) {
high = bNum;
System.out.println(temp + ": ");
System.out.println(high);
} else if (temp.equals("low")) {
low = bNum;
System.out.println(temp + ": ");
System.out.println(low);
} else if (temp.equals("won")) {
System.out.println(temp);
serverRun = false;
} else if (temp.equals("lost")) {
System.out.println(temp);
serverRun = false;
}
} else {
serverRun = false;
}
}
client.close();
in.close();
out.close();
server.close();
} catch (IOException e) {
}
}
}
And here is the test bench:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.Socket;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Scanner;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class GuessingServerTest {
@Test
public void testReflection() {
Class<?> iClass = GuessingServer.class;
Field[] iFields = iClass.getDeclaredFields();
for (Field f : iFields) {
if (!f.isSynthetic()) {
assertTrue ( "Field ""+f.getName()+"" should be private", Modifier.isPrivate( f.getModifiers() ));
assertFalse( "Field ""+f.getName()+"" can't be static", Modifier.isStatic ( f.getModifiers() ));
}
}
}
@BeforeClass
public static void startServer() {
// run server
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
GuessingServer.main( new String[]{ } );
}
});
thread.start();
}
@Before
public void waitTwoSecondsBetweenTests() {
try {
Thread.sleep( 2000 );
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test(timeout=3000)
public void testA_WinningAfter1Try() {
try {
// run client
Socket socket = new Socket( "localhost", 5150 );
Scanner scanner = new Scanner ( socket.getInputStream() );
PrintWriter writer = new PrintWriter( socket.getOutputStream(), true );
writer.println( "101 401" );
// try 1
int actual = scanner.nextInt();
assertEquals( "Incorrect result", 251, actual );
writer.println( "won" );
socket .close();
scanner.close();
}
catch (IOException e) {
e.printStackTrace();
fail( "Error opening client socket" );
}
catch (NoSuchElementException e) {
e.printStackTrace();
fail( "The server didn't return a value" );
}
}
@Test(timeout=3000)
public void testB_WinningAfter2Tries() {
try {
// run client
Socket socket = new Socket( "localhost", 5150 );
Scanner scanner = new Scanner ( socket.getInputStream() );
PrintWriter writer = new PrintWriter( socket.getOutputStream(), true );
writer.println( "37 337" );
// try 1
int actual = scanner.nextInt();
assertEquals( "Incorrect result", 187, actual );
writer.println( "low" );
// try 2
actual = scanner.nextInt();
assertEquals( "Incorrect result", 262, actual );
writer.println( "high" );
// try 3
actual = scanner.nextInt();
assertEquals( "Incorrect result", 224, actual );
writer.println( "high" );
// try 4
actual = scanner.nextInt();
assertEquals( "Incorrect result", 205, actual );
writer.println( "low" );
// try 5
actual = scanner.nextInt();
assertEquals( "Incorrect result", 214, actual );
writer.println( "high" );
// try 6
actual = scanner.nextInt();
assertEquals( "Incorrect result", 209, actual );
writer.println( "high" );
// try 7
actual = scanner.nextInt();
assertEquals( "Incorrect result", 207, actual );
writer.println( "high" );
// try 8
actual = scanner.nextInt();
assertEquals( "Incorrect result", 206, actual );
writer.println( "won" );
socket .close();
scanner.close();
}
catch (IOException e) {
e.printStackTrace();
fail( "Error opening client socket" );
}
catch (NoSuchElementException e) {
e.printStackTrace();
fail( "The server didn't return a value" );
}
}
@Test(timeout=3000)
public void testC_PlayingARandomGame() throws IOException {
final int UPPER = 200;
final int RANGE = 300;
final int TRIES = 9;
final Random random = new Random();
try {
// run client
Socket socket = new Socket( "localhost", 5150 );
Scanner scanner = new Scanner ( socket.getInputStream() );
PrintWriter writer = new PrintWriter( socket.getOutputStream(), true );
int low = random.nextInt( UPPER );
int hi = low + RANGE;
int number = low + random.nextInt( RANGE );
writer.println( low + " " + hi );
int tries = 0;
boolean gameOver = false;
while (!gameOver) {
int guess = scanner.nextInt();
if (guess == number) {
writer.println( "won" );
gameOver = true;
}
else {
if (++tries > TRIES) {
writer.println( "lose" );
gameOver = true;
}
else {
if (guess < number) {
writer.println( "low" );
}
else {
writer.println( "high" );
}
}
}
}
socket .close();
scanner.close();
}
catch (IOException e) {
e.printStackTrace();
fail( "Error opening client socket" );
}
catch (NoSuchElementException e) {
e.printStackTrace();
fail( "The server didn't return a value" );
}
}
@Test(expected = IOException.class)
public void testD_RunsLast_ServerShutsDown() throws IOException {
try {
// running client #1...shuts down server
Socket socket = new Socket( "localhost", 5150 );
PrintWriter writer = new PrintWriter( socket.getOutputStream(), true );
writer.println( "SHUT DOWN" );
socket.close();
}
catch (IOException e) {
e.printStackTrace();
fail( "Error opening client socket" );
}
waitTwoSecondsBetweenTests();
// running client #2...should throw exception (server should have stopped)
new Socket( "localhost", 5150 ).close();
fail( "Socket should not connect after server was shut down" );
}
}
Explanation / Answer
1) For a stateless protocol (ie UDP), there is no problem because "connections" don't exist - multiple people can send packets to the same port, and their packets will arrive in whatever sequence. Nobody is ever in the "connected" state.
2) For a stateful protocol (like TCP), a connection is identified by a 4-tuple consisting of source and destination ports and source and destination IP addresses. So, if two different machines connect to the same port on a third machine, there are two distinct connections because the source IPs differ. If the same machine (or two behind NAT or otherwise sharing the same IP address) connects twice to a single remote end, the connections are differentiated by source port (which is generally a random high-numbered port).
3) Simply, if I connect to the same web server twice from my client, the two connections will have different source ports from my perspective and destination ports from the web server's. So there is no ambiguity, even though both connections have the same source and destination IP addresses.
4) Ports are a way to multiplex IP addresses so that different applications can listen on the same IP address/protocol pair. Unless an application defines its own higher-level protocol, there is no way to multiplex a port. If two connections using the same protocol have identical source and destination IPs and identical source and destination ports, they must be the same connection.
#include <stdio.h>
#include <string.h> //strlen
#include <stdlib.h>
#include <errno.h>
#include <unistd.h> //close
#include <arpa/inet.h> //close
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros
#define TRUE 1
#define FALSE 0
#define PORT 8888
int main(int argc , char *argv[])
{
int opt = TRUE;
int master_socket , addrlen , new_socket , client_socket[30] , max_clients = 30 , activity, i , valread , sd;
int max_sd;
struct sockaddr_in address;
char buffer[1025]; //data buffer of 1K
//set of socket descriptors
fd_set readfds;
//a message
char *message = "ECHO Daemon v1.0 ";
//initialise all client_socket[] to 0 so not checked
for (i = 0; i < max_clients; i++)
{
client_socket[i] = 0;
}
//create a master socket
if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
//set master socket to allow multiple connections , this is just a good habit, it will work without this
if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
//type of socket created
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
//bind the socket to localhost port 8888
if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
printf("Listener on port %d ", PORT);
//try to specify maximum of 3 pending connections for the master socket
if (listen(master_socket, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
//accept the incoming connection
addrlen = sizeof(address);
puts("Waiting for connections ...");
while(TRUE)
{
//clear the socket set
FD_ZERO(&readfds);
//add master socket to set
FD_SET(master_socket, &readfds);
max_sd = master_socket;
//add child sockets to set
for ( i = 0 ; i < max_clients ; i++)
{
//socket descriptor
sd = client_socket[i];
//if valid socket descriptor then add to read list
if(sd > 0)
FD_SET( sd , &readfds);
//highest file descriptor number, need it for the select function
if(sd > max_sd)
max_sd = sd;
}
//wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely
activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL);
if ((activity < 0) && (errno!=EINTR))
{
printf("select error");
}
//If something happened on the master socket , then its an incoming connection
if (FD_ISSET(master_socket, &readfds))
{
if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
//inform user of socket number - used in send and receive commands
printf("New connection , socket fd is %d , ip is : %s , port : %d " , new_socket , inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
//send new connection greeting message
if( send(new_socket, message, strlen(message), 0) != strlen(message) )
{
perror("send");
}
puts("Welcome message sent successfully");
//add new socket to array of sockets
for (i = 0; i < max_clients; i++)
{
//if position is empty
if( client_socket[i] == 0 )
{
client_socket[i] = new_socket;
printf("Adding to list of sockets as %d " , i);
break;
}
}
}
//else its some IO operation on some other socket :)
for (i = 0; i < max_clients; i++)
{
sd = client_socket[i];
if (FD_ISSET( sd , &readfds))
{
//Check if it was for closing , and also read the incoming message
if ((valread = read( sd , buffer, 1024)) == 0)
{
//Somebody disconnected , get his details and print
getpeername(sd , (struct sockaddr*)&address , (socklen_t*)&addrlen);
printf("Host disconnected , ip %s , port %d " , inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
//Close the socket and mark as 0 in list for reuse
close( sd );
client_socket[i] = 0;
}
//Echo back the message that came in
else
{
//set the string terminating NULL byte on the end of the data read
buffer[valread] = '';
send(sd , buffer , strlen(buffer) , 0 );
}
}
}
}
return 0;
}
OutPut :
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.