hello/hi的简单的网络聊天程序
hello/hi的简单的网络聊天程序
0 Linux Socket API
Berkeley套接字接口,一个应用程序接口(API),使用一个Internet套接字的概念,使主机间或者一台计算机上的进程间可以通讯。 它可以在很多不同的输入/输出设备和驱动之上运行,尽管这有赖于操作系统的具体实现。 接口实现用于TCP/IP协议,因此它是维持Internet的基本技术之一。 它是由加利福尼亚的伯克利大学开发,最初用于Unix系统。 如今,所有的现代操作系统都有一些源于Berkeley套接字接口的实现,它已成为连接Internet的标准接口。
我们所用的Linux Socket API实际上就是 Berkeley Socket,下面给出一些建立TCP连接常用的Socket接口函数及其功能概要:
socket()创建一个新的确定类型的套接字,类型用一个整型数值标识(文件描述符),并为它分配系统资源。bind()一般用于服务器端,将一个套接字与一个套接字地址结构相关联,比如,一个指定的本地端口和IP地址。listen()用于服务器端,使一个绑定的TCP套接字的tcp状态由CLOSE转至LISTEN;操作系统内核为此监听socket所对应的tcp服务器创建一个pending socket队列和一个established socket队列;参数backlog指定pending socket队列的长度,0表示长度可以无限大。pending socket,就是某客户端三次握手的syn包到达,内核为这个syn包对应的tcp请求生成一个socket(状态为SYN_RECV),但三次握手还没有完成时的socket。connect()用于客户端,为一个套接字分配一个自由的本地端口号。 如果是TCP套接字的话,它会试图获得一个新的TCP连接。accept()用于服务器端。 它接受一个从远端客户端发出的创建一个新的TCP连接的接入请求,创建一个新的套接字,与该连接相应的套接字地址相关联。send()和recv(),或者write()和read(),或者recvfrom()和sendto(), 用于往/从远程套接字发送和接受数据。close()用于系统释放分配给一个套接字的资源。 如果是TCP,连接会被中断。
1 hello/hi程序
1.0 功能概述
下面提供一个使用Java语言的简单的hello/hi程序代码,程序分为两部分,分别是服务端和客户端。该程序利用Java Socket API建立一个TCP连接,先从客户端向服务端发送一个“Hi,I am Client”字符串,然后服务端再返回一个“Hi,I am Server”字符串。
1.1 服务端
服务端的功能是使用Java的ServerSocket类的对象监听8090端口,然后使用ServerSocket类的accept()与8090端口建立连接,当收到一个来自客户端的"Hi,I am Client"时,回复一个"Hi,I am Server",代码如下:
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket socket = new ServerSocket(8090);// 监听8090端口
System.out.println("TCP server ready.");
Socket sock = socket.accept();// 建立连接
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(sock.getInputStream(), StandardCharsets.UTF_8))) {
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(sock.getOutputStream(), StandardCharsets.UTF_8))) {
String cmd = reader.readLine();// 读取发送到服务端的数据
if ("Hi,I am Client".equals(cmd)) {
System.out.println("Client:"+cmd+"\n");
writer.write("Hi,I am Server"+ "\n");
writer.flush();
} else {
writer.write("require data\n");
writer.flush();
}
}
}
sock.close();// 关闭连接
socket.close();// 关闭监听端口
}
}
1.2 客户端
客户端的功能是获取本机地址,然后与本机的8090端口建立连接。向服务端发送"Hi,I am Client"后等待服务的回应的消息。
public class Client {
public static void main(String[] args) throws IOException {
InetAddress addr = InetAddress.getLoopbackAddress();// 获取本机地址,即“127.0.0.1”
try (Socket socket = new Socket(addr, 8090)) {// 与本机8090端口建立连接
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))) {
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8))) {
writer.write("Hi,I am Client\n");
writer.flush();
String resp = reader.readLine();// 读取本机8090端口返回的数据
System.out.println("Server: " + resp);
}
}
}
}
}
2 分析Java Socket API和Linux Socket API的关系
上面两段程序调用ServerSocket类的构造方法和close(),还有Socket类的构造方法、accept()、getInputStream()、``getOutputStream()和close()`方法。下面通过对ServerSocket类以及Socket类的源码分析找到Java Socket API和Linux Socket API的对应关系。
2.0 SocketImpl类与Linux Socket API的关系
抽象类SocketImpl是实际上实现套接字的所有类的通用超类,它将实际的Socket操作抽象出来,在逻辑上与Linux Socket API的核心操作相对应,如果找到SocketImpl类的操作,就相当于找到了对应的Linux Socket API操作。
2.1 ServerSocket类
2.1.0 SeverSocket()
在服务端程序中,构造了一个ServerSocket类对象监听8090端口:
ServerSocket socket = new ServerSocket(8090);
找到相应的源码:
public ServerSocket(int port) throws IOException {
this(port, 50, (InetAddress)null);
}
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
this.created = false;
this.bound = false;
this.closed = false;
this.closeLock = new Object();
this.oldImpl = false;
this.setImpl();
if (port >= 0 && port <= 65535) {
if (backlog < 1) {
backlog = 50;
}
try {
this.bind(new InetSocketAddress(bindAddr, port), backlog);
} catch (SecurityException var5) {
this.close();
throw var5;
} catch (IOException var6) {
this.close();
throw var6;
}
} else {
throw new IllegalArgumentException("Port value out of range: " + port);
}
可以看到,this.bind(new InetSocketAddress(bindAddr, port), backlog);语句中调用了bind()方法,查看其具体实现:
this.getImpl().bind(epoint.getAddress(), epoint.getPort());
this.getImpl().listen(backlog);
发现SeverSocket.bind()方法与Linux Socket API中的bind()和listen()函数相对应。
所以ServerSocket()方法对应的Linux Socket API是socket(),bind(),listen()。
2.1.1 accept()
服务端中还使用ServerSocket.accept()方法初始化了一个Socket对象,下面找到其源码:
public Socket accept() throws IOException {
if (this.isClosed()) {
throw new SocketException("Socket is closed");
} else if (!this.isBound()) {
throw new SocketException("Socket is not bound yet");
} else {
Socket s = new Socket((SocketImpl)null);
this.implAccept(s);
return s;
}
}
发现其对应的Linux Socket API是accept()。
2.1.2 close()
public void close() throws IOException {
synchronized(this.closeLock) {
if (!this.isClosed()) {
if (this.created) {
this.impl.close();
}
this.closed = true;
}
}
}
显然,ServerSocket.close()对应的Linux Socket API是close()。
2.2 Socket类
在客户端和服务端分别构造了一个Socket类对象用于建立Tcp连接和通信。下面分析所用函数的源码。
2.2.0 Socket()
客户端和服务端使用的构造函数分别是Socket(SocketImpl impl)和Socket(InetAddress address, int port),前者其实是复制了一个Socket类的对象,只需看后者的源码即可:
public Socket(InetAddress address, int port) throws IOException {
this(address != null ? new InetSocketAddress(address, port) : null, (SocketAddress)null, true);
}
接着找真正的调用的构造函数:
private Socket(SocketAddress address, SocketAddress localAddr, boolean stream) throws IOException {
this.created = false;
this.bound = false;
this.connected = false;
this.closed = false;
this.closeLock = new Object();
this.shutIn = false;
this.shutOut = false;
this.oldImpl = false;
this.setImpl();
if (address == null) {
throw new NullPointerException();
} else {
try {
this.createImpl(stream);
if (localAddr != null) {
this.bind(localAddr);
}
this.connect(address);
} catch (IllegalArgumentException | SecurityException | IOException var7) {
try {
this.close();
} catch (IOException var6) {
var7.addSuppressed(var6);
}
throw var7;
}
}
}
发现该方法调用了Socket类的bind()和connect()方法。
根据前面已经知道,ServerSocket类的ServerSocket.bind()方法与Linux Socket API中的bind()和listen()函数相对应,那么在Socket类中是否是这么对应的呢?查看一下Socket.bind()方法的核心语句:this.getImpl().bind(addr, port);发现在Socket.bind()方法对应只是对应Linux Socket API中的bind()。
而Socket.connect()方法的核心语句如下:
if (!this.oldImpl) {
this.impl.connect(epoint, timeout);
} else {
if (timeout != 0) {
throw new UnsupportedOperationException("SocketImpl.connect(addr, timeout)");
}
if (epoint.isUnresolved()) {
this.impl.connect(addr.getHostName(), port);
} else {
this.impl.connect(addr, port);
}
}
发现对应的Linux Socket API中的函数是connect()。
由上面的分析得出,Socket()对于的Linux Socket API是socket(),bind(),connect()。
2.2.1 getInputStream() & getOutputStream()
像之前一样,找到对应的源码:
public InputStream getInputStream() throws IOException {
if (this.isClosed()) {
throw new SocketException("Socket is closed");
} else if (!this.isConnected()) {
throw new SocketException("Socket is not connected");
} else if (this.isInputShutdown()) {
throw new SocketException("Socket input is shutdown");
} else {
InputStream is = null;
try {
is = (InputStream)AccessController.doPrivileged(new PrivilegedExceptionAction<InputStream>() {
public InputStream run() throws IOException {
return Socket.this.impl.getInputStream();
}
});
return is;
} catch (PrivilegedActionException var3) {
throw (IOException)var3.getException();
}
}
}
public OutputStream getOutputStream() throws IOException {
if (this.isClosed()) {
throw new SocketException("Socket is closed");
} else if (!this.isConnected()) {
throw new SocketException("Socket is not connected");
} else if (this.isOutputShutdown()) {
throw new SocketException("Socket output is shutdown");
} else {
OutputStream os = null;
try {
os = (OutputStream)AccessController.doPrivileged(new PrivilegedExceptionAction<OutputStream>() {
public OutputStream run() throws IOException {
return Socket.this.impl.getOutputStream();
}
});
return os;
} catch (PrivilegedActionException var3) {
throw (IOException)var3.getException();
}
}
}
可以看到,Socket.getInputStream() 和 Socket.getOutputStream()对应的Linux Java API是recv()和send()。
2.2.2 close()
public synchronized void close() throws IOException {
synchronized(this.closeLock) {
if (!this.isClosed()) {
if (this.created) {
this.impl.close();
}
this.closed = true;
}
}
}
同样,Socket.close()对应的Linux Java API是close()。
3 总结
由上面的探究可知,Java Socket API和Linux Socket API的关系如下表:
| Java Socket API | Linux Socket API |
|---|---|
ServerSocket() |
socket(),bind(),listen() |
ServerSocket.accept() |
accept() |
ServerSocket.bind() |
bind(),listen() |
ServerSocket.close() |
close() |
Socket() |
socket(),bind(),connect() |
Socket.bind() |
bind(),connect() |
Socket.getInputStream() |
recv() |
Socket.getOutputStream() |
send() |
Socket.close() |
close() |
可以看到,Java Socket API对Linux Socket API进行了进一步封装,使之更容易根据不同情况进行TCP连接。
参考资料
https://www.cnblogs.com/abcboy/p/9769230.html
https://github.com/mengning/net
https://zh.wikipedia.org/wiki/Berkeley%E5%A5%97%E6%8E%A5%E5%AD%97
hello/hi的简单的网络聊天程序的更多相关文章
- 以您熟悉的编程语言为例完成一个hello/hi的简单的网络聊天程序
Socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信,应用程序通常通过"套接字"向网络发出 ...
- 以C语言为例完成简单的网络聊天程序以及关于socket在Linux下系统调用的分析
套接字是网络编程中的一种通信机制,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程. 端 ...
- python实现一个简单的网络聊天程序
一.Linux Socket 1.Linux Socke基本上就是BSD Socket(伯克利套接字) 伯克利套接字的应用编程接口(API)是采用C语言的进程间通信的库,经常用在计算机网络间的通信.B ...
- 用Java实现简单的网络聊天程序
Socket套接字定义: 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开.读写和关闭等操作.套接字允许应用程序将I/O插入到网络中,并与网络中的其他 ...
- telnet指令研究—以网络聊天程序为例
一.telnet指令 Telnet取名自Telecommunications和Networks的联合缩写,是早期个人计算机上连接到服务器主机的一个网络指令,由于存在安全问题,现在已经很少被使用.在wi ...
- C# 异步通信 网络聊天程序开发 局域网聊天室开发
Prepare 本文将使用一个NuGet公开的组件技术来实现一个局域网聊天程序,利用组件提供的高性能异步网络机制实现,免去了手动编写底层的困扰,易于二次开发,扩展自己的功能. 在Visual Stud ...
- boost asio异步读写网络聊天程序client 实例具体解释
boost官方文档中聊天程序实例解说 数据包格式chat_message.hpp <pre name="code" class="cpp">< ...
- boost asio异步读写网络聊天程序客户端 实例详解
boost官方文档中聊天程序实例讲解 数据包格式chat_message.hpp <pre name="code" class="cpp">< ...
- 使用Java实现hello/hi的简单网络聊天程序
Socket又称套接字,是基于应用服务与TCP/IP通信之间的一个抽象,它是计算机之间进行通信的一种约定或一种方式.通过socket这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送 ...
随机推荐
- usb相关
https://github.com/daynix/UsbDk/tree/master/UsbDk 更应该关注下libusb
- 基于JQ的记忆翻牌游戏
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8& ...
- 标准库heapq的使用
转载自: https://blog.csdn.net/y472360651/article/details/80725355 查找最大或最小的N个元素 怎么样从一个列表中取出最大或最小的N个元素的列表 ...
- BZOJ 3901 棋盘游戏 (找结论+枚举+贪心)
题面 略 BZOJ 传送门 分析 具体分析见 dalao博客 妙就妙在当i<x,j<xi<x,j<xi<x,j<x时,(i,j)(i,j)(i,j) ^ (i,x) ...
- python自动华 (十五)
Python自动化 [第十五篇]:CSS.JavaScript 和 Dom介绍 本节内容 CSS javascript dom CSS position标签 fixed: 固定在页面的某个位置 rel ...
- VSCode 插件和快捷键(MAC)
1. 插件 1. JSON 格式优化--- JSON Tools 快捷键: 1). 格式化json字符串 Mac: Cmd+Option+M win: Ctrl+Alt+M 2).压缩json Ma ...
- ueditor粘贴从word中copy的图片和文字 图片无法显示的问题
我司需要做一个需求,就是使用富文本编辑器时,不要以上传附件的形式上传图片,而是以复制粘贴的形式上传图片. 在网上找了一下,有一个插件支持这个功能. WordPaster 安装方式如下: 直接使用Wor ...
- python 关键参数和默认值
def hello_key(greeting='hello', name='world'): print('%s, %s' % (greeting, name)) hello_key() hello_ ...
- 从 s 点到 t 点的最短路(简单模板)(迪杰斯特拉)
迪杰斯特拉简单版 #include <bits/stdc++.h> using namespace std; int m,n; const int inf = 0x3f3f3f3f; in ...
- 十二、 RAID
https://blog.51cto.com/sonlich http://note.youdao.com/noteshare?id=17083150f38dd19343f82ea6cc0e0e62& ...