「tcp校验java」tcp校验码
本篇文章给大家谈谈tcp校验java,以及tcp校验码对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:
- 1、java写tcp客户端测试类该怎么写
- 2、TCP和UDP通信有什么区别 如何分别用java实现?
- 3、TCP检验和怎么计算,16位二进制数,怎么从高16位加到低16位,请问这个计算过程,谢谢
- 4、java开发要不要学tcpip
- 5、如何用JAVA来编写TCP&UDP测试工具
java写tcp客户端测试类该怎么写
1.TCP服务端的程序编写
package test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class MyTcp{
private BufferedReader reader;
private ServerSocket serverSocket;
private Socket socket;
/**
* 创建服务端的程序,读取客户端传来的数据
*/
void getserver(){
try {
serverSocket = new ServerSocket(8998); //实例化服务端socket
System.out.println("服务器套接字已经创建成功");
while (true) {
System.out.println("等待客户机的连接:");
socket = serverSocket.accept(); //实例化socket对象
reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //实例化BufferReader对象
getClientMessage();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void getClientMessage() {
try {
while (true) {
System.out.println("客户机传来的信息是:"+reader.readLine());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
MyTcp myTcp = new MyTcp(); //创建本类对象
myTcp.getserver();
}
}
2.TCP客户端程序编写
package test;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.channels.WritableByteChannel;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.BevelBorder;
public class MyTcpClient extends JFrame{
private PrintWriter printWriter;
Socket socket;
private JTextField jTextField = new JTextField();
private JTextArea jTextArea = new JTextArea();
Container container;
/**
* 创建的Tcp客户端程序
*/
public MyTcpClient (String title) {
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
container = this.getContentPane();
final JScrollPane jScrollPane = new JScrollPane();
jScrollPane.setBorder(new BevelBorder(BevelBorder.RAISED)); //显示边框
getContentPane().add(jScrollPane,BorderLayout.CENTER);
jScrollPane.setViewportView(jTextArea);
container.add(jTextField,"South"); //将文本框放在窗体下面
jTextField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
printWriter.println(jTextField.getText()); //将文本框的信息写入流(为下面的输出流写入信息做准备)
jTextArea.append(jTextField.getText() + "\n");
jTextArea.setSelectionEnd(jTextArea.getText().length());
jTextField.setText(null);
}
});
}
private void connect() {
jTextArea.append("尝试连接中...\n");
try {
socket = new Socket("127.0.0.1",8998);
printWriter = new PrintWriter(socket.getOutputStream(),true); //将printwriter中的信息流写入到套接字的输出流传送给服务端
jTextArea.setText("已完成连接\n\n");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
MyTcpClient myTcpClient = new MyTcpClient("向服务器发送数据");
myTcpClient.setSize(500,200);
myTcpClient.setVisible(true);
myTcpClient.connect();
}
}
3.效果展示
1先将服务端的程序跑起来
2再将客户端的程序跑起来
3.客户端和服务端进行交互
TCP和UDP通信有什么区别 如何分别用java实现?
TCP是面向连接,UDP面向非连接,资料不复制,在这里简单说下:
TCP建立连接时需要传说的三次握手,服务端与客户端需要确认对方身份而已,建立好连接后,就开始传递消息,直到有一方断开连接位置。 就好比两个人打电话,要先通了才能说话。
UDP只是数据报发送,它的优点速度快,并非要向TCP那样麻烦建立,它只负责将信息发出,但是并不确保信息的准确完整性等,就好比发短信,短信是出去了,但是中间是否有问题,是否对方手机能收到就不管了。
在java中想要实现上述两种协议通信,可采用socket建立连接,socket可以理解为码头,其实是套接字,这里简单说下,就好比两个城市运输货物,通过码头走货一样。至于如何通过socket建立两个连接,网上资料多的是,在这里不复制例子了。
TCP检验和怎么计算,16位二进制数,怎么从高16位加到低16位,请问这个计算过程,谢谢
TCP校验和是一个端到端的校验和,由发送端计算,然后由接收端验证。其目的是为了发现TCP首部和数据在发送端到
接收端之间发生的任何改动。如果接收方检测到校验和有差错,则TCP段会被直接丢弃。
TCP校验和覆盖TCP首部和TCP数据,而IP首部中的校验和只覆盖IP的首部,不覆盖IP数据报中的任何数据。
TCP的校验和是必需的,而UDP的校验和是可选的。
TCP和UDP计算校验和时,都要加上一个12字节的伪首部。
伪首部共有12字节,包含如下信息:源IP地址、目的IP地址、保留字节(置0)、传输层协议号(TCP是6)、TCP报文长度(报头+数据)。
伪首部是为了增加TCP校验和的检错能力:如检查TCP报文是否收错了(目的IP地址)、传输层协议是否选对了(传输层协议号)等。
首先,把伪首部、TCP报头、TCP数据分为16位的字,如果总长度为奇数个字节,则在最后增添一个位都为0的字节。
把TCP报头中的校验和字段置为0(否则就陷入鸡生蛋还是蛋生鸡的问题)。
其次,用反码相加法累加所有的16位字(进位也要累加)。
最后,对计算结果取反,作为TCP的校验和。
实现
基于2.6.18、x86_64。
csum_tcpudp_nofold()按4字节累加伪首部到sum中。
[java] view plaincopy
static inline unsigned long csum_tcpudp_nofold (unsigned long saddr, unsigned long daddr,
unsigned short len, unsigned short proto,
unsigned int sum)
{
asm("addl %1, %0\n" /* 累加daddr */
"adcl %2, %0\n" /* 累加saddr */
"adcl %3, %0\n" /* 累加len(2字节), proto, 0*/
"adcl $0, %0\n" /*加上进位 */
: "=r" (sum)
: "g" (daddr), "g" (saddr), "g" ((ntohs(len) 16) + proto*256), "0" (sum));
return sum;
}
csum_tcpudp_magic()产生最终的校验和。
首先,按4字节累加伪首部到sum中。
其次,累加sum的低16位、sum的高16位,并且对累加的结果取反。
最后,截取sum的高16位,作为校验和。
[java] view plaincopy
static inline unsigned short int csum_tcpudp_magic(unsigned long saddr, unsigned long daddr,
unsigned short len, unsigned short proto,
unsigned int sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
}
static inline unsigned int csum_fold(unsigned int sum)
{
__asm__(
"addl %1, %0\n"
"adcl 0xffff, %0"
: "=r" (sum)
: "r" (sum 16), "0" (sum 0xffff0000)
/* 将sum的低16位,作为寄存器1的高16位,寄存器1的低16位补0。
* 将sum的高16位,作为寄存器0的高16位,寄存器0的低16位补0。
* 这样,addl %1, %0就累加了sum的高16位和低16位。
*
* 还要考虑进位。如果有进位,adcl 0xfff, %0为:0x1 + 0xffff + %0,寄存器0的高16位加1。
* 如果没有进位,adcl 0xffff, %0为:0xffff + %0,对寄存器0的高16位无影响。
*/
);
return (~sum) 16; /* 对sum取反,返回它的高16位,作为最终的校验和 */
}
发送校验
[java] view plaincopy
#define CHECKSUM_NONE 0 /* 需要由传输层自己计算校验和 */
#define CHECKSUM_HW 1 /* 由硬件计算报头和首部的校验和 */
#define CHECKSUM_UNNECESSARY 2 /* 表示不需要校验,或者已经成功校验了 */
#define CHECKSUM_PARTIAL CHECKSUM_HW
#define CHECKSUM_COMPLETE CHECKSUM_HW
@tcp_transmit_skb()
icsk-icsk_af_ops-send_check(sk, skb-len, skb); /* 计算校验和 */
[java] view plaincopy
void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb)
{
struct inet_sock *inet = inet_sk(sk);
struct tcphdr *th = skb-h.th;
if (skb-ip_summed == CHECKSUM_HW) {
/* 只计算伪首部,TCP报头和TCP数据的累加由硬件完成 */
th-check = ~tcp_v4_check(th, len, inet-saddr, inet-daddr, 0);
skb-csum = offsetof(struct tcphdr, check); /* 校验和值在TCP首部的偏移 */
} else {
/* tcp_v4_check累加伪首部,获取最终的校验和。
* csum_partial累加TCP报头。
* 那么skb-csum应该是TCP数据部分的累加,这是在从用户空间复制时顺便累加的。
*/
th-check = tcp_v4_check(th, len, inet-saddr, inet-daddr,
csum_partial((char *)th, th-doff 2, skb-csum));
}
}
[java] view plaincopy
unsigned csum_partial(const unsigned char *buff, unsigned len, unsigned sum)
{
return add32_with_carry(do_csum(buff, len), sum);
}
static inline unsigned add32_with_carry(unsigned a, unsigned b)
{
asm("addl %2, %0\n\t"
"adcl $0, %0"
: "=r" (a)
: "0" (a), "r" (b));
return a;
}
do_csum()用于计算一段内存的校验和,这里用于累加TCP报头。
具体计算时用到一些技巧:
1. 反码累加时,按16位、32位、64位来累加的效果是一样的。
2. 使用内存对齐,减少内存操作的次数。
[java] view plaincopy
static __force_inline unsigned do_csum(const unsigned char *buff, unsigned len)
{
unsigned odd, count;
unsigned long result = 0;
if (unlikely(len == 0))
return result;
/* 使起始地址为XXX0,接下来可按2字节对齐 */
odd = 1 (unsigned long) buff;
if (unlikely(odd)) {
result = *buff 8; /* 因为机器是小端的 */
len--;
buff++;
}
count = len 1; /* nr of 16-bit words,这里可能余下1字节未算,最后会处理*/
if (count) {
/* 使起始地址为XX00,接下来可按4字节对齐 */
if (2 (unsigned long) buff) {
result += *(unsigned short *)buff;
count--;
len -= 2;
buff += 2;
}
count = 1; /* nr of 32-bit words,这里可能余下2字节未算,最后会处理 */
if (count) {
unsigned long zero;
unsigned count64;
/* 使起始地址为X000,接下来可按8字节对齐 */
if (4 (unsigned long)buff) {
result += *(unsigned int *)buff;
count--;
len -= 4;
buff += 4;
}
count = 1; /* nr of 64-bit words,这里可能余下4字节未算,最后会处理*/
/* main loop using 64byte blocks */
zero = 0;
count64 = count 3; /* 64字节的块数,这里可能余下56字节未算,最后会处理 */
while (count64) { /* 反码累加所有的64字节块 */
asm ("addq 0*8(%[src]), %[res]\n\t" /* b、w、l、q分别对应8、16、32、64位操作 */
"addq 1*8(%[src]), %[res]\n\t" /* [src]为指定寄存器的别名,效果应该等同于0、1等 */
"adcq 2*8(%[src]), %[res]\n\t"
"adcq 3*8(%[src]), %[res]\n\t"
"adcq 4*8(%[src]), %[res]\n\t"
"adcq 5*8(%[src]), %[res]\n\t"
"adcq 6*8(%[src]), %[res]\n\t"
"adcq 7*8(%[src]), %[res]\n\t"
"adcq %[zero], %[res]"
: [res] "=r" (result)
: [src] "r" (buff), [zero] "r" (zero), "[res]" (result));
buff += 64;
count64--;
}
/* 从这里开始,反序处理之前可能漏算的字节 */
/* last upto 7 8byte blocks,前面按8个8字节做计算单位,所以最多可能剩下7个8字节 */
count %= 8;
while (count) {
asm ("addq %1, %0\n\t"
"adcq %2, %0\n"
: "=r" (result)
: "m" (*(unsigned long *)buff), "r" (zero), "0" (result));
--count;
buff += 8;
}
/* 带进位累加result的高32位和低32位 */
result = add32_with_carry(result32, result0xffffffff);
/* 之前始按8字节对齐,可能有4字节剩下 */
if (len 4) {
result += *(unsigned int *) buff;
buff += 4;
}
}
/* 更早前按4字节对齐,可能有2字节剩下 */
if (len 2) {
result += *(unsigned short *) buff;
buff += 2;
}
}
/* 最早之前按2字节对齐,可能有1字节剩下 */
if (len 1)
result += *buff;
/* 再次带进位累加result的高32位和低32位 */
result = add32_with_carry(result32, result 0xffffffff);
/* 这里涉及到一个技巧,用于处理初始地址为奇数的情况 */
if (unlikely(odd)) {
result = from32to16(result); /* 累加到result的低16位 */
/* result为:0 0 a b
* 然后交换a和b,result变为:0 0 b a
*/
result = ((result 8) 0xff) | ((result oxff) 8);
}
return result; /* 返回result的低32位 */
}
[java] view plaincopy
static inline unsigned short from32to16(unsigned a)
{
unsigned short b = a 16;
asm ("addw %w2, %w0\n\t"
"adcw $0, %w0\n"
: "=r" (b)
: "0" (b), "r" (a));
return b;
}
csum_partial_copy_from_user()用于拷贝用户空间数据到内核空间,同时计算用户数据的校验和,
结果保存到skb-csum中(X86_64)。
[java] view plaincopy
/**
* csum_partial_copy_from_user - Copy and checksum from user space.
* @src: source address (user space)
* @dst: destination address
* @len: number of bytes to be copied.
* @isum: initial sum that is added into the result (32bit unfolded)
* @errp: set to -EFAULT for an bad source address.
*
* Returns an 32bit unfolded checksum of the buffer.
* src and dst are best aligned to 64bits.
*/
unsigned int csum_partial_copy_from_user(const unsigned char __user *src,
unsigned char *dst, int len, unsigned int isum, int *errp)
{
might_sleep();
*errp = 0;
if (likely(access_ok(VERIFY_READ, src, len))) {
/* Why 6, not 7? To handle odd addresses aligned we would need to do considerable
* complications to fix the checksum which is defined as an 16bit accumulator. The fix
* alignment code is primarily for performance compatibility with 32bit and that will handle
* odd addresses slowly too.
* 处理X010、X100、X110的起始地址。不处理X001,因为这会使复杂度大增加。
*/
if (unlikely((unsigned long)src 6)) {
while (((unsigned long)src 6) len = 2) {
__u16 val16;
*errp = __get_user(val16, (__u16 __user *)src);
if (*errp)
return isum;
*(__u16 *)dst = val16;
isum = add32_with_carry(isum, val16);
src += 2;
dst += 2;
len -= 2;
}
}
/* 计算函数是用纯汇编实现的,应该是因为效率吧 */
isum = csum_parial_copy_generic((__force void *)src, dst, len, isum, errp, NULL);
if (likely(*errp == 0))
return isum; /* 成功 */
}
*errp = -EFAULT;
memset(dst, 0, len);
return isum;
}
上述的实现比较复杂,来看下最简单的csum_partial_copy_from_user()实现(um)。
[java] view plaincopy
unsigned int csum_partial_copy_from_user(const unsigned char *src,
unsigned char *dst, int len, int sum,
int *err_ptr)
{
if (copy_from_user(dst, src, len)) { /* 拷贝用户空间数据到内核空间 */
*err_ptr = -EFAULT; /* bad address */
return (-1);
}
return csum_partial(dst, len, sum); /* 计算用户数据的校验和,会存到skb-csum中 */
}
java开发要不要学tcpip
需要学tcpip。
编程语言原本是被设计成专门使用在计算机上的,但它们也可以用来定义算法或者数据结构。正是因为如此,程序员才会试图使程序代码更容易阅读.
TCP协议目前是事实上的网络基础。许多更高层的应用协议HTTP,FTP都基于TCP。
TCP协议的学习可以说枯燥无比,尤其是学生阶段,根本不知道用在什么地方,根本不知道重要性是什么。事实上是,基于目前的网络发展和分布式发展,TCP简直就是基础中的基础。许多网络的问题,配置,入侵,防御乃至架构,都涉及到TCP的具体应用及机制。
以下是我总结的TCP学习过程
1. 了解学习TCP协议的重要性和必要性,了解TCP协议为什么被发展出来
推荐这个问题下的各个回答:TCP/IP 协议到底在讲什么?
2. 学习TCP协议的三次握手以及四次挥手,重点了解为什么要三次握手,为什么要四次挥手,在整个过程中状态是如何变迁的。(经典的状态图以及握手挥手图)
a.为什么要三次握手?不是一次,两次或者四次。我们来论证一下,如果只有一次会发生什么情况,a向b发起连接请求,假设b没收到,则b其实完全不知道a发起了请求,而a也完全不知道b收没收到,所以一次握手是不可靠的;如果两次握手呢,a向b发起连接请求,b收到a的请求给a回复一个请求,假设此时a收到了b的回复,a知道了b已经ready了,可b完全不知道a是否ready,有可能a并没有收到b的请求,也有可能收到了,但这些b都完全不知道,所以只是单向的建立了连接;如果是四次握手呢,其实第2次让a知道b ready了,第三次让b知道a也ready了,第四次完全就是多余了,会浪费网络资源。
b.为什么要四次挥手?不是3次?实际上两边连接完全可以分开看,用2次挥手断开其中一边连接,用另外2次挥手断开另一边的连接,最终完成整个连接关闭。之所以这样设计,是因为有可能某一边数据还未传输完,连接还未关闭。因为TCP被设计为全双工协议,可以任何一边单向发送数据。
1. 握手及挥手过程
2. TCP的状态转换图
3. 学习TCP协议是如何保持可靠性设计的。
主要目的是用来参考,以便在其他通信场合时用作架构和设计的参考
1).包应答序列号及包重组。
面临的问题:网络传输中,会出现数据的破坏,丢包,重复,分片混乱等问题。
本质上,要想保证传输的可靠性,则需要对传输的内容进行验证。
a. 对于网络数据的破坏(比如宇宙射线影响偷笑导致发射火箭的数据中某一位从0变为1),采取的策略是丢弃重新发送,以确保不会出现致命的错误。TCP在自身协议中单独划了一块checksum用于这种校验,校验算法本质上是将整块数据通过某个函数映射到16位的校验位上(比如用字符相加的和来校验)
b. 对于数据传输正确,但是分片乱序,重复等问题,或是丢包,采取的策略并非丢弃而是自行进行包重组。
考虑两种情况:第一种情况是某个包缺少了,导致整个数据中间缺了一段1000字节,那么如何通知到对方自己少了哪一段数据;另一种情况是由于网络或者重发机制的原因导致某一个包收到多次,如何把多余的包都排除掉,仅保留已有数据。
TCP在设计时候充分考虑这点,其中SYN和ACK就是用来确保这个过程的,SYN发送的是字节顺序,ACK则应答收到的字节序加1。这样,无论是发送方还是接收方,都可以准确的维护一张发送接收字节的列表。从而可以知道对方还需要哪些字节,或自己已经接收了哪些字节。
如何用JAVA来编写TCP&UDP测试工具
用 Socket java专门负责UDP/TCP传输的。用法和IO流有点相似,毕竟 这个也是流。
在不明白的可以HI我和我交流。
tcp校验java的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于tcp校验码、tcp校验java的信息别忘了在本站进行查找喔。