Java Socket简例

Socket是对传输层TCP或UDP连接的抽象,以便于应用层使用

示例代码如下:

Socket IO工具类:

package com.test.util;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class SocketIO{
	public static DataInputStream getInput(Socket socket) throws IOException{
		//接收缓存区大小,socket获取输入流之前设置
		socket.setReceiveBufferSize(10);
		InputStream input = socket.getInputStream();
		return new DataInputStream(input);
	}
	
	public static DataOutputStream getOutput(Socket socket) throws IOException{
		//发送缓存区大小,socket获取输出流之前设置
		socket.setSendBufferSize(10);
		OutputStream output = socket.getOutputStream();
		return new DataOutputStream(output);
	}
}

如果传输数据量较大,则应配置较大缓存区,以减少数据传输次数,提高数据传输效率

如果传输数据量较小并且比较频繁,则应配置较小缓存,以提高通信速度

Socket Client客户端:

package com.test.client;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

import org.apache.log4j.Logger;

import com.test.util.SocketIO;

public class Client {
	private Logger logger = Logger.getLogger(Client.class);
	private int port = 10000;
	private String host = "127.0.0.1";
	private Socket socket;
	
	public Client(){
		try {
			socket = new Socket();
			//关闭socket时,立即释放socket绑定端口以便端口重用,默认为false
			socket.setReuseAddress(true);
			//关闭传输缓存,默认为false
			socket.setTcpNoDelay(true);
			//如果输入流等待1000毫秒还未获得服务端发送数据,则提示超时,0为永不超时
			socket.setSoTimeout(10000);
			//关闭socket时,底层socket不会直接关闭,会延迟一会,直到发送完所有数据
			//等待10秒再关闭底层socket连接,0为立即关闭底层socket连接
			socket.setSoLinger(true, 10);
			//设置性能参数,可设置任意整数,数值越大,相应的参数重要性越高(连接时间,延迟,带宽)
			socket.setPerformancePreferences(3, 2, 1);
			SocketAddress address = new InetSocketAddress(host, port);
			//socket创建超时时间为1000毫秒
			socket.connect(address, 10000);
			
			logger.info("client ip:"+socket.getLocalAddress());
			logger.info("client port:"+socket.getLocalPort());
			logger.info("servetr ip:"+socket.getInetAddress());
			logger.info("servetr port:"+socket.getPort());
		} catch (IOException e) {
			e.printStackTrace();
			logger.error("Cilent socket establish failed!");
		}
		logger.info("Client socket establish success!");
	}
	
	public void request(){
		DataOutputStream output = null;
		DataInputStream input = null;
		try{
			output = SocketIO.getOutput(socket);
			input = SocketIO.getInput(socket);
			
			String question = "your name?";
			byte[] bytes = question.getBytes("utf-8");
			int len = bytes.length;
			output.writeInt(len);
			output.write(bytes);
			
			len = input.readInt();
			bytes = new byte[len];
			input.read(bytes);
			
			logger.info("server answer:"+new String(bytes,"utf-8"));
		}catch(Exception e){
			e.printStackTrace();
			logger.error("client request error");
		}finally{
			if(socket != null){
				try{
					socket.close();
				}catch(Exception e){
					e.printStackTrace();
					logger.error("socket close error");
				}
			}
		}
	}
	
	public static void main(String[] args){
		Client client = new Client();
		client.request();
	}
}

Socket Server服务端:

package com.test.server;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

import org.apache.log4j.Logger;

public class Server {
	private Logger logger = Logger.getLogger(Server.class);
	private int queueSize = 10;
	private int port = 10000;
	private ServerSocket serverSocket;
	
	public Server(){
		try{
			serverSocket = new ServerSocket();
			//关闭serverSocket时,立即释放serverSocket绑定端口以便端口重用,默认为false
			serverSocket.setReuseAddress(true);
			//accept等待连接超时时间为1000毫秒,默认为0,永不超时
			//serverSocket.setSoTimeout(10000);
			//为所有accept方法返回的socket对象设置接收缓存区大小,单位为字节,默认值和操作系统有关
			serverSocket.setReceiveBufferSize(128*1024);
			//设置性能参数,可设置任意整数,数值越大,相应的参数重要性越高(连接时间,延迟,带宽)
			serverSocket.setPerformancePreferences(3, 2, 1);
			//服务端绑定至端口,10为服务端连接请求队列长度
			serverSocket.bind(new InetSocketAddress(port), queueSize);
		}catch(Exception e){
			e.printStackTrace();
			logger.error("Server establish error!");
		}
		logger.info("Server start up!");
	}

	public void service(){
		while(true){
			Socket socket = null;
			try{
				//从连接请求队列中取出一个客户连接请求,创建与客户连接的socket对象
				//如果队列中没有请求,accept方法就会一直等待
				socket = serverSocket.accept();
				Thread thread = new Thread(new ServiceHandler(socket));
				thread.start();
			}catch(Exception e){
				e.printStackTrace();
				logger.error("Server run exception!");
			}
		}
	}
	
	public static void main(String[] args) {
		Server server = new Server();
		server.service();
	}
}
package com.test.server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

import org.apache.log4j.Logger;

import com.test.util.SocketIO;

public class ServiceHandler implements Runnable {
	private Logger logger = Logger.getLogger(ServiceHandler.class);
	private Socket socket;
	
	public ServiceHandler(Socket socket){
		this.socket = socket;
	}
	
	@Override
	public void run() {
		DataOutputStream output = null;
		DataInputStream input = null;
		try{
			output = SocketIO.getOutput(socket);
			input = SocketIO.getInput(socket);
			
			int len = input.readInt();
			byte[] bytes = new byte[len];
			input.read(bytes);
			
			String request = new String(bytes, "utf-8");
			
			logger.info("client request:"+request);
			
			String answer = "not supported";
			if(request.equals("your name?")){
				answer = "server";
			}
			bytes = answer.getBytes("utf-8");
			len = bytes.length;
			output.writeInt(len);
			output.write(bytes);
		}catch(Exception e){
			e.printStackTrace();
			logger.error("Service handler run exception!");
		}finally{
			if(socket != null){
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
					logger.error("socket close error");
				}
			}
		}
	}
}

每接收到一个请求,就会建立一个新的连接处理请求,当请求量很大时,将建立大量线程导致系统资源耗尽,所以我们应当在服务端使用线程池,避免创建过多的线程

log4j.properties日志设置:

log4j.rootLogger=info,logOutput

#log console out put 
log4j.appender.logOutput=org.apache.log4j.ConsoleAppender
log4j.appender.logOutput.layout=org.apache.log4j.PatternLayout
log4j.appender.logOutput.layout.ConversionPattern=%p%d{[yy-MM-dd HH:mm:ss]}[%c] -> %m%n

Client端日志:

INFO[13-10-10 10:04:23][com.test.client.Client] -> client ip:/127.0.0.1
INFO[13-10-10 10:04:23][com.test.client.Client] -> client port:52362
INFO[13-10-10 10:04:23][com.test.client.Client] -> servetr ip:/127.0.0.1
INFO[13-10-10 10:04:23][com.test.client.Client] -> servetr port:10000
INFO[13-10-10 10:04:23][com.test.client.Client] -> Client socket establish success!
INFO[13-10-10 10:04:23][com.test.client.Client] -> server answer:server

Server端日志:

INFO[13-10-10 10:04:19][com.test.server.Server] -> Server start up!
INFO[13-10-10 10:04:23][com.test.server.Server] -> client request:your name?

 

PS:

这里要特别说明一下DataInputStream这个类的readShort方法:

public final short readShort() throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        if ((ch1 | ch2) < 0)
            throw new EOFException();
        return (short)((ch1 << 8) + (ch2 << 0));
    }

可以看到是先读高位,再读低位

再来看下DataOutputStream这个类的writeShort方法:

public final void writeShort(int v) throws IOException {
        out.write((v >>> 8) & 0xFF);
        out.write((v >>> 0) & 0xFF);
        incCount(2);
}

可以看到是先写高位,再写低位

如果传输Short类型数据(其它类型数据相同)时,要求先传低位,再传高位,则不能使用自带的方法

将数据转为低位在前高位在后的字节数组,然后传输整个数组即可

public void write(byte b[]) throws IOException {
	write(b, 0, b.length);
}
public void write(byte b[], int off, int len) throws IOException {
	if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
	    throw new IndexOutOfBoundsException();

	for (int i = 0 ; i < len ; i++) {
	    write(b[off + i]);
	}
}

 

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值