这可能是最容易入门的socket教程了
前言:
如今,网络编程已然成为了一个后端开发工程师需要具备的核心技能之一。因此,该博客力求提供最简单、通俗的描述方式,来描绘网络编程中常见的知识点,同时附带代码示例,后期会加上具体的抓包分析,实际项目、框架案例,希望可以和大家共同探索网络世界。
什么是socket?
socket在网络编程中的位置
- TCP是面向连接的、UDP是面向无连接的,所谓面向连接,指的就是通讯双方会维护一组状态,保证连接的可靠性。
- TCP的数据包结构相对复杂,而UDP则相对简单。无论是TCP还是UDP,他们的包头上都应该有端口号和目标端口号,TCP的包头上有序号(顺序),确认序号(不丢包)、窗口大小(流量控制 & 拥塞控制)、状态码(FIN ACK SYN)等。TCP和UDP的包头如下图。
- TCP数据传输方式是基于数据流的,而UDP则是基于数据报。
udp包头

tcp包头
如何使用socket:
代码(TCP):
服务器:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket; /**
* 服务器.
*
* @author jialin.li
* @date 2019-12-10 19:45
*/
public class TcpServer {
public static void main(String[] args) throws IOException {
int port = 8099;
ServerSocket connectionSocket = new ServerSocket(port);
// 监听端口,accept为阻塞方法
System.out.println("Get socket successfully, wait for request...");
Socket communicationSocket = connectionSocket.accept();
// 获取输入流,读取数据
InputStream inputStream = communicationSocket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String message;
while ((message = bufferedReader.readLine()) != null) {
System.out.printf("get message from client : %s",message);
}
communicationSocket.shutdownInput();
// 获取输出流,返回结果
OutputStream outputStream = communicationSocket.getOutputStream();
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
bufferedWriter.write("I got your message and the communication is over.");
bufferedWriter.flush();
communicationSocket.shutdownOutput();
// 关闭资源
bufferedWriter.close();
bufferedReader.close();
}
}
客户端:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket; /**
* 客户端.
*
* @author jialin.li
* @date 2019-12-10 22:30
*/
public class TcpClient {
public static void main(String[] args) throws IOException {
// 指定ip 端口,创建socket
String host = "127.0.0.1";
int port = 8099;
Socket communicationSocket = new Socket(host, port);
// 获取输出流,写入数据
OutputStream outputStream = communicationSocket.getOutputStream();
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
bufferedWriter.write("hello world");
bufferedWriter.flush();
communicationSocket.shutdownOutput();
// 获取输入流,读取服务器返回信息
InputStream inputStream = communicationSocket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String message;
while ((message = bufferedReader.readLine()) != null) {
System.out.printf("get message from server : %s", message);
}
communicationSocket.shutdownInput();
// 关闭资源
bufferedWriter.close();
bufferedReader.close();
}
}
执行结果:
server:
Get socket successfully, wait for request...
get message from client : hello world
client:
get message from server : I got your message and the communication is over.
outputStream.close与shutdownOutput的区别:
这里只对outputStream进行分析,inputStream与之相同。
可以看出,上述代码,我们在执行完输入/输出动作之后,会调用一个shutdownInput/shutdownOutput方法,这个方法有什么作用呢?可不可以用close方法替代?
首先,我们阅读jdk中关于该方法的doc注释:
Disables the output stream for this socket.
For a TCP socket, any previously written data will be sent
followed by TCP's normal connection termination sequence.
If you write to a socket output stream after invoking
shutdownOutput() on the socket, the stream will throw
an IOException.
大体意思是说,该方法会禁用socket的输出流,对于基于TCP协议的Socket,任何在方法执行之前发送数据,都可以被正常发送,如果在这个方法执行后发送数据,就会抛出一个IO异常。通过该方法关闭流,Socket连接不会收到影响,但是如果我们直接使用close方法关闭流,那么Socket连接也会随之关闭。接下来我们来测试一下,在Server中用inputStream.close来代替socket.shutdownInput方法。
结果是由于inputStream.close提前关闭了socket,导致服务器在输出数据的时,socket.getOutputStream方法抛出异常:java.net.SocketException: Socket is closed
为什么我们每次调用BufferedWrite的write方法,都要调用flush方法:
这个问题不属于网络编程的范畴,但却是我们在写socket程序时,很容易犯的一个错误。bufferedWrite是字符缓存流,它的原理其实很简单,在内存中设置一个缓存区,将原本逐个发送的字符缓存起来,批量发送(有很多框架也采用了这种思想,比如kafka的批量发送),flush方法是手动的将我们缓存区中的数据刷出。缓存区中的数据,将会在流关闭之前,进行flush。但是由于我们在发送数据之后,调用了shutdownOutput方法,导致最后close的时候,没办法将缓存区中的数据flush,因此会抛出一个写入失败的异常:java.net.SocketException: Broken pipe (Write failed)。
UDP是基于数据报的,因此不需要每对连接都建立一组socket,而是只要有一个socket,就能与多个客户端通讯,所以只需要通过创建数据报,然后通过socket发送即可,具体步骤如下:

代码(UDP):
服务器:
import java.net.DatagramPacket;
import java.io.IOException;
import java.net.DatagramSocket; /**
* 服务器.
*
* @author jialin.li
* @date 2019-12-10 19:45
*/
public class UdpServer {
public static void main(String[] args) throws IOException {
// 监听端口,阻塞方法
int port = 8099;
DatagramSocket socket = new DatagramSocket(port);
// 创建数据报,用于接收客户端发送的数据
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
// 接收客户端发送的数据
System.out.println("Get socket successfully, wait for request...");
socket.receive(packet);
String message = new String(data, 0, packet.getLength());
System.out.printf("get message from client : %s", message); // 向客户端发送数据
byte[] data2 = "I got your message and the communication is over.".getBytes();
DatagramPacket packet2 = new DatagramPacket(data2, data2.length, packet.getAddress(), packet.getPort());
socket.send(packet2);
socket.close();
}
}
客户端:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress; /**
* 客户端.
*
* @author jialin.li
* @date 2019-12-11 11:32
*/
public class UdpClient {
public static void main(String[] args) throws IOException {
// 封装数据报:ip、端口、数据
InetAddress ip = InetAddress.getByName("127.0.0.1");
int port = 8099;
byte[] data = "hello world".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, ip, port);
// 创建socket,发送数据报
DatagramSocket socket = new DatagramSocket();
socket.send(packet); // 读取服务器返回信息
byte[] data2 = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(data2, data2.length);
socket.receive(packet2);
// 读取数据
String message = new String(data2, 0, packet2.getLength());
System.out.printf("get message from server : %s", message);
//关闭资源
socket.close();
}
}
执行结果:
server:
Get socket successfully, wait for request...
get message from client : hello world
client:
get message from server : I got your message and the communication is over.
这可能是最容易入门的socket教程了的更多相关文章
- Spring Boot 2.0 的快速入门(图文教程)
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! Spring Boot 2.0 的快速入门(图文教程) 大家都 ...
- MyBatis学习总结-MyBatis快速入门的系列教程
MyBatis学习总结-MyBatis快速入门的系列教程 [MyBatis]MyBatis 使用教程 [MyBatis]MyBatis XML配置 [MyBatis]MyBatis XML映射文件 [ ...
- Git入门基础详情教程
前言 写了一篇文章<一篇文章了解Github和Git教程>还觉得不错,继续写了<为了Github默默付出,我想了解你>,那么继续写Git 基础知识. Git 官网:https: ...
- react 入门与进阶教程
react 入门与进阶教程 前端学习对于我们来说越来越不友好,特别是随着这几年的发展,入门门槛越来越高,连进阶道路都变成了一场马拉松.在学习过程中,我们面临很多选择,vue与react便是一个两难的选 ...
- 最适合新手入门的SpringCloud教程 6—Ribbon负载均衡「F版本」
SpringCloud版本:Finchley.SR2 SpringBoot版本:2.0.3.RELEASE 源码地址:https://gitee.com/bingqilinpeishenme/Java ...
- python入门之socket代码练习
Part.1 简单的socket单次数据传输 服务端: #服务器端 import socket server = socket.socket() # 声明socket类型,同时生成socket连接对象 ...
- webpack入门文档教程
.octicon{margin-right:2px}a.tabnav-extra:hover{color:#4078c0;text-decoration:none}.tabnav-btn{margin ...
- MVC5与EF6 Code First 第一个入门完整实例教程
mvc如今火的不行,我今天就来介绍一个MVC5与EF6开发的实际的入门实例,因为EF6默认是Code First的,所以我今天也就用EF6 的Code First来做一个简单的实例,为了让实例显得简单 ...
- Highcharts入门+Highcharts基础教程,【非常值得学习的资料】
http://www.hcharts.cn/docs/index.php?doc=index Highcharts入门章节目录 Highcharts简介 Highcharts下载与使用 Highcha ...
随机推荐
- 伪紫题p5194 天平(dfs剪枝)
这题作为一个紫题实在是过分了吧...绿的了不起了.—————————————————————————— 看题第一眼,01背包无误.2min打好一交全屏紫色(所以这就是这题是紫色的原因233?) re原 ...
- Python的看门狗实现自动化实时对服务器、Windows或Linux文件夹的实时监控
众所周知,在运维过程中,实时获取目标文件夹至关重要,Python的watchdog是用程序来监视文件系统事件Python库,所以用该库可以实现对文件夹的实时监控,filenotify.py代码如下: ...
- 数据分析之路 第一篇 numpy
第一篇 numpy 1.N维数组对象 :ndarray在Python中既然有了列表类型,为啥还要整个数组对象(类型)?那是因为:1.数组对象可以除去元素间运算所需要的循环,使得一维向量更像单个数据2. ...
- [LC]21题 Merge Two Sorted Lists (合并两个有序链表)(链表)
①英文题目 Merge two sorted linked lists and return it as a new list. The new list should be made by spli ...
- Java枚举——枚举的作用、使用方法、使用场景
枚举的定义 枚举关键字enum 枚举类是一种特殊类,它和普通类一样可以使用构造器.定义成员变量和方法,也可以实现多个接口,但不能继承类. 枚举的使用 enum Color { RED, BLUE, G ...
- 领扣(LeetCode)错误的集合 个人题解
集合 S 包含从1到 n 的整数.不幸的是,因为数据错误,导致集合里面某一个元素复制了成了集合里面的另外一个元素的值,导致集合丢失了一个整数并且有一个元素重复. 给定一个数组 nums 代表了集合 S ...
- PL真有意思(四):控制流
前言 对大多数计算模型而言,顺序都是基本的东西,它确定了为完成所期望的某种工作,什么事情应该最先做,什么事应该随后做,我们可以将语言规定顺序的机制分为几个类别: 顺序执行 选择 迭代 过程抽象 递归 ...
- Alibaba Nacos 学习(三):Spring Cloud Nacos Discovery - FeignClient,Nacos 服务注册与发现
Alibaba Nacos 学习(一):Nacos介绍与安装 Alibaba Nacos 学习(二):Spring Cloud Nacos Config Alibaba Nacos 学习(三):Spr ...
- 小白学 Python 爬虫(6):前置准备(五)爬虫框架的安装
人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...
- 在UEFI+GPT下使用rEFind实现Win10 + Kali2.0 双引导
转载自:在UEFI+GPT下使用rEFind实现Win10 + Kali2.0 双引导 https://www.linuxidc.com/Linux/2016-07/133717.htm