「tcp校验java」tcp校验码

博主:adminadmin 2023-03-22 20:48:10 495

本篇文章给大家谈谈tcp校验java,以及tcp校验码对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

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的信息别忘了在本站进行查找喔。