Java实现一个hello/hi的简单的网络聊天程序

使用Java的Socket实现客户端和服务器端之间的连接,实现客户端重复发送数据到服务器端的功能。即,用户可以在控制台不断输入内容,并将内容逐一发送给服务端。并在服务端显示。

socket定义

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
        Socket的英文原义是"孔"或"插座"。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原义那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。
        简单来说,我们网络上的那些数据可以看成自来水管里的那些水,那现在服务器就是 自来水厂 而客户端就是你自己的家,那架设在外面的那些水管就是光纤,电话线等等传播网络信号的介质,现在可以直接用个破水管捅到自来水厂,然后另一端再直接捅进你家吗??这肯定不行,不然我不想要水的时候,水厂还在往死里灌,家里不就水漫金山了,所以这个时候水厂想把水给你送过去就需要做个阀门然后通过这个阀门控制水的流量啊等等,你自己家也同样装一个阀门来控制,不然水费咋算啊,哈哈哈

程序实现如下:

客户端:

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class Client {

    public static void main(String[] args) {

        Socket socket = null;//与服务器链接的套接字
        Scanner fromKey = null;//键盘输入流
        Scanner fromServer = null;//获取服务器发送的字节流
        PrintWriter toServer = null;//向服务器发送的字节流

        try {
            //链接服务器的套接字
            socket = new Socket("localhost",8866);

            /*
             * 注意:服务器端与客户端不能都先获取输入流,这样会导致socket阻塞
             * */
            //实例化从服务器读的字节流
            fromServer = new Scanner(socket.getInputStream());
            //实例化向服务器写的字节流
            toServer = new PrintWriter(socket.getOutputStream());
            //实例化键盘输入流
            fromKey = new Scanner(System.in);

            while(fromServer.hasNextLine()){
                //阻塞等待服务器发送消息
                String fromServerData = fromServer.nextLine();
                System.out.println("服务器 :" + fromServerData);
                System.out.print("我(客户端) :");
                //获取输入的数据
                String toServerData = fromKey.nextLine();
                //发送给服务器
                toServer.println(toServerData);
                toServer.flush();
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            try {
                fromServer.close();
                toServer.close();
                socket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

}

服务端:

import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class Server {

    public static void main(String[] args) {

        ServerSocket ServerSocket = null;//开放的套接字
        Socket socket = null;//链接客户端的套接字
        Scanner fromKey = null;//键盘输入流
        Scanner fromClient = null;//获取从客户端读的字节流
        PrintWriter toClient = null;//获取向客户端写的字节流

        try {
            ServerSocket = new ServerSocket(8866);
            System.out.println("服务器已启动,等待客户端链接");
            //链接客户端的套接字
            socket = ServerSocket.accept();

            //实例化向客户端写的字节流
            toClient = new PrintWriter(socket.getOutputStream());
            toClient.println("你好啊客户端!!!");
            toClient.flush();
            System.out.println("我(服务器端) : 你好啊客户端!!!");
            /*
             * 注意:服务器端与客户端不能都先获取输入流,这样会导致socket阻塞
             * */
            //实例化从服务器读的字节流
            fromClient = new Scanner(socket.getInputStream());
            //实例化键盘输入流
            fromKey = new Scanner(System.in);

            //阻塞等待客户端发送消息
            while(fromClient.hasNextLine()){
                String fromClientData = fromClient.nextLine();
                System.out.println("客户端 :" + fromClientData);
                System.out.print("我(服务器端) :");
                //获取输入的数据
                String toClientData = fromKey.nextLine();
                //发送给客户端
                toClient.println(toClientData);
                toClient.flush();
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            try {
                fromClient.close();
                toClient.close();
                socket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

}

执行结果:

Socket通信过程和API全解析

udp和TCP socket通信过程基本上是一样的,只是调用api时传入的配置不一样,以TCP client/server模型为例子看一下整个过程。

1. socket()

  #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       int socket(int domain, int type, int protocol);

    - 参数说明
    domain: 设定socket双方通信协议域,是本地/internet ip4 or ip6
       Name                Purpose                          Man page
       AF_UNIX, AF_LOCAL   Local communication              unix(7)
       AF_INET             IPv4 Internet protocols          ip(7)
       AF_INET6            IPv6 Internet protocols          ipv6(7)

    type: 设定socket的类型,常用的有
        SOCK_STREAM - 一般对应TCP、sctp
        SOCK_DGRAM - 一般对应UDP
        SOCK_RAW - 

    protocol: 设定通信使用的传输层协议
    常用的协议有IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,可以设置为0,系统自己选定。注意protocol和type不是随意组合的。

  socket() API是在glibc中实现的,该函数又调用到了kernel的sys_socket(),调用链如下。

详细的kernel实现我没有去读,大体上这样理解。调用socket()会在内核空间中分配内存然后保存相关的配置。同时会把这块kernel的内存与文件系统关联,以后便可以通过filehandle来访问修改这块配置或者read/write socket。操作socket就像操作file一样,应了那句unix一切皆file。提示系统的最大filehandle数是有限制的,/proc/sys/fs/file-max设置了最大可用filehandle数。当然这是个linux的配置,可以更改,方法参见Increasing the number of open file descriptors,有人做到过1.6 million connection。

2. bind()

#include <sys/types.h>          /* See NOTES */
   #include <sys/socket.h>
   int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

   参数说明
   sockfd:之前socket()获得的file handle
   addr:绑定地址,可能为本机IP地址或本地文件路径
   addrlen:地址长度

   功能说明
   bind()设置socket通信的地址,如果为INADDR_ANY则表示server会监听本机上所有的interface,如果为127.0.0.1则表示监听本地的process通信(外面的process也接不进啊)。

3. listen()

 #include <sys/types.h>          /* See NOTES */
   #include <sys/socket.h>
   int listen(int sockfd, int backlog);

   参数说明
   sockfd:之前socket()获得的file handle
   backlog:设置server可以同时接收的最大链接数,server端会有个处理connection的queue,listen设置这个queue的长度。

   功能说明
   listen()只用于server端,设置接收queue的长度。如果queue满了,server端可以丢弃新到的connection或者回复客户端ECONNREFUSED。
   

4. accept()

 #include <sys/types.h>          /* See NOTES */
   #include <sys/socket.h>
   int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

   参数说明:
   addr:对端地址
   addrlen:地址长度

   功能说明:
   accept()从queue中拿出第一个pending的connection,新建一个socket并返回。
   新建的socket我们叫connected socket,区别于前面的listening socket。
   connected socket用来server跟client的后续数据交互,listening socket继续waiting for new connection。
   当queue里没有connection时,如果socket通过fcntl()设置为 O_NONBLOCK,accept()不会block,否则一般会block。
    

疑问:kernel是如何区分listening socket和connected socket的呢??虽然二者的五元组是不一样的,kernel如何知道通过哪个socket跟APP交互?通过解析内容,是SYN还是数据?暂时存疑。

5. connect()

  #include <sys/types.h>          /* See NOTES */
   #include <sys/socket.h>
   int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

   参数说明:
   sockfd: socket的标示filehandle
   addr:server端地址
   addrlen:地址长度

   功能说明:
   connect()用于双方连接的建立。
   对于TCP连接,connect()实际发起了TCP三次握手,connect成功返回后TCP连接就建立了。
   对于UDP,由于UDP是无连接的,connect()可以用来指定要通信的对端地址,后续发数据send()就不需要填地址了。
   当然UDP也可以不使用connect(),socket()建立后,在sendto()中指定对端地址。

  

原文地址:https://www.cnblogs.com/sunxiangyang/p/12013397.html

时间: 2024-08-29 17:56:38

Java实现一个hello/hi的简单的网络聊天程序的相关文章

使用java实现一个hello/hi的简单的网络聊天程序

1.socket原理 Socket实质上提供了进程通信的端点.进程通信之前,双方首先必须各自创建一个端点,否则是没有办法建立联系并相互通信的.正如打电话之前,双方必须各自拥有一台电话机一样. 套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认. 1.服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态. 2.客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字.为此,客户端的套接字必须首先描述它要连接的服务器

以您熟悉的编程语言为例完成一个hello/hi的简单的网络聊天程序

在这片博文我们将使用python完成一个hello/hi的简单的网络聊天程序 先做一下准备工作 1.linux的socket基础api: 使用socket()创建套接字 int socket(int af, int type, int protocol); af为IP地址类型,AF_INE和AF_INET6分别对应ipv4和ipv6地址type是数据传输方式,Sock_stream(面向连接套接字)和sock_dgram(无连接套接字)protocol是传输协议,IPPROTO_TCP和IPPR

基于Python完成一个hello/hi的简单的网络聊天程序

一.Socket 套接字简介 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开.读写和关闭等操作.套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信.网络套接字是IP地址与端口的组合. 传输层实现端到端的通信,因此,每一个传输层连接有两个端点.那么,传输层连接的端点是什么呢?不是主机,不是主机的IP地址,不是应用进程,也不是传输层的协议端口.传输层连接的端点叫做套接字(socket).根据RFC793的定义:端口号拼接到IP

一个hello/hi的简单的网络聊天程序

我选择使用python来实现hello/hi的简单网络聊天程序,源代码包括两个部分,客户端代码和服务器端代码,源代码部分如下图所示: 服务器端代码 1 import socket 2 3 HOST = '127.0.0.1' 4 PORT = 8888 5 6 server = socket.socket() 7 server.bind((HOST, PORT)) 8 server.listen(1) 9 10 print(f'the server is listening at {HOST}:

一个hello/hi的简单的网络聊天程序和python Socket API与Linux Socket API之间的关系

1.Socket概述 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开.读写和关闭等操作.套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信.网络套接字是IP地址与端口的组合. 套接字可以看成是两个网络应用程序进行通信时,各自通信连接中的一个端点.通信时,其中的一个网络应用程序将要传输的一段信息写入它所在主机的Socket中,该Socket通过网络接口卡的传输介质将这段信息发送给另一台主机的Socket中,使这段信息能传送到

使用python实现一个hello/hi的简单的网络聊天程序

一.TCP/IP协议通信原理 TCP/IP协议包含的范围非常的广,它是一种四层协议,包含了各种硬件.软件需求的定义.TCP/IP协议确切的说法应该是TCP/UDP/IP协议.UDP协议(User Datagram Protocol 用户数据报协议),是一种保护消息边界的,不保障可靠数据的传输.TCP协议(Transmission Control Protocol 传输控制协议),是一种流传输的协议.他提供可靠的.有序的.双向的.面向连接的传输. 保护消息边界,就是指传输协议把数据当作一条独立的消

使用python完成一个hello/hi的简单的网络聊天程序

在这篇文章中,我将先简要介绍socket原理,然后给出一个利用Python实现的简单通信样例,最后通过跟踪系统调用来分析Python中socket函数与Linux系统调用的对应关系. 1.socket简介 Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议.下图为原理图: 2.简单通信样例

用c++完成一个hello/hi的简单的网络聊天程序

1.什么是Socket 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开.读写和关闭等操作.套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信.网络套接字是IP地址与端口的组合. Socket起源于Unix,而Unix/Linux 基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式 来操作.Socket就是该模式的一个实现,socket即是一种特殊的文件,一些

java实现hello/hi的简单的网络聊天程序与ServerSocket调用栈跟踪

java实现hello/hi的简单的网络聊天程序 网络聊天采用TCP协议通过java实现 import java.io.*; import java.net.Socket; public class Client { public static void main(String[] args) throws Exception{ Socket socket = new Socket("192.168.31.68", 6666); BufferedReader reader = new