socket实现C#和Java之间的通信

1 篇文章 0 订阅

一、SOCKET原理

1.1 SOCKET概念

SOCKET是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议、本地主机的IP地址、本地进程的协议端口、远方主机的IP、远方进程的协议端口。
应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了socket接口。应用层可以和传输层通过socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。

1.2建立socket连接

建立socket连接至少需要一对socket,其中一个运行与客户端,称为clientSocket,另一个运行于服务器端,称为serverSocket.

socket之间的连接过程分为三个步骤:服务器监听客户端,客户端请求服务端,客户端与服务端连接确认。
服务器监听客户端:
服务器端SOCKET并不定位具体的客户端SOCKET,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
客户端请求服务端:
指客户端的SOCKET提出连接请求,要连接的目标是服务器端的SOCKET。为此,客户端的SOCKET必须首先描述它要连接的服务器的SOCKET,
指出服务器SOCKET的地址和端口号,然后向服务器端SOCKET提出连接请求。
客户端与服务端连接确认:
当服务器端SOCKET监听到或者说接收到客户端SOCKET的连接请求时,就响应客户端SOCKET的请求,建立一个新的线程,把服务器端的SOCKET的描述发给客户端,
一旦客户端确认了此描述,双方就正式建立连接。而服务器端SOCKET继续处于监听状态,继续接受其他客户端SOCKET的连接请求。

二、socket中TCP的三次握手建立连接详解

我们知道tcp建立连接要进行“三次握手”,即交换三个分组。那么socket这个三次握手发生在socket的哪几个函数呢?
  1. 当客户端调用connect函数时,向服务端发送了连接请求,向服务器发送了SYN J包,这时connect函数进入阻塞状态;
  2. 服务器监听到客户端的连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K,ACKJ+1,这时accpet函数进入阻塞状态;
  3. 客户端接收到服务器的SYN K,ACK J+1之后,这时connect返回,并对SYN进行确认; 服务器收到ACK K+1时,accpet返回,至此三次握手完毕,连接建立。

三、socket中TCP的四次握手释放连接详解

  • 某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
  • 另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据。
  • 一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
  • 接收到这个FIN的源发送端TCP对它进行确认。

四、 C#和JAVA的socket实践

实现一个简单的C#为客户端,java为接收端的例子,这是一个可以监听多个客户端的例子,可以传输照片或者文字,由于C#是无符号的二进制,而java是有符号的二进制,所以在图片的处理上我使用base64作为中间的过渡,不然从C#到Java的传输过程会出现字节丢失的情况,接着在文字的传输中我统一了字节码,否则会出现乱码现象,因为C#的默认编码不是UTF-8而java是UTF-8,从而导致了乱码现象。

C#实现的客户端:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Generic;
using System.Threading;  


namespace ClientMany
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Client Running...");

             //定义要传送的信息
            string msg = "这个是客户端的消息";
            string filename = "C:\\send\\image.jpg";
             string str1 = SentPictures(filename, msg);
             str1 = str1 + "#" + str1 + "#" + str1 + "#" + str1 + "#" + str1; 
             Send(str1);
            //按Q退出

            ConsoleKey key;
            Console.WriteLine("\n\n 输入 \"Q\"键退出。n继续");

            do
            {
                key = Console.ReadKey(true).Key;
            } while (key != ConsoleKey.Q);

        }



        /// <summary>
        /// 向远程客户端发送图片
        /// 这一步是base64编码
        /// </summary>
        /// <param name="strRemoteIP">远程客户端IP</param>
        /// <param name="sendPort">发送图片的端口</param>
        private  static string SentPictures(String filename,String str)
        {

            String str1;
            if (File.Exists(filename))
            {
                FileInfo file = new FileInfo(filename);
                var stream = file.OpenRead();
                byte[] buffer = new byte[file.Length];

                //读取图片字节流
                stream.Read(buffer, 0, Convert.ToInt32(file.Length));
                //将base64字符串保存到base64.txt文件中
                //将字节流转化成base64字符串
                str1 = Convert.ToBase64String(buffer);

            }
            else {
                Console.WriteLine("文件不存在");
                str1 = "";
            }

            str1 = str + ")" + str1 ;
            return str1;


        }

        private static void Send(string str){
            TcpClient client;

            try
            {
                client = new TcpClient();
                client.Connect("localhost", 8500);//与服务器连接
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
            Console.WriteLine("发送消息");
             NetworkStream streamToServer = client.GetStream();
            byte[] buffer1 = Encoding.UTF8.GetBytes(str);//获得缓存同时规定编码为UTF-8
            streamToServer.Write(buffer1, 0, buffer1.Length);//发往服务器
           // Console.Read();

            client.Close();

        }


    }
}

Java实现的服务器端:
实现接收的实现的程序:

    package com.wxshan.test;

import lombok.extern.log4j.Log4j2;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.BeanUtils;
import org.springframework.messaging.simp.SimpMessagingTemplate;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.net.Socket;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;


@Log4j2
public class ListenSocket extends Thread {


    //端口号
    private static final int SERVER_PORT = 8500;
    //服务端socket
    private ServerSocket serverSocket = null;

    public ListenSocket() {
        try {
            if (null == serverSocket) {

                this.serverSocket = new ServerSocket(SERVER_PORT);
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 监听的线程
     */
    public void run() {
        Socket client;
        System.out.println("-------主监听线程----------start----------");
        try {
            while (true) {
                client = serverSocket.accept();
                Thread c_thread = new CreateServerThread(client);
                c_thread.start();
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

    class CreateServerThread extends Thread {
        private Socket client;
        private PrintWriter out = null;
        private BufferedReader in = null;
        InputStreamReader inSR;
        OutputStream outSR;
        CarWaringVO carWaringVO;

        public CreateServerThread(Socket client) throws IOException {
            System.out.println("启动分线程");
            this.client = client;
        }

        public void run() {
            try {
                inSR = new InputStreamReader(client.getInputStream(), "UTF-8");//规定编码
                in = new BufferedReader(inSR);//接收请求的流
                outSR = client.getOutputStream();
                out = new PrintWriter(outSR);
                //String imgUrl = "";
                HashMap<String, String> imgUrl = new HashMap<>();
                int len = 0;//监听到的字符串长度
                StringBuffer tStringBuffer = new StringBuffer();//监听到的字符串w
                char[] buf = new char[4096];
                while ((len = in.read(buf)) != -1) {
                    String string = new String(buf, 0, len);
                    tStringBuffer.append(string);

                }

                //将接收的字符串分割出不同的消息
                String[] stringInfo = tStringBuffer.toString().split("#");
                for (int i = 0; i < stringInfo.length; i++) {
                    UUID uuid = UUID.randomUUID();
                    String date = CommonUtils.dateParse(LocalDate.now(), "yyyy/MM/dd");
                    String fileName = uuid + ".jpg";
                    String FilePath = new File(System.getProperty("java.io.tmpdir")).getParentFile().getParentFile().getAbsolutePath() + uploadPath + date + "/";
                    String urlpath = "/picture/car/" + date + "/" + fileName;
                    String path = FilePath + fileName;
                    File file = new File(FilePath);
                    if (!file.exists()) {
                        file.mkdirs();
                    }

                    //imgUrl += urlpath + ",";

                    //将消息的信息与照片分割出来
                    String[] strings = stringInfo[i].split("\\)");

                    //区别照片的类型
                    imgs(strings[0],imgUrl,urlpath);
                    //对图片进行base64的反转码
                    base64ToFile(strings[1], path);
                    System.out.println("客户端接收的消息:"+stringinfo + ",图片地址:"+urlpath);

            } catch (IOException e) {
                log.error(e.getMessage(), e);

            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        log.error(LogException.getTrace(e));
                    }
                }
                if (inSR != null) {
                    try {
                        inSR.close();
                    } catch (IOException e) {
                        log.error(LogException.getTrace(e));
                    }
                }
                if (out != null) {
                    try {
                        out.close();
                    } catch (Exception e) {
                        log.error(LogException.getTrace(e));
                    }
                }
                try {
                    if (client != null) {
                        client.close();
                    }
                } catch (IOException e) {
                    log.error(LogException.getTrace(e));
                }

            }
        }
    }

    /**
     * 将base64转化为文件
     * 这步是base解码
     * @param base64
     * @param path
     * @return
     */
    public boolean base64ToFile(String base64, String path) {
        byte[] buffer;
        InputStream stream;
        try {
            buffer = Base64.getDecoder().decode(base64);
            stream = new ByteArrayInputStream(buffer);
            Thumbnails.of(stream).scale(1f).outputQuality(0.5f).outputFormat("jpg").toFile(path);
            return true;
        } catch (Exception e) {
            throw new RuntimeException("base64字符串异常或地址异常\n" + e.getMessage());
        }
    }


    /**
     * 关闭当前线程
     */
    public void closeSocketServer() {
        try {
            if (null != serverSocket && !serverSocket.isClosed()) {
                serverSocket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }




}

声明类:

package com.wxshan.test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.annotation.Resource;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;


@WebListener
public class SocketListener  implements ServletContextListener{


    private ListenSocket sockertThread;

    @Resource
    private SimpMessagingTemplate simpMessagingTemplate;

    /**
     * 初始化 当servert容器启动web应用时调用该方法
     * @param servletContextEvent
     */
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        WebApplicationContextUtils.getRequiredWebApplicationContext(servletContextEvent.getServletContext())
                .getAutowireCapableBeanFactory().autowireBean(this);
        if (null == sockertThread) {
            sockertThread = new ListenSocket();
            sockertThread.start();
        }
    }

    /**
     * 销毁 当servlet容器终止web应用时调用改方法
     * @param servletContextEvent
     */
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        if (null != sockertThread && !sockertThread.isInterrupted()){
            //关闭线程
            sockertThread.closeSocketServer();
            //中断线程
            sockertThread.interrupt();
        }
    }


}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值