C#程序和java程序间的socket通信
在上⼀篇博客《DALSA⼯业相机流程》中讲过,需要在java和C#两个平台的两个项⽬⾥,实现通信。
⼀. 进程间的通信定义
先理解进程,进程是操作系统的内部概念,每当我们执⾏⼀个程序的时候,OS就创建⼀个进程,在这个过程中伴随着资源的分配和释放。可以这么说,进程就是⼀个程序的⼀次执⾏过程。
进程间通信(IPC:InterProcess Communication),进程之间的⽤户空间是独⽴的,⼀般来说不能互相访问,但是我们实际编程过程中很多进程之间存在⼀些逻辑条件判断,需要互相通信,才能完成我们的编程设计,这就有了进程间的通信。进程间通信主要应⽤于以下⼏个场景:数据传输、共享数据、通知事件、资源共享、进程控制,我的应⽤场景是第⼀个也是最常见的“数据传输”:将数据从⼀个进程发送到另外⼀个进程,发送数据量在⼀字节到⼏兆字节之间。
⼆. 进程间通信的⽅式
1.管道:管道的实质是内核利⽤ 环形队列 的数据结构在 内核缓冲区 中的⼀个实现,顺序读写。管道包括普通管道、流管道、命名管道,是使⽤最简单的⽅式。
2.消息队列:保存在内核中的消息 链表 ,跟管道相⽐不需要按照队列次序来读写,可以⾃定义接收消息。
3.共享内存:⼀对多的关系,⼀个进程创建⼀段共享内存,其他进程都可以访问该共享内存,是最快的IPC⽅式。
4.信号量:信号量是⼀个计时器,主要⽤于进程间的互斥和同步,⽽不是通信数据。
5.信号是⼀种⽐较复杂的通信⽅式,⽤于通知接收进程某个事件已经发⽣。
6.套接字:Socket也是⼀种进程间通信机制,但是与以上都不同的是,Socket可以⽤于不同机器间的通信,因为使⽤了通络通信,设置IP 和端⼝。
本地(localhost、127.0.0.1)套接字也是最稳定的IPC。
三. Socket通信
⾸先,我们上⾯讲的6中通信⽅式中,1-5都是本地进程通信,只有socket是可以在不同主机间进⾏通信的⽅式。这是如何实现的呢?我们回顾⼀下TCP/IP协议,传输控制协议/⽹间协议,所谓⽹间协议,就是不⽌是在本地通信,放到⽹络上也要能准确通信。我们知道在本地主机上,我们识别⼀个进程的⽅式是到进程的PID,PID是进程的唯⼀标识,如果只在本地通信,那么我们拿到PID就⾜够了,但是当我们进⾏⽹络通信,在不同的主机之间,PID重复的概率⾮常⼤。此时,就想办法让进程在⽹络上也具备唯⼀性。
办法⾃然就是TCP/IP了,IP层的ip地址可以在⽹络中确定唯⼀的主机,TCP层协议和端⼝号可以唯⼀标⽰主机的⼀个进程,也就是ip地址+协议+端⼝号来在⽹络中标⽰唯⼀进程,这是⽹络中进程间通信的前提基础。
标⽰了唯⼀进程后,我们就可以⽤socket来进⾏通信了,Socket翻译过来是套接字,是针对⽹络中不同主机的进程之间双向通信的端点的抽象,直⽩来说就是⽹络进程通信的端点,提供了应⽤层进程利⽤⽹络协议交换数据的机制,是应⽤程序通过⽹络协议进⾏通信和交互的接⼝。
Scoket是接⼝,是把TCP/IP层⼀些复杂的操作抽象成了接⼝,供应⽤层调⽤的,所以socket的使⽤⼀定是在应⽤层。
Socket分为服务端和客户端(这⼀点上能看出来底层是TCP/IP),服务端和客户端的⼯作流程包括交互如下图所⽰:
如果你回顾⼀下TCP/IP的三次握⼿,就会发现有不少共同点。
四. 实例
我的实际需求是java进程向C#进程发送数据,所以C#为服务端,java为客户端。C#代码(Server):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
namespace GrabImgByd
{
class SocketJavaServer
{
static void Main(string[] args)
{
server2();
}
static void server1()
进程通信方式{
Console.WriteLine("⽅法1");
//第⼀个参数是指定socket对象使⽤的寻址⽅案,即IPV4或IPV6;
/
/第⼆个参数socket对象的套接字的类型,此处stream是表⽰流式套接字
//第三个参数socket对象⽀持的协议,TCP协议或UDP协议
//第三个参数socket对象⽀持的协议,TCP协议或UDP协议
Socket soo = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//参数这样写就好了            IPAddress ip = IPAddress.Parse("127.0.0.1");//本机ip作为服务器
IPEndPoint ipend = new IPEndPoint(ip, 61666);//将⽹络终结点表⽰为 IP 地址和端⼝号
soo.Bind(ipend);//⽤socket对像的Bind()⽅法绑定IPEndPoint 绑定主机端⼝号
soo.Listen(10);//挂起的连接队列的最⼤长度。貌似可以不写。不是最⼤连接数
Console.WriteLine("C#创建服务器成功...");
Socket so = soo.Accept();//等待⽤户连接
byte[] by = new byte[1024];
while (true)
{
int len = so.Receive(by);//Send发送 Receive接收
Console.WriteLine("C#服务器收到消息长度+" + len);
Console.WriteLine(Encoding.UTF8.GetString(by, 0, len));
}
}
static void server2()
{
Console.WriteLine("⽅法2");
TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 61666);
listener.Start();//启动服务器
/
/上⾯这⼀段相当于java的ServerSocket soo=new ServerSocket(61666);
Console.WriteLine("C#创建服务器成功...");
Socket so = listener.AcceptSocket();//等待⽤户连接
NetworkStream io = new NetworkStream(so);
byte[] by = new byte[1024];
while (true)
{
//接收⼀组byte 存储到字节数组by⾥
//从by[0]处开始存储该数据要读取1024个字节
//len为实际读取到的字节数
int len = io.Read(by, 0, 10);
Console.WriteLine("C#服务器收到消息长度+" + len);
Console.WriteLine(Encoding.UTF8.GetString(by, 0, len));
}
}
}
}
此处列举了两个⽅法,server1和server2,都可以运⾏,建议使⽤server1。
运⾏图:
Java代码(Client):
/*
* ⽂件名:SocketCScharpClient.java
* 版权:Copyright BYD Auto Industry Company Limited. All rights reserved.. */
package abImg;
import java.io.IOException;
import java.io.OutputStream;
import java.Socket;
import java.util.Scanner;
/**
* 〈⼀句话功能简述〉
* 〈功能详细描述〉
*
* @author xu.shuo2
* @version
* @see
* @since
*/
public class SocketCScharpClient {
public static void main(String[] args) {
try {
// 第⼀个参数的服务器的ip 第⼆个参数是端⼝号
Socket so = new Socket("127.0.0.1", 61666);
System.out.println("java连接服务器成功...");
OutputStream out = so.getOutputStream();// 获取socket的输出流
Scanner in = new Scanner(System.in);// 从键盘输⼊值
while (true) {
String s = in.nextLine();
out.Bytes());
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
⽤的是键盘输⼊的值来发送给服务端,运⾏效果如下:
发送字符串:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。