[英]Socket sends message only once
下面的代码可以在计划的时间工作并发送消息,但是我认为这不是每次计时器执行计划的任务时都打开新套接字的好方法。 我想要的是只在run方法中打开一次套接字,并在计时器中创建类的新实例时在SendMessage类中对其进行访问。 这样,它就无法工作,它只会发送一条消息,然后停止发送。 对于一些批评者或使线程安全的技巧,我也很高兴。
public class Client implements Runnable{
// Client Constructor here
@Override
public void run(){
//SENDS ONLY ONE MESSAGE
pitcherSocket = new Socket(InetAddress.getByName(hostname), port);
Timer timer = new Timer();
timer.schedule(new SendMessage(), 0, 1000/mps);
}
private class SendMessage extends TimerTask{
private int id;
@Override
public void run() {
try
{ // THIS WORKS FINE, SENDS MESSAGES AT SCHEDULED TIME
pitcherSocket = new Socket(InetAddress.getByName(hostname), port);
OutputStream outToServer = pitcherSocket.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeInt(id);
out.flush();
}catch(IOException e)
{
e.printStackTrace();
}
}
}
}
编辑:整个代码
客户
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
public class Pitcher implements Runnable{
private int port;
private int mps;
private int size;
private String hostname;
private List<Integer> messageIds = Collections.synchronizedList(new ArrayList<Integer>());
private Socket pitcherSocket;
//constatns, integer is 4 bytes, long is 8 bytes
private static final int INT_SIZE = 4;
private static final int LONG_SIZE = 8;
public Pitcher(int port, int mps, int size, String hostname) {
this.port = port;
this.mps = mps;
this.size = size;
this.hostname = hostname;
}
@Override
public void run(){
System.out.println("Pitcher running...");
System.out.println();
Timer timer = new Timer();
timer.schedule(new SendMessage(), 0, 1000/mps);
timer.schedule(new DisplayStatistics(), 0, 1000/mps);
}
//Nested class that sends messages
private class SendMessage extends TimerTask{
private int numberOfSentMessages = 0;
private int id;
@Override
public void run() {
try {
pitcherSocket = new Socket(InetAddress.getByName(hostname), port);
OutputStream outToServer = pitcherSocket.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
//send message size
out.writeInt(size);
//message id is same as number of the sent message
id = numberOfSentMessages + 1;
out.writeInt(id);
messageIds.add(id);
//get system timestamp
long currentTimestamp = System.currentTimeMillis();
out.writeLong(currentTimestamp);
//fill in the rest-
byte[] rest = new byte[size - 2 * INT_SIZE - LONG_SIZE]; //message size(default 300 bytes) - size(4 bytes) - message id(4 bytse) - timestamp(8 bytes)
out.write(rest);
out.flush();
numberOfSentMessages++;
InputStream inFromServer = pitcherSocket.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
Integer catcherMessageSize = in.readInt();
Integer catcherId = in.readInt();
long catcherTimestamp = in.readLong();
System.out.println("Sent message: " + size + " " + id + " " + currentTimestamp + "...");
System.out.println("Received message: " + catcherMessageSize + " " + catcherId + " " + catcherTimestamp + "...");
System.out.println();
}catch(IOException e)
{
e.printStackTrace();
}
}
}
}
服务器
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
public class Catcher implements Runnable{
private int port;
private String bind;
private ServerSocket serverSocket;
//constatns, integer is 4 bytes, long is 8 bytes
private static final int INT_SIZE = 4;
private static final int LONG_SIZE = 8;
public Catcher(int port, String bind) {
this.port = port;
this.bind = bind;
}
@Override
public void run() {
System.out.println("Catcher running...");
System.out.println();
try {
serverSocket = new ServerSocket(port, 100, InetAddress.getByName(bind));
}
catch (IOException e1) {
e1.printStackTrace();
}
while(true){
try
{
Socket server = serverSocket.accept();
DataInputStream in = new DataInputStream(server.getInputStream());
Integer pitcherMessageSize = in.readInt();
Integer pitcherId = in.readInt();
long pitcherTimestamp = in.readLong();
DataOutputStream out = new DataOutputStream(server.getOutputStream());
//message id and size are sent back
out.writeInt(pitcherMessageSize);
out.writeInt(pitcherId);
//send back current time
long currentTimestamp = System.currentTimeMillis();
out.writeLong(currentTimestamp);
//fill in the rest
byte[] rest = new byte[pitcherMessageSize - 2 * INT_SIZE - LONG_SIZE]; //message size(default 300 bytes) - size(4 bytes) - message id(4 bytes) - timestamp(8 bytes)
out.write(rest);
out.flush();
System.out.println("Received message: " + pitcherMessageSize + " " + pitcherId + " " + pitcherTimestamp + "...");
System.out.println("Sent message: " + pitcherMessageSize + " " + pitcherId + " " + currentTimestamp + "...");
System.out.println();
//server.close();
}
catch(SocketTimeoutException s){
System.out.println("Socket timed out!");
break;
}
catch(IOException e){
e.printStackTrace();
break;
}
}
}
}
您是否考虑过同时设置套接字和SendMessage的DataOutputStream成员变量。 这是一些代码,可以让您大致入手。 您可能需要进行一些增强,例如检查套接字是否已打开,如果当前套接字已关闭,则能够创建新套接字...
private class SendMessage extends TimerTask {
private int id = 10;
private Socket pitchSocket;
private DataOutputStream out;
public SendMessage(Socket socket) {
this.pitchSocket = socket;
try{
out = new DataOutputStream(pitchSocket.getOutputStream());
} catch(IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
out.writeInt(id);
out.flush();
} catch(IOException e) {
e.printStackTrace();
}
}
}
能够查看整个代码之后,我认为您肯定有一些线程问题,尽管我认为它们更多地是在服务器端而不是客户端。 您的服务器是单线程的。 这意味着您一次只能处理一个请求。 您需要一个多线程服务器。 我重构了您的代码,以创建一个多线程的Catcher示例。 我正在使用Thead类来完成所有这一切,这可能有点过时了。 您可能想看一看java.util.concurrent,它们可能会是最新的。
package clientserver;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
public class Catcher implements Runnable{
private int port;
private String bind;
private ServerSocket serverSocket;
public Catcher(int port, String bind) {
this.port = port;
this.bind = bind;
}
@Override
public void run() {
System.out.println("Catcher running...");
System.out.println();
try {
serverSocket = new ServerSocket(port, 100, InetAddress.getByName(bind));
}
catch (IOException e1) {
e1.printStackTrace();
}
while(true){
try
{
new Thread(new CatcherHandler(serverSocket.accept())).start();
Thread.sleep(1000);
}
catch(SocketTimeoutException s){
System.out.println("Socket timed out!");
break;
}
catch(IOException e){
e.printStackTrace();
break;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] argv){
new Thread( new Catcher(8093, "localhost")).start();;
}
}
class CatcherHandler implements Runnable{
Socket server;
DataOutputStream out;
DataInputStream in;
private static final int INT_SIZE = 4;
private static final int LONG_SIZE = 8;
public CatcherHandler(Socket server) {
super();
this.server = server;
try {
in = new DataInputStream(server.getInputStream());
out = new DataOutputStream(server.getOutputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
try{
if(in.available() > 0){
Integer pitcherMessageSize = in.readInt();
Integer pitcherId = in.readInt();
long pitcherTimestamp = in.readLong();
//message id and size are sent back
out.writeInt(pitcherMessageSize);
out.writeInt(pitcherId);
//send back current time
long currentTimestamp = System.currentTimeMillis();
out.writeLong(currentTimestamp);
//fill in the rest
byte[] rest = new byte[pitcherMessageSize - 2 * INT_SIZE - LONG_SIZE]; //message size(default 300 bytes) - size(4 bytes) - message id(4 bytes) - timestamp(8 bytes)
out.write(rest);
out.flush();
System.out.println("Received message: " + pitcherMessageSize + " " + pitcherId + " " + pitcherTimestamp + "...");
System.out.println("Sent message: " + pitcherMessageSize + " " + pitcherId + " " + currentTimestamp + "...");
System.out.println();
Thread.sleep(1000);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{}
//server.close();
}
}
另外,我重构了您的客户端,使其能够使用一个插槽并安全可靠。 现在,SendMessage将一个DataInputStream和一个DataOutputSteam作为其参数。
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
public class Pitcher implements Runnable{
private int port;
private int mps;
private int size;
private String hostname;
private List<Integer> messageIds = Collections.synchronizedList(new ArrayList<Integer>());
private Socket pitcherSocket;
private DataOutputStream out;
private DataInputStream in;
//constatns, integer is 4 bytes, long is 8 bytes
private static final int INT_SIZE = 4;
private static final int LONG_SIZE = 8;
public Pitcher(int port, int mps, int size, String hostname) {
this.port = port;
this.mps = mps;
this.size = size;
this.hostname = hostname;
try {
this.pitcherSocket = new Socket(InetAddress.getByName(hostname), port);
out = new DataOutputStream(pitcherSocket.getOutputStream());
in = new DataInputStream(pitcherSocket.getInputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] argv) throws Exception{
for(int i = 0; i < 10; i++){
new Thread(new Pitcher(8093, 1, 200, "localhost")).start();
Thread.sleep(1000);
}
Thread.sleep(10000);
}
@Override
public void run(){
System.out.println("Pitcher running...");
System.out.println();
Timer timer = new Timer();
timer.schedule(new SendMessage(out, in), 0, 1000);
//timer.schedule(new DisplayStatistics(), 0, 1000);
}
//Nested class that sends messages
private class SendMessage extends TimerTask{
private int numberOfSentMessages = 0;
private int id;
private DataOutputStream out;
private DataInputStream in;
public SendMessage(DataOutputStream out, DataInputStream in){
this.out = out;
this.in = in;
}
@Override
public void run() {
try {
long currentTimestamp = 0L;
synchronized(out){
//send message size
out.writeInt(size);
//message id is same as number of the sent message
id = numberOfSentMessages + 1;
out.writeInt(id);
messageIds.add(id);
//get system timestamp
currentTimestamp = System.currentTimeMillis();
out.writeLong(currentTimestamp);
//fill in the rest-
byte[] rest = new byte[size - 2 * INT_SIZE - LONG_SIZE]; //message size(default 300 bytes) - size(4 bytes) - message id(4 bytse) - timestamp(8 bytes)
out.write(rest);
out.flush();
}
numberOfSentMessages++;
long catcherTimestamp = 0L;
Integer catcherMessageSize;
Integer catcherId;
synchronized(in){
catcherMessageSize = in.readInt();
catcherId = in.readInt();
catcherTimestamp = in.readLong();
}
System.out.println("Sent message: " + size + " " + id + " " + currentTimestamp + "...");
System.out.println("Received message: " + catcherMessageSize + " " + catcherId + " " + catcherTimestamp + "...");
System.out.println();
Thread.sleep(1000);
}catch(IOException e)
{
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Java Socket类不是线程安全的。 要使多个线程访问同一个Socket对象,您需要同步它们的操作。 这可以通过为您的所有SendMessage线程提供一个公共对象来完成,该对象随后将充当锁。 您计划要使用的每个套接字操作(例如,读取和写入)都需要一个对象。 然后,将对Socket对象进行调用的每个操作重构为单独的方法,并在该对象周围同步它们。 例如,对于读取操作,您可以在SendMessage内部拥有一个名为read()的方法,该方法调用Socket.read并在锁定对象周围同步此方法以进行读取。
private class SendMessage extends TimerTask{
private Object readLock;
private Socket socket;
public SendMessage(Object readLock, Socket socket) {
this.readLock = readLock;
this.socket = socket;
}
public void readFromSocket() {
synchronized(readLock) {
socket.read();
}
}
@Override
public void run() {
readFromSocket();
// do other stuff
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.