本文是学习java Socket整理的资料,供参考。html
应用层至关于OSI中的会话层,表示层,应用层。java
区别参考:http://blog.chinaunix.net/uid-22166872-id-3716751.html算法
(1)序号:Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。设计模式
(2)确认序号:Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。浏览器
(3)标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义以下:服务器
(A)URG:紧急指针(urgent pointer)有效。网络
(B)ACK:确认序号有效。多线程
(C)PSH:接收方应该尽快将这个报文交给应用层。架构
(D)RST:重置链接。socket
(E)SYN:发起一个新链接。
(F)FIN:释放一个链接。
须要注意的是:
(A)不要将确认序号Ack与标志位中的ACK搞混了。
(B)确认方Ack=发起方Req+1,两端配对。
Socket是应用层与TCP/IP协议族通讯的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来讲,一组简单的接口就是所有,让Socket去组织数据,以符合指定的协议。
Socket链接创建和关闭,详见:http://www.2cto.com/net/201310/251896.html
链接->传输数据->关闭链接
短链接是指SOCKET链接,发送数据,接收数据后,立刻断开链接。
好比:
HTTP1.0默认是短链接,无状态的,浏览器和服务器每进行一次HTTP操做,就创建一次链接,但任务结束就中断链接。 、
Http1.1默认是长链接
无状态:协议对于事务处理没有记忆能力;
链接->传输数据->保持链接 -> 传输数据-> 。。。 ->关闭链接。
创建SOCKET链接后,不论是否使用,一致保持链接。
接受方没有接受到一个完整的包,只接受了部分;
缘由:TCP为提升传输效率,将一个包分配的足够大,致使接受方并不能一次接受完。
影响:长链接和短链接中都会出现
发送方发送的多个包数据到接收方接收时粘成一个包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
分类:一种是粘在一块儿的包都是完整的数据包,另外一种状况是粘在一块儿的包有不完整的包
出现粘包现象的缘由是多方面的:
1)发送方粘包:由TCP协议自己形成的,TCP为提升传输效率,发送方每每要收集到足够多的数据后才发送一包数据。若连续几回发送的数据都不多,一般TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。
2)接收方粘包:接收方用户进程不及时接收数据,从而致使粘包现象。这是由于接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据还没有被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据以后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。
分包(1):在出现粘包的时候,咱们的接收方要进行分包处理;
分包(2):一个数据包被分红了屡次接收;
缘由:1. IP分片传输致使的;2.传输过程当中丢失部分包致使出现的半包;3.一个包可能被分红了两次传输,在取数据的时候,先取到了一部分(还可能与接收的缓冲区大小有关系)。
影响:粘包和分包在长链接中都会出现
出现粘包和半包现象,是由于TCP当中,只有流的概念,没有包的概念。
UDP不会出现半包,粘包状况,缘由是UDP是一个完整的数据包,发送时不进行合并,所以接收的时候就不存在粘包状况。
固定长度:每次发送固定长度的数据;
特殊标示:以回车,换行做为特殊标示;获取到指定的标识时,说明包获取完整。
字节长度:包头+包长+包体的协议形式,当服务器端获取到指定的包长时才说明获取完整;
参考文章(有图):http://blog.csdn.net/pi9nc/article/details/17165171
短链接:不用考虑粘包的状况;
发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储,也不用考虑粘包;
长链接:须要在链接后一段时间内发送不一样结构数据;
处理方式:接收方建立一预处理线程,对接收到的数据包进行预处理,将粘连的包分开;
一个包没有固定长度,以太网限制在46-1500字节,1500就是以太网的MTU,超过这个量,TCP会为IP数据报设置偏移量进行分片传输,如今通常可容许应用层设置8k(NTFS系)的缓冲区,8k的数据由底层分片,而应用看来只是一次发送。
对于UDP,就不要太大,通常在1024至10K。注意一点,你不管发多大的包,IP层和链路层都会把你的包进行分片发送,通常局域网就是1500左右,广域网就只有几十字节。分片后的包将通过不一样的路由到达接收方,对于UDP而言,要是其中一个分片丢失,那么接收方的IP层将把整个发送包丢弃,这就造成丢包。
TCP做为流,发包是不会整包到达的,而是源源不断的到,那接收方就必须组包。而UDP做为消息或数据报,它必定是整包到达接收方。
关于接收,通常的发包都有包边界,首要的就是你这个包的长度要让接收方知道,因而就有个包头信息,对于TCP,接收方先收这个包头信息,而后再收包数据。一次收齐整个包也能够,可要对结果是否收齐进行验证。这也就完成了组包过程。UDP,那你只能整包接收了。要是你提供的接收Buffer太小,TCP将返回实际接收的长度,余下的还能够收,而UDP不一样的是,余下的数据被丢弃并返回WSAEMSGSIZE错误。注意TCP,要是你提供的Buffer佷大,那么可能收到的就是多个发包,你必须分离它们,还有就是当Buffer过小,而一次收不完Socket内部的数据,那么Socket接收事件(OnReceive),可能不会再触发,使用事件方式进行接收时。
//创建ServerSocket对象,监听绑定端口
ServerSocket server=new ServerSocket(1000);
//创建接收Socket,阻塞响应
Socket client=server.accept();
//得到输入流,用于接收客户端信息
InputStream in=server.getInputStream();
//得到输出流,用于输出客户端信息
//对输入流进行包装,方便使用
BufferedReader inRead=new BufferedReader(new InputStreamReader(in));
//对输出流进行包装,方便使用
PrintWriter outWriter=new PrintWriter(out);
while(true)
{
String str=inRead.readLine();//读入
outWriter.println("输出到客户端");
outWriter.flush();//输出
if(str.equals("end"))
{
break;
}
}
//关闭Socket
client.close();
server.close();
//使用Socket,创建与服务端的链接
Socket client=new Socket("127.0.0.1",1000);
InputStream in=client.getInputStream();//得到输入流
OutputStream out=client.getOutputStream();//得到输出流
//包装输入流,输出流
BufferedReader inRead=new BufferedReader(new InputStreamReader(in));
PrintWriter outWriter=new PrintWriter(out);
//得到控制台输入
BufferedReader inConsole=new BufferedReader(new InputStreamReader(in));
while(true)
{
String str=inConsole.readLine();//读取控制台输入
outWriter.println(str);//输出到服务端
outWriter.flush();//刷新缓冲区
if(str.equals("end"))
{
break;
}//退出
System.out.println(inRead.readLine())//读取服务端输出
}
client.close();
DOS下运行客户端
java -classpath sockettest-0.0.1-SNAPSHOT.jar cn.com.gome.sockettest.basic.ClientTest
方法1:socket.sendUrgentData(0);//发送1个字节的紧急数据,默认状况下,服务器端没有开启紧急数据处理,不影响正常通讯
try{
socket.sendUrgentData(0xFF);
}catch(Exception ex){
reconnect();
}
只要对方Socket的SO_OOBINLINE属性没有打开,就会自动舍弃这个字节,而SO_OOBINLINE属性默认状况下就是关闭的。
方法2:自定义心跳字符串
传输字节,字符,对象【ObjectInputStream ObjectOutputStream】(实例)