socket JAVA 源代码

socket JAVA 源代码,第1张

很久以前做的了,启动程序两次,在单选框中选服务器点连接(一定要先点服务器-连接),在在另外一个界面中选客户端点连接;

import java.awt.Color

import java.awt.Container

import java.awt.Font

import java.awt.event.ActionEvent

import java.awt.event.ActionListener

import java.io.BufferedReader

import java.io.InputStreamReader

import java.io.PrintWriter

import java.net.InetAddress

import java.net.ServerSocket

import java.net.Socket

import java.util.Date

import javax.swing.ButtonGroup

import javax.swing.JButton

import javax.swing.JFrame

import javax.swing.JLabel

import javax.swing.JRadioButton

import javax.swing.JTextArea

import javax.swing.JTextField

public class QQ extends JFrame implements ActionListener{

public static void main(String args[]){

QQ qq=new QQ()

}

String input

ServerSocket ss

Socket s1,s2

PrintWriter pw

BufferedReader br

private server s

private client cc

private JLabel l1,l2,l3,l4,l5

private JRadioButton jb[]=new JRadioButton[2]

private JTextField jf1,jf2,jf3

private JButton j1,j2,j3

private JTextArea ja

public QQ(){

super("聊天")

Container c=getContentPane()

c.setLayout(null)

l1=new JLabel("TCP通信程序"和余如)

l1.setFont(new Font("宋体",Font.BOLD,16))

l1.setBackground(Color.black)

l1.setSize(2000,20)

l1.setLocation(10,10)

c.add(l1)

String str1[]={"服务唤启端","客户端"}

ButtonGroup bg=new ButtonGroup()

for(int x=0x<str1.lengthx++)

{

jb[x]=new JRadioButton(str1[x])

jb[x].setFont(new Font("宋体",Font.BOLD,15))

jb[x].setForeground(Color.black)

jb[x].setSize(80,40)

jb[x].setLocation(10+x*80,37)

bg.add(jb[x])

c.add(jb[x])

}

jb[0].setSelected(true)

l2=new JLabel("连接主机IP")

l2.setFont(new Font("宋体",Font.BOLD,16))

l2.setBackground(Color.black)

l2.setSize(120,20)

l2.setLocation(20, 80)

c.add(l2)

jf1=new JTextField("毁老127.0.0.1")

jf1.setSize(220,30)

jf1.setLocation(120, 80)

c.add(jf1)

jf3=new JTextField("离线")

jf3.setSize(150,30)

jf3.setLocation(280, 40)

c.add(jf3)

l5=new JLabel("连接状态:")

l5.setFont(new Font("宋体",Font.BOLD,16))

l5.setBackground(Color.black)

l5.setSize(120,20)

l5.setLocation(200, 47)

c.add(l5)

j1=new JButton("连接")

j1.setSize(110,20)

j1.setLocation(360,85)

j1.addActionListener(this)

c.add(j1)

l3=new JLabel("接收到的信息")

l3.setFont(new Font("宋体",Font.BOLD,16))

l3.setBackground(Color.black)

l3.setSize(120,20)

l3.setLocation(20, 130)

c.add(l3)

ja=new JTextArea()

ja.setSize(250,200)

ja.setLocation(130, 130)

c.add(ja)

l4=new JLabel("发送信息")

l4.setFont(new Font("宋体",Font.BOLD,16))

l4.setBackground(Color.black)

l4.setSize(120,20)

l4.setLocation(20, 340)

c.add(l4)

jf2=new JTextField("gf")

jf2.setSize(220,30)

jf2.setLocation(120, 340)

c.add(jf2)

j2=new JButton("发送信息")

j2.setSize(110,20)

j2.setLocation(360,350)

j2.addActionListener(this)

c.add(j2)

j3=new JButton("结束连接")

j3.setSize(110,20)

j3.setLocation(360,110)

j3.addActionListener(this)

c.add(j3)

s=new server()

cc=new client()

j3.setEnabled(false)

j2.setEnabled(false)

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)

setSize(500,450)

setVisible(true)

setLocation(300,300)

}

public void actionPerformed(ActionEvent e) {

// TODO 自动生成方法存根

if(e.getSource()==j1)

{

try{

if(jb[0].isSelected()==true)

{

input=""

s.start()

}

else {

input=""

cc.start()

}

}

catch(Exception ee)

{

jf3.setText("发生错误")

}

}

if(e.getSource()==j2)

{

pw.write(jf2.getText()+"\n")

pw.flush()

}

if(e.getSource()==j3)

{

try

{

if(jb[0].isSelected()==true)

{ s1.close()

jf3.setText("离线")

j2.setEnabled(false)

j3.setEnabled(false)

}

else

{

s2.close()

jf3.setText("离线")

j2.setEnabled(false)

j3.setEnabled(false)

}

}

catch (Exception e1) {

// TODO 自动生成 catch 块

}

}

}

class server extends Thread{

public void run(){

try {

j1.setEnabled(false)

jf3.setText("正在连接中@")

ss=new ServerSocket(4000)

s1=ss.accept()

br=new BufferedReader(new InputStreamReader(s1.getInputStream()))

pw=new PrintWriter(s1.getOutputStream(),true)

// bs=new BufferedOutputStream(os)

while(true){

if(ss.isBound()==true){

jf3.setText("连接成功")

j2.setEnabled(true)

j3.setEnabled(true)

break

}

}

while(true)

{

input=br.readLine()

if(input.length()>0){

ja.append(input)

ja.append("\n")

}

}

} catch (Exception e) {

// TODO 自动生成 catch 块

}

}

}

class client extends Thread{

public void run(){

try {

j1.setEnabled(false)

jf3.setText("正在连接中@")

s2=new Socket(InetAddress.getByName(jf1.getText()),4000)

// s2=new Socket()

// s2.connect(new InetSocketAddress(jf1.getText(),21),1000)

br=new BufferedReader(new InputStreamReader(s2.getInputStream()))

pw=new PrintWriter(s2.getOutputStream(),true)

// bs=new BufferedOutputStream(os)

while(true){

if(s2.isConnected()==true){

jf3.setText("连接成功")

j2.setEnabled(true)

j3.setEnabled(true)

break

}

}

input=""

while(true){

input=br.readLine()

if(input.length()>0)

{

ja.append(input)

}

}

} catch (Exception e) {

// TODO 自动生成 catch 块

}

}

}

}

上一篇文章着重的聊了socket服务端的bind,listen,accpet的逻辑。本文来着重聊聊connect都做了什么?

如果遇到什么问题,可以来本文 https://www.jianshu.com/p/da6089fdcfe1 下讨论

当服务端一切都准备好了。客户端就会尝试的通过 connect 系统调用,尝试的和服务端建立远程连接。

首先校验当前socket中是否有正确的目标地址。然后获取IP地址和端口调用 connectToAddress 。

在这个方法中,能看到有一个 NetHooks 跟踪socket的调用,也能看到 BlockGuard 跟踪了socket的connect调用。因此可以hook这两个地方跟踪socket,不过很少用就是了。

核心方法是 socketConnect 方法,这个方法就是调用 IoBridge.connect 方法。同理也会调用到jni中。

能看到也是调用了 connect 系统调用。

文件:/ net / ipv4 / af_inet.c

在这个方法中做的事情如下:

注意 sk_prot 所指向的方法是, tcp_prot 中 connect 所指向的方法,也就是指 tcp_v4_connect .

文件:/ net / ipv4 / tcp_ipv4.c

本质上核心任务有三件:

想要能够理解下文内容,先族汪要明白什么是路由表

路由表分为两大类:

每个路由器都有一个路由表(RIB)和转发表 (fib表),路由表用于决策路由,转发表决策转发分组。下文会接触到这两种表。

这两个表有什么区别呢?

网上虽然给了如下的定义:

但实际上在Linux 3.8.1中并没有明确的区分。整个路由相关的逻辑都是使用了fib转发表承担的。

先来看看几个和FIB转发表相关的核心结构体:

熟悉Linux命令朋友一定就能认出这里面大部分的字段都可以通过route命令查找到。

命令执行结果如下:

在这扮余route命令结果的字段实际上都对应上了结构体中的字段含义:

知道路由表的的内容后。再来FIB转发表的内容。实际上从下面的源码其实可以得知,路由表的获取,实际上是先从fib转发表的路由字典树获取到后在同感加工获得路由表对象。

转发表的内容就更加简单

还记得在之前总结的ip地址的结构吗?

需要进行一次tcp的通信,意味着需要把ip报文准备好。因此需要决定源ip地址和目标IP地址。目标ip地址在之前通过netd查询到了,此时需要得到本地发送的源ip地址。

然而在实际情况下,往兆缺仔往是面对如下这么情况:公网一个对外的ip地址,而内网会被映射成多个不同内网的ip地址。而这个过程就是通过DDNS动态的在内存中进行更新。

因此 ip_route_connect 实际上就是选择一个缓存好的,通过DDNS设置好的内网ip地址并找到作为结果返回,将会在之后发送包的时候填入这些存在结果信息。而查询内网ip地址的过程,可以成为RTNetLink。

在Linux中有一个常用的命令 ifconfig 也可以实现类似增加一个内网ip地址的功能:

比如说为网卡eth0增加一个IPV6的地址。而这个过程实际上就是调用了devinet内核模块设定好的添加新ip地址方式,并在回调中把该ip地址刷新到内存中。

注意 devinet 和 RTNetLink 严格来说不是一个存在同一个模块。虽然都是使用 rtnl_register 注册方法到rtnl模块中:

文件:/ net / ipv4 / devinet.c

文件:/ net / ipv4 / route.c

实际上整个route模块,是跟着ipv4 内核模块一起初始化好的。能看到其中就根据不同的rtnl *** 作符号注册了对应不同的方法。

整个DDNS的工作流程大体如下:

当然,在tcp三次握手执行之前,需要得到当前的源地址,那么就需要通过rtnl进行查询内存中分配的ip。

文件:/ include / net / route.h

这个方法核心就是 __ip_route_output_key .当目的地址或者源地址有其一为空,则会调用 __ip_route_output_key 填充ip地址。目的地址为空说明可能是在回环链路中通信,如果源地址为空,那个说明可能往目的地址通信需要填充本地被DDNS分配好的内网地址。

在这个方法中核心还是调用了 flowi4_init_output 进行flowi4结构体的初始化。

文件:/ include / net / flow.h

能看到这个过程把数据中的源地址,目的地址,源地址端口和目的地址端口,协议类型等数据给记录下来,之后内网ip地址的查询与更新就会频繁的和这个结构体进行交互。

能看到实际上 flowi4 是一个用于承载数据的临时结构体,包含了本次路由 *** 作需要的数据。

执行的事务如下:

想要弄清楚ip路由表的核心逻辑,必须明白路由表的几个核心的数据结构。当然网上搜索到的和本文很可能大为不同。本文是基于LInux 内核3.1.8.之后的设计几乎都沿用这一套。

而内核将路由表进行大规模的重新设计,很大一部分的原因是网络环境日益庞大且复杂。需要全新的方式进行优化管理系统中的路由表。

下面是fib_table 路由表所涉及的数据结构:

依次从最外层的结构体介绍:

能看到路由表的存储实际上通过字典树的数据结构压缩实现的。但是和常见的字典树有点区别,这种特殊的字典树称为LC-trie 快速路由查找算法。

这一篇文章对于快速路由查找算法的理解写的很不错: https://blog.csdn.net/dog250/article/details/6596046

首先理解字典树:字典树简单的来说,就是把一串数据化为二进制格式,根据左0,右1的方式构成的。

如图下所示:

这个过程用图来展示,就是沿着字典树路径不断向下读,比如依次读取abd节点就能得到00这个数字。依次读取abeh就能得到010这个数字。

说到底这种方式只是存储数据的一种方式。而使用数的好处就能很轻易的找到公共前缀,在字典树中找到公共最大子树,也就找到了公共前缀。

而LC-trie 则是在这之上做了压缩优化处理,想要理解这个算法,必须要明白在 tnode 中存在两个十分核心的数据:

这负责什么事情呢?下面就简单说说整个lc-trie的算法就能明白了。

当然先来看看方法 __ip_dev_find 是如何查找

文件:/ net / ipv4 / fib_trie.c

整个方法就是通过 tkey_extract_bits 生成tnode中对应的叶子节点所在index,从而通过 tnode_get_child_rcu 拿到tnode节点中index所对应的数组中获取叶下一级别的tnode或者叶子结点。

其中查找index最为核心方法如上,这个过程,先通过key左移动pos个位,再向右边移动(32 - bits)算法找到对应index。

在这里能对路由压缩算法有一定的理解即可,本文重点不在这里。当从路由树中找到了结果就返回 fib_result 结构体。

查询的结果最为核心的就是 fib_table 路由表,存储了真正的路由转发信息

文件:/ net / ipv4 / route.c

这个方法做的事情很简单,本质上就是想要找到这个路由的下一跳是哪里?

在这里面有一个核心的结构体名为 fib_nh_exception 。这个是指fib表中去往目的地址情况下最理想的下一跳的地址。

而这个结构体在上一个方法通过 find_exception 获得.遍历从 fib_result 获取到 fib_nh 结构体中的 nh_exceptions 链表。从这链表中找到一模一样的目的地址并返回得到的。

文件:/ net / ipv4 / tcp_output.c


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12234653.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-22
下一篇 2023-05-22

发表评论

登录后才能评论

评论列表(0条)

保存