JAVA实现hello/hi的简单的网络聊天程序

一、TCP/IP

TCP/IP 即传输控制协议/网间协议是在网络的使用中最基本的通信协议。TCP/IP 传输协议对互联网中各部分进行通信的标准和方法进行了规定。并且,TCP/IP 传输协议是保证网络数据信息及时、完整传输的两个重要的协议。

二、socket

套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,可以用它们来开发TCP/IP网络上的应用程序。

Client/Server通信模式中我们将请求服务的一方称为客户(client),将提供某种服务的一方称为服务器(server),一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说服务进程一直处于休眠状态,直到一个客户对这个服务的地址提出了连接请求。在这个时刻,服务程序被“唤醒”并且为客户提供服务—对客户的请求作出适当的反应。

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

服务端代码:

package socket;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class server {
    public static void main(String[] args) throws Exception{
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端已启动,等待客户端连接...");
        Socket socket = serverSocket.accept();
        System.out.println("客户端"+socket.getPort()+"已连接");
        
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        BufferedReader bufferedReader2 = new BufferedReader(new InputStreamReader(System.in));
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(socket.getOutputStream());
        
        String msg = null;
        while ((msg = bufferedReader.readLine()) != null) {
            System.out.println("客户端发来消息:"+msg);
            System.out.println("服务端:");
            msg = bufferedReader2.readLine();
            outputStreamWriter.write(msg+"\n");
            outputStreamWriter.flush();
        }
        serverSocket.close();
    }
}

客户端代码:

package socket;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class client {
    public static void main(String[] args) throws Exception{
        Socket socket = new Socket("127.0.0.1", 8888);
        System.out.println("开始聊天吧!");
        
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        BufferedReader bufferedReader2 = new BufferedReader(new InputStreamReader(System.in));
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(socket.getOutputStream());
        
        String msg = null;
        System.out.println("客户端:");
        while ((msg = bufferedReader2.readLine()) != null) {
            outputStreamWriter.write(msg+"\n");
            outputStreamWriter.flush();
            msg = bufferedReader.readLine();
            System.out.println("服务端发来消息:"+msg);
        }
        socket.close();
    }
}

运行结果:

客户端:

服务端:

四、对比JAVA函数和Linux Socket API

Linux socket API:

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

domain 表示使用的底层协议族 , 如PF_INET (TCP/IPv4),PF_INET6 (TCP/IPv6)等;

type 表示指定服务类型,主要有SOCK_STREAM(TCP流服务)和SOCK_DGRAM(UDP数据报)等服务;

protocol 表示具体传输协议,如IPPROTO_TCP、IPPROTO_UDP等;

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

sockfd 表示需要命名(绑定)的目标socket文件描述符;

my_addr 表示将socket地址绑定至sockfd,即命名该sockfd;

addrlen 表示该地址的长度;

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

sockfd 表示创建的socket 文件描述符;
backlog 表示提示内核监听队列中处于完全连接状态(ESTABLISHED)socket的最大长度,而半连接状态(SYN_RCVD)socket队列的最大长度定义在/proc/sys/net/ipv4/tcp_max_syn_backlog,若队列满,则peer客户端(connect())将收到ECONNREFUSED;

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

sockfd 表示已监听的socket文件描述符;

addr 表示用来获取远端的socket地址;

addrlen 表示地址的长度;

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

sockfd 表示客户端创建的socket文件描述符;

serv_addr 表示服务器端socket地址;

addrlen 表示服务器端socket地址的大小;

#include <sys/unistd.h>
#include <sys/socket.h>
int close(int sockfd);

close函数会将本地sockfd的读写两个方向全部关闭,并将sockfd的引用计数减一;

JAVA API:

server中ServreSocket函数:

ServerSocket serverSocket = new ServerSocket(8888);

追踪ServerSocket函数的调用栈:

结合java.net.serverSocket的API:

可知代码创建套接字同时绑定了指定端口。

client中Socket函数:

Socket socket = new Socket("127.0.0.1", 8888);

追踪Socket函数的调用栈:

结合java.net.socket的API:

可知代码通过给定地址和端口号创建了流套接字,即协议族为TCP。

bind(new InetSocketAddress(bindAddr, port), backlog);

追踪调用栈:

java.net.ServerSocket的API:

java.net.socket的API:

listen(backlog);

追踪调用栈:

serverSocket.accept();

追踪调用栈:

java.net.ServerSocket的API:

socket.close();
serverSocket.close();

追踪调用栈:

五、总结

通过对这几个函数进行追踪分析,并于Linux socket API进行对比,不难看出,在创建一个ServerSocket(JAVA)对象时就相当于进行了一下执行了Linux Socket API中的socket()、bind()、linsten()三个函数,而JAVA API和Linux Socket API中的accpet()和close()函数类似。

猜你喜欢

转载自www.cnblogs.com/melatonin/p/12011264.html
今日推荐