场景
在实际开发当中,语言经常会根据业务进行调整,比如网关和探针之间。之间可能都是用java实现,后面由于调整,探针改成go来写,网关不变。这个时候,通信的接口就不要定义开源的私有协议,否则两头都要改。采用最原始的socket通信,就没有这种问题。代码就是demo,仅供参考。
socket服务端 java实现
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketDemoServer {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
BufferedReader in = null;
int port = 9988;
try {
serverSocket = new ServerSocket(port); //创建服务器套接字
System.out.println("服务器开启,等待连接。。。");
socket = serverSocket.accept();// 获得链接
InputStream is = null;
byte[] bytes = new byte[4096];
is = socket.getInputStream();
StringBuilder sb = new StringBuilder();
while (true) {
is.read(bytes);
String msg = new String(bytes);
System.out.println("客户端发送的内容为:" + msg);
if (msg == null || msg.contains("end")) {
break;
}
}
OutputStream os = socket.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os));
sb.append("这是服务端的一条消息啊");
writer.write(sb.toString());
writer.newLine();
writer.write("end");
writer.newLine();
writer.flush();
socket.close();
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
socket客户端 go实现
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:9988")
if err != nil {
fmt.Println("客户端建立连接失败")
return
}
cConnHandler(conn)
}
func cConnHandler(c net.Conn) {
//返回一个拥有 默认size 的reader,接收客户端输入
reader := bufio.NewReader(os.Stdin)
//缓存 conn 中的数据
buf := make([]byte, 1024)
fmt.Println("请输入客户端请求数据...")
for {
//客户端输入
input, _ := reader.ReadString('\n')
//去除输入两端空格
input = strings.TrimSpace(input)
//客户端请求数据写入 conn,并传输
c.Write([]byte(input))
//服务器端返回的数据写入空buf
cnt, err := c.Read(buf)
if err != nil {
fmt.Printf("客户端读取数据失败 %s\n", err)
continue
}
fmt.Print("服务器端回复" + string(buf[0:cnt]))
}
}
测试截图
附录
顺便贴一下java版的客户端,go的服务端代码,组成完美demo ,百度抄一抄,改一下 。。。
socket客户端 java实现
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;
public class TCPClient {
public static void main(String[] args) throws Exception {
try {
while (true){
Socket socket = new Socket("localhost",9988);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()) );
Scanner scanner = new Scanner(System.in);
String str = scanner.nextLine();
writer.write(str);
writer.newLine(); //todo 必须加这一句,否则服务端读取的时候可能会阻断,不能读取
writer.write("end");
writer.newLine();
writer.flush();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true){
String rs = reader.readLine(); //todo while循环读,最后一个都不到的时候会出现阻塞,而不是读出null,所以只能约定读到end时结束
if(rs.equals("end")){
break;
}
System.out.println(rs);
}
socket.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
socket客户端 go实现
import (
"fmt"
"log"
"net"
"strings"
)
/**
需求:
socket编程实现 客户端 与 服务端进行通讯
通讯测试场景:
1.client 发送 ping, server 返回 pong
2.client 发送 hello, server 返回 world
3.其余client发送内容, server 回显即可
抽象&解决方案:
1. socket 编程是对 tcp通讯 过程的封装,unix server端网络编程过程为 Server->Bind->Listen->Accept
go 中直接使用 Listen + Accept
2. client 与客户端建立好的请求 可以被新建的 goroutine(go func) 处理 named connHandler
3. goroutine 的处理过程其实是 输入流/输出流 的应用场景
积累:
1.基础语法
2.基本数据结构 slice 使用
3.goroutine 使用
4.switch 使用
4.socket 编程核心流程
5.net 网络包使用
*/
func connHandler(c net.Conn) {
//1.conn是否有效
if c == nil {
log.Panic("无效的 socket 连接")
}
//2.新建网络数据流存储结构
buf := make([]byte, 4096)
//3.循环读取网络数据流
for {
//3.1 网络数据流读入 buffer
cnt, err := c.Read(buf)
//3.2 数据读尽、读取错误 关闭 socket 连接
if cnt == 0 || err != nil {
c.Close()
break
}
//3.3 根据输入流进行逻辑处理
//buf数据 -> 去两端空格的string
inStr := strings.TrimSpace(string(buf[0:cnt]))
//去除 string 内部空格
cInputs := strings.Split(inStr, " ")
//获取 客户端输入第一条命令
fCommand := cInputs[0]
fmt.Println("客户端传输->" + fCommand)
switch fCommand {
case "ping":
c.Write([]byte("服务器端回复-> pong\n"))
case "hello":
c.Write([]byte("服务器端回复-> world\n"))
default:
c.Write([]byte("服务器端回复" + fCommand + "\n"))
}
//c.Close() //关闭client端的连接,telnet 被强制关闭
fmt.Printf("来自 %v 的连接关闭\n", c.RemoteAddr())
}
}
//开启serverSocket
func main() {
//1.监听端口
server, err := net.Listen("tcp", "127.0.0.1:9988")
if err != nil {
fmt.Println("开启socket服务失败")
}
fmt.Println("正在开启 Server ...")
for {
//2.接收来自 client 的连接,会阻塞
conn, err := server.Accept()
if err != nil {
fmt.Println("连接出错")
}
//并发模式 接收来自客户端的连接请求,一个连接 建立一个 conn,服务器资源有可能耗尽 BIO模式
go connHandler(conn)
}
}