如何使用TCP/IP开发网络程序

硅谷漂流者
• 阅读 1999
摘要:进行TCP协议网络程序的编写,关键在于ServerSocket套接字的熟练使用,TCP通信中所有的信息传输都是依托ServerSocket类的输入输出流进行的。

本文分享自华为云社区《Java利用TCP协议实现客户端与服务器通信【附通信源码】》,作者:灰小猿。

TCP协议概念

我们知道TCP是可靠而非安全的网络协议。它可以保证数据在从一端送至另一端的时候可以准确的送达,并且抵达的数据的排列顺序和送出时的顺序是相同的。因此在进行TCP协议通信的时候,我们首先应该保证客户端和服务器之间的连接通畅。

而TCP协议程序的编写,仍然是依靠套接字Socket类来实现的,并且利用TCP协议进行通信的两个程序之间是有主次之分的,即一个是服务器的程序,另一个是客户端的程序。因此两者的功能和编写上也略有不同。如下图是服务器与客户端之间进行通信的示意图:
如何使用TCP/IP开发网络程序

以上就是在TCP协议中客户端与服务器建立连接的过程示意图。而在这其中起到关键作用的就是服务器端套接字ServerSocket和客户端套接字Socket。通过这两个套接字来建立服务器和客户端,从而利用其中的函数进行数据的通信。

在ServerSocket类中有很多需要注意的地方,接下来大灰狼和大家分享一下ServerSocket类的具体用法:

ServerSocket类

ServerSocket类存在于http://Java.net包中,表示服务器端的套接字,在使用时需要首先导入这个类,我们也知道ServerSocket类的主要功能就是通过指定的端口等待来自于网络中客户端的请求并且进行连接。

值得注意的是:服务器套接字一次只能与一个客户端套接字进行连接,因此如果存在多台客户端同时发送连接请求,则服务器套接字就会将请求的客户端存放到队列中去,然后从中取出一个套接字与服务器建立的套接字进行连接,但是服务器端能够容纳的客户端套接字也不是无限的,当请求连接的数量大于最大容纳量时,那么多出来的请求就会被拒接,一般来说队列的默认大小是50。

ServerSocket类的构造方法通常会抛出IOException异常,具体有以下几种形式:

• ServerSocket():创建非绑定服务器套接字
• ServerSocket(inr port):创建绑定到特定端口的服务器套接字
• ServerSocket(int port, int backlog):利用指定的backlog创建服务器套接字,并将其绑定到指定的服务器端口上,
• ServerSocket(int port, int backlog, InetAddress bindAddress):使用指定的端口,侦听backlog和要绑定到本地的IP地址创建服务器。这种情况适用于计算机上有多个网卡和多个IP地址的情况,用户可以明确的规定ServerSocket在哪块网卡或哪个IP地址上等待用户的连接请求。

以下是ServerSocket类中一些常用的方法:
如何使用TCP/IP开发网络程序

了解了ServerSocket类的基本方法之后,就是如何进行客户端和服务器进行连接的问题了。

在服务器端我们可以调用ServerSocket类的accpet()方法与请求连接的客户机建立连接,这时会返回一个和客户端相连接的Socket对象,这个时候其实已经连接成功了,使用getInetAddress()方法就可以获取到进行请求的客户机的IP地址。

对于如何进行客户端和服务器端数据的通信,就要用到数据的输入流和输出流了,服务器端的Socket对象使用getOutputStream()方法获取到的输出流,将指向客户端的Socket对象使用getInputStream()方法获取到的输入流。由此就实现在服务器向客户端发送数据的一个过程,同样的道理,客户端端的Socket对象使用getOutputStream()方法获取到的输出流,将指向服务器端的Socket对象使用getInputStream()方法获取到的输入流。从而实现由客户端向服务器发送数据的过程。

注意:accpet()方法会阻塞线程的继续执行,如果在对应的接口没有收到客户端的呼叫,则程序会停留在此处,直到获取到客户端的呼叫才会继续向下执行,但是如果服务器没有收到来自客户端的呼叫请求,并且accpet()方法没有发生阻塞,那么通常情况下就是程序出了问题,一般来说可能是使用了一个已经被其他程序占用了的端口号,导致ServerSocket没有绑定成功!遇到这种情况可以尝试更换新的端口号。

了解了TCP协议的通信过程,接下来就是进行TCP通信程序的书写啦!

在网络通信中,如果只要求客户机向服务器发送信息,不要求服务器向客户端反馈信息的行为称为“单向通信”,要求客户机和服务器双方互相通信的过程称为“双向通信”,双向通信只不过是比单向通信多了一个服务器向客户端发送消息的过程,

接下来分别是服务器端和客户端程序的编写:

服务器端程序

package server_1;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class MyTcp {

    private ServerSocket server;    //设置服务器套接字
    private Socket client;        //设置客户端套接字
 
    //连接客户端函数
    void getServer()
    {
        try {
            server = new ServerSocket(1100);    //建立服务器 端口为1100
            System.out.println("服务器建立成功!正在等待连接......");
            client = server.accept();    //调用服务器函数对客户端进行连接            
            System.out.println("客户端连接成功!ip为:" + client.getInetAddress());    //返回客户端IP        
            getClientMessage();        //调用信息传输和接收函数
 
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
    }
 
    void getClientMessage()
    {
        try {
            while (true) {
                InputStream is = client.getInputStream();    //获取到客户端的输入流
                byte[] b = new byte[1024];    //定义字节数组
                int len = is.read(b);    //由于信息的传输是以二进制的形式,所以要以二进制的形式进行数据的读取
                String data = new String(b, 0,len);
                System.out.println("客户端发来消息:" + data);
 
                //定义发送给客户端的输出流
                OutputStream put = client.getOutputStream();
                String putText = "我已经收到!欢迎你!";
                put.write(putText.getBytes());    //将输出流信息以二进制的形式进行写入
            }
        } catch (Exception e) {
            // TODO: handle exception
        }
        try {
            //判断客户端字节流不是空,则关闭客户端
            if (server != null) {
                server.close();
            }
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        MyTcp myTcp = new MyTcp();    //调用该类生成对象
        myTcp.getServer();    //调用方法
    }

}

客户端程序

package client_1;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class MyClient {
    private Socket client;    //定义客户端套接字
 
    //建立客户端函数
    void getClient()
    {
        try {
            client = new Socket("127.0.0.1", 1100);    //建立客户端,使用的IP为127.0.0.1,端口和服务器一样为1100
            System.out.println("客户端建立成功!");
 
            setClientMessage();        //调用客户端信息写入函数
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
 
    //定义客户端信息写入函数
    void setClientMessage()
    {
        try {        
            OutputStream pt = client.getOutputStream();        //建立客户端信息输出流
            String printText = "服务器你好!我是客户端!";    
            pt.write(printText.getBytes());        //以二进制的形式将信息进行输出
 
            InputStream input = client.getInputStream();    //建立客户端信息输入流
            byte [] b = new byte[1024];        //定义字节数组
            int len = input.read(b);    //读取接收的二进制信息流
            String data = new String(b, 0,len);
            System.out.println("收到服务器消息:" + data);
 
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        try {
            //如果客户端信息流不为空,则说明客户端已经建立连接,关闭客户端
            if (client != null) {
                client.close();
            }
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //生成客户端类对象
        MyClient myClient  = new MyClient();
        myClient.getClient();
    }

}

同时要注意:在客户端和服务器搭建成功之后,应该先打开服务器等待连接,再打开客户端进行连接,同样在进行关闭时,应该先关闭客户端,再关闭服务器。

以上面程序为例:

打开服务器等待客户端连接
如何使用TCP/IP开发网络程序

打开客户端与服务器连接成功,并且实现双向通信:
如何使用TCP/IP开发网络程序

注意:当一台机器上安装了多个网络应用程序时,很可能指定的端口已经被占用,甚至还可能遇到之前运行很好的程序突然卡住的情况,这种情况很可能是端口被别的程序占用了,这时可以运行netstat-help来活的帮助,可以使用命令netstat-an来查看该程序所使用的端口。

点击关注,第一时间了解华为云新鲜技术~

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java Socket
1\.套接字介绍套接字是介于传输层(TCP/UDP)和应用层(HTTP/FTP)之间的一个抽象,被应用程序调用;在java环境中套接字编程主要使用TCP/IP协议,但是套接字支持的协议族远不止这些;在java套接字编程中有Socket和ServerSocket两个核心类,ServerSocket位于服务器端监听连接,S
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
HTTP基础知识
1、网络本质进行资源共享和信息传输。2、基于网络的应用程序的本质就是获取数据和传输数据给用户使用。3、TCP/IP协议栈工作流程实体层是不属于TCP/IP协议栈的一层。也就是说TCP/IP协议栈共计四层。首先得接入网络,局域网或
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
34.TCP取样器
阅读文本大概需要3分钟。1、TCP取样器的作用   TCP取样器作用就是通过TCP/IP协议来连接服务器,然后发送数据和接收数据。2、TCP取样器详解!(https://oscimg.oschina.net/oscnet/32a9b19ba1db00f321d22a0f33bcfb68a0d.png)TCPClien
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(