-
Java Socket 网络编程的过程是怎样的?
2020-03-23 22:02:58Socket 网络编程的过程是怎样的?解答。。。
1. 问题
- Socket 网络编程的过程是怎样的?
2. 解答
- 客户端向服务器端发送连接请求后,就被动地等待服务器的响应。典型的 TCP 客户端要进行下面 3 步操作:
- 步骤1:创建一个 Socket 实例,通过构造函数与指定的远程主机和端口建立一个 TCP 链接;
- 步骤2:通过套接字的 I/O 流与服务器端通信;
- 步骤3:使用 Socket 类的 close() 方法关闭链接;
- 服务器端的工作室建立一个通信终端,并被动地等待客户端的链接。典型的 TCP 服务器端执行如下两步操作:
- 步骤1:创建一个 ServerSocket 实例并制定本地端口,用来监听客户端在改端口发送的 TCP 链接请求;
- 步骤2:重复执行以下操作:
- 调用 ServerSocket 的 accept() 方法以获取客户端连接,并通过其返回值创建一个 Socket 实例;
- 为返回的 Socket 实例开启新的线程,并使用返回的 Socket 实例的 I/O 流与客户端通信;
- 通信完成后,使用 Socket 类的 close() 方法关闭该客户端的套接字链接;
推荐阅读:Java TCP 网络编程 >>>
-
java 网络编程发展过程以及nio的特点
2020-07-07 16:15:16背景: 省分短信发送每天都差不多要1000W条上下,遇到特殊...TCP/IP网络协议: 网上很多有关这个协议的解释,自行google,下面是简单的理解记忆: tcp/ip的3次握手, 简单来说就是第一次我连接你给你一个标识SYN,你给我返回.背景:
省分短信发送每天都差不多要1000W条上下,遇到特殊节假日和政府通告时量会更大!boss系统中存放的是短信发送内容,而真正完成发送短信指令动作是的华为方做的短厅,这么大的通信量选择了netty来完成数据传输并自定义了一套基于netty的SGIP协议进行通信;
省分boss系统—>短信营业厅();
基本知识
TCP/IP网络协议:
网上很多有关这个协议的解释,自行google,下面是简单的理解记忆:
tcp/ip的3次握手, 简单来说就是第一次我连接你给你一个标识SYN,你给我返回SYN并给一个新的ACK标记我,然后我再把ACK给你,
这样证明我们之前传东西是可靠,的然后就正式传数据了
图片来自网上
tcp/ip的4次挥手断开,相当于,你给我一个ACK我给你一个FIN,然后再次彼此交换确认,OK就可以结束通信了
java的socket就是对tcp/ip的一种实现
基础代码,:
一个简单的socket实现tcp/ip的样例,后面的BIO/NIO/AIO都是基本上于这个例子进行变化
client端:import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; public class Client { final static String ADDRESS = "127.0.0.1"; final static int PORT = 7788; public static void main(String[] args) Socket socket = null; BufferedReader in = null; PrintWriter out = null; socket = new Socket(ADDRESS, PORT); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = new PrintWriter(socket.getOutputStream(), true); //向服务器端发送数据 out.println("接收到客户端的请求数据..."); out.println("接收到客户端的请求数据1111..."); String response = in.readLine(); System.out.println("Client: " + response); ...
Server端:
public class Server { final static int PROT = 7788; public static void main(String[] args) { ServerSocket server = null; server = new ServerSocket(PROT); System.out.println(" server start .. "); //进行阻塞 Socket socket = server.accept(); //新建一个线程执行客户端的任务 new Thread(new ServerHandler(socket)).start(); } } ServerHandler.java 如下: public class ServerHandler implements Runnable{ private Socket socket ; public ServerHandler(Socket socket){ this.socket = socket; } @Override public void run() { BufferedReader in = null; PrintWriter out = null; try { in = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); out = new PrintWriter(this.socket.getOutputStream(), true); String body = null; while(true){ body = in.readLine(); if(body == null) break; System.out.println("Server :" + body); out.println("服务器端回送响的应数据."); } } }
上面这个代码很简单转换成图型说明就是web浏览器发一个请求过来,web服务器就要new 一个线程来处理这个请求,这是传统的请求处理模型,这也就引来一个很大的问题,当请求越多,服务器端的启用线程也要越多,我们都知道linux(window)的文件句柄数有是限的,默认是1024,当然可以修改,上限好像是65536 ,(一个柄也相当于一个socket也相当于一个thread,linux查看文件句柄Unlimit -a) 其实在实际当中只要并发到1000上下响应请求就会很慢了,所以这种模型是有问题的,这种也就是同步阻塞IO编程(JAVA BIO)
网上查的定义:
同步阻塞IO(JAVA BIO):
同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销.BIO—>NIO—->AIO的发展历程
上面的BIO是有问题的,也就是出现在jdk1.4那个古时代的产物,现在当然这要改进下,上面的问题无非就是服务器端的线程无限制的增长才会导致服务器崩掉,那我们就对征下药,加个线程池限制线程的生成,又可以复用空闲的线程,是的,在jdk1.5也是这样做的,下面是服务器端改进后的代码:
public class Server { final static int PORT = 7788; public static void main(String[] args) { ServerSocket server = null; BufferedReader in = null; PrintWriter out = null; server = new ServerSocket(PORT); System.out.println("server start"); Socket socket = null; HandlerExecutorPool executorPool = new HandlerExecutorPool(50, 1000); while(true){ socket = server.accept(); executorPool.execute(new ServerHandler(socket)); } } } HandlerExecutorPool.java import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class HandlerExecutorPool { private ExecutorService executor; public HandlerExecutorPool(int maxPoolSize, int queueSize){ this.executor = new ThreadPoolExecutor( Runtime.getRuntime().availableProcessors(), maxPoolSize, 120L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize)); } public void execute(Runnable task){ this.executor.execute(task); } }
Jdk1.5创造了一个假的nio 用一个HanderExecutorPool来限定了线程数量,但只是解决了服务器端不会因为并发太多而死掉,但解决不了并发大而响应越来越慢的,到时你也会怀疑你是不是真的用了一个假的nio!!!!!!!
为了解决这个问题,就要用三板斧来解决!别急,要解决一个诸葛亮,你必先要造三个臭皮匠,先引入3个NIO相关概念先!
1> Buffer 缓冲区
难用的buffer是一个抽象的对象,下面还有ByteBuffer,IntBuffer,LongBuffer等子类,相比老的IO将数据直接读/写到Stream对象,NIO是将所有数据都用到缓冲区处理,它本质上是一个数组,提供了位置,容量,上限等操作方法,还是直接看代码代码来得直接
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 7788);//创建连接的地址 SocketChannel sc = null;//声明连接通道 ByteBuffer buf = ByteBuffer.allocate(1024);//建立缓冲区 sc = SocketChannel.open();//打开通道 sc.connect(address);//进行连接 while(true){ //定义一个字节数组,然后使用系统录入功能: byte[] bytes = new byte[1024]; System.in.read(bytes); buf.put(bytes);//把数据放到缓冲区中 buf.flip();//对缓冲区进行复位 sc.write(buf);//写出数据 buf.clear();//清空缓冲区数据 }
2>Channel 通道
如自来水管一样,支持网络数据从Channel中读写,通道写流最大不同是通道是双向的,而流是一个方向上移动(InputStream/OutputStream),通道可用于读/写或读写同时进行,它还可以和下面要讲的selector结合起来,有多种状态位,方便selector去识别. 通道分两类,一:网络读写(selectableChannel),另一类是文件操作(FileChannel),我们常用的是上面例子中的网络读写!
3>Selector 多路复用选择器
它是神一样存在的东西,多路复用选择器提供选择已经就绪的任务的能力,也就是selector会不断轮询注册在其上的通道(Channel),如果某个通道发生了读写操作,这个通道处于就绪状态,会被selector轮询出来,然后通过selectionKey可以取得就绪的Channel集合,从而进行后续的IO操作.
一个多路复用器(Selector)可以负责成千上万个Channel,没有上限,这也是JDK使用epoll代替了传统的selector实现,获得连接句柄没有限制.这也意味着我们只要一个线程负责selector的轮询,就可以接入成千上万个客户端,这是JDK,NIO库的巨大进步.来张精心整好的图
这个学习进到深水区了,注意罗,下面是服务器端的代码,上面例子代码是client端的,看里面的注解,如果还不明白,多看几次,代码是可运行的,记得要jdk1.7以上版本,多运行,自己意会下,我也只帮到这了!
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; public class Server implements Runnable{ //1 多路复用器(管理所有的通道) private Selector seletor; //2 建立缓冲区 private ByteBuffer readBuf = ByteBuffer.allocate(1024); //3 private ByteBuffer writeBuf = ByteBuffer.allocate(1024); public Server(int port){ try { //1 打开路复用器 this.seletor = Selector.open(); //2 打开服务器通道 ServerSocketChannel ssc = ServerSocketChannel.open(); //3 设置服务器通道为非阻塞模式 ssc.configureBlocking(false); //4 绑定地址 ssc.bind(new InetSocketAddress(port)); //5 把服务器通道注册到多路复用器上,并且监听阻塞事件 ssc.register(this.seletor, SelectionKey.OP_ACCEPT); System.out.println("Server start, port :" + port); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { while(true){ try { //1 必须要让多路复用器开始监听 this.seletor.select(); //2 返回多路复用器已经选择的结果集 Iterator<SelectionKey> keys = this.seletor.selectedKeys().iterator(); //3 进行遍历 while(keys.hasNext()){ //4 获取一个选择的元素 SelectionKey key = keys.next(); //5 直接从容器中移除就可以了 keys.remove(); //6 如果是有效的 if(key.isValid()){ //7 如果为阻塞状态 if(key.isAcceptable()){ this.accept(key); } //8 如果为可读状态 if(key.isReadable()){ this.read(key); } //9 写数据 if(key.isWritable()){ //this.write(key); //ssc } } } } catch (IOException e) { e.printStackTrace(); } } } private void write(SelectionKey key){ //ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); //ssc.register(this.seletor, SelectionKey.OP_WRITE); } private void read(SelectionKey key) { try { //1 清空缓冲区旧的数据 this.readBuf.clear(); //2 获取之前注册的socket通道对象 SocketChannel sc = (SocketChannel) key.channel(); //3 读取数据 int count = sc.read(this.readBuf); //4 如果没有数据 if(count == -1){ key.channel().close(); key.cancel(); return; } //5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位) this.readBuf.flip(); //6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据 byte[] bytes = new byte[this.readBuf.remaining()]; //7 接收缓冲区数据 this.readBuf.get(bytes); //8 打印结果 String body = new String(bytes).trim(); System.out.println("Server : " + body); // 9..可以写回给客户端数据 } catch (IOException e) { e.printStackTrace(); } } private void accept(SelectionKey key) { try { //1 获取服务通道 ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); //2 执行阻塞方法 SocketChannel sc = ssc.accept(); //3 设置阻塞模式 sc.configureBlocking(false); //4 注册到多路复用器上,并设置读取标识 sc.register(this.seletor, SelectionKey.OP_READ); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { new Thread(new Server(7788)).start();; } }
如果你理解了Java NIO ,下面讲的netty也是水到渠成的事,只想说,深水区已过了!
差点忘记还要补下AIO的,这个比NIO先进的技术,最终实现了
netty
这是神一样存在的java nio框架, 这个偏底层的东西,可能你接触较少却又无处不在,比如:
在业界有一篇无法超越的netty入门文章,我也没这个能力超越,只能双手奉上,你们好好研读,必然学有所成!
http://ifeve.com/netty5-user-guide/还有杭州华为的李林锋写的 Netty权威指南 ,我是买了一本,不知如何评论好,中等吧!
刚好,我这边要修改的项目也是华为的………………
未完,先发…………………………………..
————————————————
版权声明:本文为CSDN博主「六楼外的风景」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yangfanend/article/details/55100327 -
网络编程创建socket过程
2020-11-26 21:21:08** 网络编程接受连接请求,创建套接字的过程 ** 1.调用socket函数创建套接字 2. 调用bind函数分配IP地址和端口号 3. 调用listen函数转为可接收请求状态 4. 调用accept函数受理连接请求。展开全文 -
python网络编程之网络通信过程
2018-10-19 19:37:15python网络编程之网络通信过程 该文档仅用作学习笔记,转载请表明出处 UDP广播 只有UDP才有广播,TCP没有广播。 一台电脑发出的数据,所有电脑可以收到,这就叫广播数据。 单播,多播,广播 单播:点对点 多播:一...python网络编程之网络通信过程
该文档仅用作学习笔记,转载请表明出处
UDP广播
- 只有UDP才有广播,TCP没有广播。
- 一台电脑发出的数据,所有电脑可以收到,这就叫广播数据。
- 单播,多播,广播
- 单播:点对点
- 多播:一对多
- 广播 :一对所有
- 使用广播的前提:需要发送广播数据的套接字进行修改设置,否则不能发送广播数据。setsockopu()方法。
- 192.168.1.0 -----》网络号
- 192.168.1.255 -----》广播地址
案例v07: udp广播
''' udp广播案例 ''' from socket import * import sys udpSocket = socket(AF_INET,SOCK_DGRAM) destAddr = ('<broadcast>',8080) udpSocket.setsockopt(SOL_SOCKET,SO_BROADCAST,1) udpSocket.sendto('i love you'.encode(),destAddr) while True: print(udpSocket.recvfrom(2048).decode()) udpSocket.close()
Packet Tracer介绍&安装
Packet Tracer介绍
- Packet Tracer是由Cisco(著名网络公司 - 思科)公司发布的一个辅助学习工具。为学习思科网络课程的初学者去设计,配置,排除网络故障提供了网络模拟环境。
- 用户可以在软件的图形用户界面上直接使用拖曳的方式建立网络拓扑,并可提供数据包在网络中行进的详细处理过程,观察网络实时运行情况。
Packet Tracer安装
链接:https://pan.baidu.com/s/1H7GnYQLr4KSXd0U64RaV7w 提取码:dgrz
- 你可以在Packet Tracer官网上直接下载,当然也可以通过上方链接下载
- 安装方法只需要你在 www.baidu.com 上搜索“如何安装Packet Tracer”就可以
两台电脑组网
如图所示:
将ip地址,子网掩码,默认网关分别设置为:
pc0分别ping自己和ping pc1:
网络掩码,3台电脑使用hub组网
- 网络掩码和IP地址按位与得到该主机网络号,网络号相同可以直接通信。
- hub 集线器
如图:
PC0,PC1设置不用改变,将PC2设置成:
此时使用PC3 ping PC1可以ping通:
集线器和交换机
交换机配置的局域网:
arp和icmp
- 两台电脑通信的前提是:在同一网络号内。
- 链接多台电脑的hub集线器有什么作用:实现多台电脑的连接,组成一个小型局域网。
- 集线器和交换机的区别:集线器收到的消息都是广播,交换机有些时候不是广播,效率比较高。
- ping:使用的是icmp协议。
- 实际地址(MAC地址):网卡有一个序列号:前3组表示厂商地址,后3组确定一个厂商中生产的网卡的序列号。(6个字节)
- arp:命令插卡
- mac:地址信息
路由器的作用以及组网
- 路由器又称网关设备是用于连接多个逻辑上分开的网络。
- 所谓逻辑网络是代表一个单词的网络或者一个子网,当数据从一个子网传输到另一个子网时,可通过路由器的路由功能来完成。
- 具有判断网络地址和选择IP路径的功能。
- TCP/IP规定不是一个网络号不可以通信,哪怕距离再近。
- 链接不同网络使之可以通信的设备叫做路由器。
网络通信过程中mac地址以及ip的不同
- mac地址:在两个设备之间通信时在变化(标记实际转发数据时的设备地址)
- ip地址:在整个通信的过程中都不会发生任何变化(标记逻辑上的地址)
- netmask:和ip地址一起来确定网络号
- 默认网关:发送的ip不在同一个网段内,那么就会把这个数据转发给默认网关。
访问百度的过程
- 第一步:先要解析出www.baidu.com对应的ip地址
- 1.先知道默认网关的mac(使用arp获取默认网关的mac地址)
- 2:组织数据发送给默认网关(ip还是DNS服务器的ip,但是mac地址是默认网关的mac地址)
- 3:默认网关拥有转发数据的能力,把数据转发给路由器
- 4:路由器根据自己的路由协议,来选择一个合适的较快的路径,转发数据给目的网管
- 5:目的网关(dns服务器所在的网关),把数据转发给dns服务器。
- 6:dns服务器查询解析出baidu.com对应的ip地址,并把它原路返回给请求域名的客户端。
- 第二步:得到百度对应的ip地址之后,会发送tcp的3次握手,进行连接。
- 第三步:使用http协议发送请求数据给web服务器
- 第四步:web服务器收到数据请求之后,通过查询自己的服务器得到相应的结果原路返回给浏览器
- 第五步:浏览器接收到数据后,通过浏览器自己的渲染功能来显示这个网页
- 第六步:浏览器关闭tcp连接,即四次挥手。
-
网络编程的实践过程中总结出来的一些经验
2012-03-13 16:14:55本文是我在进行MS-Windows、HP-Unix网络编程的实践过程中总结出来的一些经验,仅供大家参考。本文所谈到的Socket函数如果没有特别说明,都是指的Windows Socket API。 一、WSAStartup函数 int WSAStartup( WORD ... -
网络编程的实践过程中总结出来的一些经验(转)
2016-04-28 16:11:08本文是我在进行MS-Windows、HP-Unix网络编程的实践过程中总结出来的一些经验,仅供大家参考。本文所谈到的Socket函数如果没有特别说明,都是指的Windows Socket API。 一、WSAStartup函数 int WSAStartup( WORD... -
基于Matlab的BP神经网络编程过程
2014-12-09 15:27:18(1)对样本集进行归一化 确定输入样本和输出样本,并对它们进行归一化,将输入...在样本集确定之后,即可进行网络的结构设计,在Matlab中一般使用newff创建函数,它不但创建了网络对象,还自动初始化网络的权重和阈值。 -
linux网络编程UDP过程遇到的问题记录
2011-03-28 20:20:00几天来写的代码,一经测试,竟然最基本的练级都没有成功。甚是失望~整了两天的时间终于解决了该些问题。现在记录下来。 first of all,关于sendto()和recvfrom()的参数问题。recvfrom()的最后的... -
【网络编程】Socket网络编程基础
2019-06-12 21:41:50文章目录网络编程概述Socket...通过操作相应Api调度计算机硬件资源,并利用传输管道(网线)进行数据交换的过程 更为具体的涉及:网络模型、套接字、数据包 网络模型 – 对应关系 具体细节看一看我的 专栏《计算机... -
网络数据到底怎样的传输过程?什么是网络编程?一文教你简单入门 linux下socket网络编程 —— 客户端篇...
2020-08-17 11:50:24socket网络编程入门篇之客户端socket网络编程入门篇之客户端1、网络编程入门篇——前章1.1、网络数据传输过程1.2、什么是socket套接字1.3、网络结构体1.4、网络字节序 (Network Byte Order)和本机转换2、网络编程... -
网络编程 数据的封装与解封装过程
2020-01-03 14:25:05文章目录数据封装过程数据解封装过程 数据封装过程 数据解封装过程 参考文章:封装与解封装 -
网络编程
2019-10-06 16:15:34文章目录网络编程网络编程的三要素IP地址:端口发送端接收端 网络编程 计算机的基础知识 1.网络模型 5层模型和七层模型 2.网络编程的三要素 ip 端口 协议 3.Tcp连接过程 三次握手和四次挥手 网络模型 详情... -
java学习之简单的网络编程
2017-08-04 08:37:40在学习网络编程的过程中,简单写了一下客户端和服务器的小demo package Ray.net2; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; ... -
网络编程系列一网络编程中的基本概念
2017-03-25 22:41:18本次文章是一个序列,是对工作以及学习中关于网络编程的一个总结,本序列将从一个最简单的网络程序开始介绍当前网络编程的递进过程,有兴趣的朋友可以去阅读关于C10K问题的文档。在开始代码介绍之前,本文先介绍一些... -
linux网络编程学习笔记(6)——TCP连接状态的多种判断方法
2020-01-10 14:06:01在TCP网络编程模型中,无论是客户端还是服务端,在网络编程的过程中都需要判断连接的对方网络状态是否正常。在linux系统中,有很多种方式可以判断连接的对方网络是否已经断开。 通过错误码和信号判断 通过select... -
Unix网络编程开篇
2015-08-22 22:02:42最近想学习下后端,网上搜了搜,最后决定还是从《UNIX网络编程》这本书...本系列博客只是对我在学习UINIX网络编程的过程中的记录,把我认为比较重要的记录下来,高手在看的过程中发现我有理解不对的地方请不吝指正! -
Android网络编程(三) 之 网络请求握手过程
2019-07-17 16:58:511 一次网络请求的过程 我们平常在浏览器输入一个网址回车后经过了大概不到一秒时间的网络请求便可展示出相应的页面,其实这样一次完整的网络请求过程要经过好几个步骤: 第一步:DNS解析IP地址; 第二步:TCP三次... -
JAVA深度分析解析网络编程Socket过程及方法
2018-10-23 19:53:31OSI 一、应用层TCP/IP模型 1.会话层 2.表示层 3.应用层 二、传输层 三、网络层 四、网络接口 1.数据链路程 ... TP地址, TP地址就悬计算机在网络上的唯一维对地址 1相当干人居住的房子的地方 三、计... -
java网络socket编程过程中遇到的问题
2013-08-01 06:58:36运行代码 import java.io.*; import java.net.*; public class InetAddressDemo { public static void main(String[] args) { ...第二次以及后来程序依旧显示上面的错误,求大神指点一下!! -
Linux 网络编程及底层实现过程
2013-11-13 16:07:39也许学过从事过网络编程的人都知道socket是什么,表示什么?socket的英文原义是“孔”或“插座。但我们用网络术语将它称为“套接字”(见Linux网络编程),但是我习惯叫“套接口”,可能是受Unix网络编程的影响。... -
网络编程中Socket和TCP的断开过程—学习笔记
2019-07-04 12:45:21目录一小段引言什么是socket网络编程功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants... -
Linux 网络编程——TCP 通信过程中各步骤的状态
2018-10-23 16:46:34那么你就一定要对这张图的每一个状态,及转换的过程有深刻的认识,不能只停留在一知半解之中。下面对这张图的11种状态详细解析一下,以便加强记忆!不过在这之前,先回顾一下 TCP 建立连接的三次握手过程,以及关闭... -
初识 - TCP/IP网络编程过程中遇到的问题(持续更新中)
2020-12-03 16:00:02刚开始学习Windows下的TCP/IP网络编程,记录一下编程可能会遇到的一些小问题。 1、无法打开源文件sys/socket.h 在Windows下面被替换成了<WinSock.h> 2、未定义标识符socklen_t socklen_t类型在Windows中的WS2... -
创建CSocket的过程,MFC网络编程
2014-04-16 11:24:43 -
Linux网络编程
2018-06-17 22:00:11本书介绍Linux系统下网络编程的开发过程,主要是c语言为主。