Socket通讯学习手册.docx
《Socket通讯学习手册.docx》由会员分享,可在线阅读,更多相关《Socket通讯学习手册.docx(16页珍藏版)》请在冰豆网上搜索。
Socket通讯学习手册
Socket通讯学习手册
1什么是socket
所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。
应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
以J2SDK-1.3为例,Socket和ServerSocket类库位于包中。
ServerSocket用于服务器端,Socket是建立网络连接时使用的。
在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。
对于一个网络连接来说,套接字是平等的,并没有差别,不因为在服务器端或在客户端而产生不同级别。
不管是Socket还是ServerSocket它们的工作都是通过SocketImpl类及其子类完成的。
重要的SocketAPI:
.Socket继承于java.lang.Object,有八个构造器,其方法并不多,下面介绍使用最频繁的三个方法,其它方法大家可以见JDK文档。
●Accept方法用于产生"阻塞",直到接受到一个连接,并且返回一个客户端的Socket对象实例。
"阻塞"是一个术语,它使程序运行暂时"停留"在这个地方,直到一个会话产生,然后程序继续;通常"阻塞"是由循环产生的。
●getInputStream方法获得网络连接输入,同时返回一个IutputStream对象实例。
●getOutputStream方法连接的另一端将得到输入,同时返回一个OutputStream对象实例。
注意:
其中getInputStream和getOutputStream方法均会产生一个IOException,它必须被捕获,因为它们返回的流对象,通常都会被另一个流对象使用。
2开发示例
2.1示例一
这个程序建立了一个服务器,它一直监听10000端口,等待用户连接。
在建立连接后给客户端返回一段信息,然后结束会话。
这个程序一次只能接受一个客户连接。
2.1.1设计原理:
服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。
客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。
客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口。
2.1.2源代码
服务器端
packagetest;
importjava.io.BufferedReader;
importjava.io.IOException;
importjava.io.InputStreamReader;
importjava.io.PrintWriter;
import.ServerSocket;
import.Socket;
publicclassServerTest
{
privateServerSocketss;
privateSocketsocket;
privateBufferedReaderin;
privatePrintWriterout;
publicServerTest()
{
try
{
ss=newServerSocket(10000);
while(true)
{
socket=ss.accept();
in=newBufferedReader(newInputStreamReader(
socket.getInputStream()));
out=newPrintWriter(socket.getOutputStream(),true);
Stringline=in.readLine();
out.println("youinputis:
"+line);
out.close();
in.close();
socket.close();
}
}
catch(IOExceptione)
{
}
}
publicstaticvoidmain(String[]args)
{
newServerTest();
}
}
客户端:
importjava.io.BufferedReader;
importjava.io.IOException;
importjava.io.InputStreamReader;
importjava.io.PrintWriter;
import.Socket;
publicclassClientTest
{
Socketsocket;
BufferedReaderin;
PrintWriterout;
publicClientTest()
{
try
{
socket=newSocket("xxx.xxx.xxx.xxx",10000);
in=newBufferedReader(newInputStreamReader(
socket.getInputStream()));
out=newPrintWriter(socket.getOutputStream(),true);
BufferedReaderline=newBufferedReader(newInputStreamReader(
System.in));
out.println(line.readLine());
line.close();
out.close();
in.close();
socket.close();
}
catch(IOExceptione)
{
}
}
publicstaticvoidmain(String[]args)
{
newClientTest();
}
}
2.2示例二
在实际的网络环境里,同一时间只对一个用户服务是不可行的。
一个优秀的网络服务程序除了能处理用户的输入信息,还必须能够同时响应多个客户端的连接请求。
在java中,实现以上功能特点是非常容易的。
2.2.1设计原理:
程序监听一端口,等待客户接入;
serverListenSocket=newServerSocket(PORT);
serverListenSocket.setReuseAddress(true);//设置端口复用为true
同时创建一个线程池,准备接管会话。
serverThreadPool=newThreadPoolExecutor(CORE_POOL_SIZE,MAX_POOL_SIZE,KEEPALIVE_TIME,TIME_UNIT,BlockingQueueworkQueue,rejectedExecutionHandler);
当一个Socket会话产生后,将这个会话交给线程池中的线程处理,然后主程序继续监听。
while(true){
Socketsocket=serverListenSocket.accept();
serverThreadPool.execute(newTask(socket));
}
Task.java是专门处理通讯会话的类,里面负责对socket套接字的处理。
首先接收通讯报文头:
dis=newDataInputStream(connectedSocket.getInputStream());
dos=newDataOutputStream(connectedSocket.getOutputStream());
byte[]head=newbyte[14];
dis.read(head,0,14);
intrcvLen=Integer.parseInt(newString(head).substring(0,8).trim());
StringtransCode=newString(head).substring(8,14);
System.out.println("报文长度=["+rcvLen+"]交易=["+transCode+"]");
根据通讯报文头中传来的报文体长度循环接收报文体内容:
while(readLenretLen=dis.read(readBuff,readLen,rcvLen-readLen);
if(retLen==-1||retLen==0){
//由于指定读取非0的长度,因此访问不可能为0,-1也不可能,因为是按长度的循环读
System.err.println("can'treadmessagefromsocket,readret=["+retLen+"]");
return;
}
readLen+=retLen;
}
根据交易码处理返回:
if("BSV001".equals(transCode)){
dos.write("00000173BSV00120110629P000010000000ABCD0000000000000000000000001234ABCD000000000000000000000000123499999999".getBytes());
}elseif("BSV002".equals(transCode)){
}elseif("BSV003".equals(transCode)){
}elseif("BSV004".equals(transCode)){
}elseif("BSV005".equals(transCode)){
}elseif("BSV006".equals(transCode)){
}elseif("BSV007".equals(transCode)){
}elseif("BSV008".equals(transCode)){
}elseif("BSV009".equals(transCode)){
}else{
System.err.println("交易["+transCode+"]没有配置响应报文!
");
return;
}
dos.flush();
2.2.2源代码
服务器端
Server.java
packagem;
importjava.io.IOException;
import.ServerSocket;
import.Socket;
importjava.util.concurrent.ArrayBlockingQueue;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.RejectedExecutionHandler;
importjava.util.concurrent.ThreadPoolExecutor;
importjava.util.concurrent.TimeUnit;
publicclassServer{
privatestaticfinalintCORE_POOL_SIZE=2;
privatestaticfinalintMAX_POOL_SIZE=100;
privatestaticfinalintKEEPALIVE_TIME=3;
privatestaticfinalintQUEUE_CAPACITY=(CORE_POOL_SIZE+MAX_POOL_SIZE)/2;
privatestaticfinalTimeUnitTIME_UNIT=TimeUnit.SECONDS;
privatestaticfinalintPORT=1912;
privateArrayBlockingQueueBlockingQueueworkQueue=newArrayBlockingQueue(QUEUE_CAPACITY);
privateThreadPoolExecutorserverThreadPool=null;
privateExecutorServicepool=null;
privateRejectedExecutionHandlerrejectedExecutionHandler=newThreadPoolExecutor.DiscardOldestPolicy();
privateServerSocketserverListenSocket=null;
publicvoidstart(){
/**
*线程池
*CORE_POOL_SIZE核心线程数
*MAX_POOL_SIZE最大线程数
*
*BlockingQueueworkQueue任务队列
*
*模型基本概述:
提交任务,首先提交至线程池任务队列,即BlockingQueueworkQueue
*如果有空闲线程,则由某个现场出来该任务(任务实现Runnable接口)
*/
serverThreadPool=newThreadPoolExecutor(CORE_POOL_SIZE,MAX_POOL_SIZE,KEEPALIVE_TIME,TIME_UNIT,BlockingQueueworkQueue,rejectedExecutionHandler);
try{
serverListenSocket=newServerSocket(PORT);
serverListenSocket.setReuseAddress(true);//设置端口复用为true
System.out.println("服务器启动......");
System.out.println("监听端口=["+PORT+"]");
while(true){
Socketsocket=serverListenSocket.accept();
serverThreadPool.execute(newTask(socket));
}
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
cleanup();
}
publicvoidcleanup(){
if(null!
=serverListenSocket){
try{
serverListenSocket.close();
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
//serverThreadPool.shutdown();
pool.shutdown();
}
publicstaticvoidmain(Stringargs[]){
Serverserver=newServer();
server.start();
}
}
Task.java
packagem;
importjava.io.DataInputStream;
importjava.io.DataOutputStream;
importjava.io.IOException;
importjava.io.Serializable;
import.Socket;
classTaskimplementsRunnable,Serializable{
privatestaticfinallongserialVersionUID=0;
privateSocketconnectedSocket=null;
Task(Socketsocket){
connectedSocket=socket;
}
publicvoidrun(){
DataInputStreamdis=null;
DataOutputStreamdos=null;
try{
dis=newDataInputStream(connectedSocket.getInputStream());
dos=newDataOutputStream(connectedSocket.getOutputStream());
byte[]head=newbyte[14];
dis.read(head,0,14);
intrcvLen=Integer.parseInt(newString(head).substring(0,8).trim());
StringtransCode=newString(head).substring(8,14);
System.out.println("报文长度=["+rcvLen+"]交易=["+transCode+"]");
byte[]readBuff=newbyte[rcvLen];
intreadLen=0;
intretLen=0;
/**
*按报文长度rcvLen,循环收报文数据。
*/
while(readLenretLen=dis.read(readBuff,readLen,rcvLen-readLen);
if(retLen==-1||retLen==0){
//由于指定读取非0的长度,因此访问不可能为0,-1也不可能,因为是按长度的循环读
System.err.println("can'treadmessagefromsocket,readret=["+retLen+"]");
return;
}
readLen+=retLen;
}
byte[]rcvBuff=newbyte[readBuff.length];
System.arraycopy(readBuff,0,rcvBuff,0,readBuff.length);
StringrcvString=newString(rcvBuff);
System.out.println("交易["+transCode+"]报文["+rcvString+"]");
if("BSV001".equals(transCode)){
dos.write("00000173BSV00120110629P000010000000ABCD0000000000000000000000001234ABCD000000000000000000000000123499999999".getBytes());
}elseif("BSV002".equals(transCode)){
}elseif("BSV003".equals(transCode)){
}elseif("BSV004".equals(transCode)){
}elseif("BSV005".equals(transCode)){
}elseif("BSV006".equals(transCode)){
}elseif("BSV007".equals(transCode)){
}elseif("BSV008".equals(transCode)){
}elseif("BSV009".equals(transCode)){
}else{
System.err.println("交易["+transCode+"]没有配置响应报文!
");
return;
}
dos.flush();
}catch(Exceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}finally{
if(null!
=dis){
try{
dis.close();
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
if(null!
=dos){
try{
dos.close();
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
if(null!
=connectedSocket){
try{
connectedSocket.close();
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
}
}
客户端:
Client.java
packagem;
importjava.io.IOException;
importjava.io.InputStream;
impo