javasocket编程以及与linuxsocketapi关系

温冷      2022-05-20     459

关键词:

Socket 编程(基于Linux)

Socket独立于具体协议的网络编程接口,在ISO模型中,主要位于会话层和传输层之间;在通用的计算机网络五层模型中,主要位于应用层和传输层之间。

Linux Socket

  • 基本上就是BSD Socket
  • 需要使用的头文件
    • 数据类型:#include <sys/types.h>
    • 函数定义:#include <sys/socket.h>

Socket类型

套接字是一种通信机制,通信两方的一种约定,用套接字中的相关函数来完成通信过程。根据传输内容分为流式套接字、数据报套接字、原始套接字;根据使用方式分为主动套接字和被动套接字。

流式套接字(SOCK_STREAM)

提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。(面向TCP)

数据报套接字(SOCK_DGRAM)

提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。(面向UDP)

原始套接字(SOCK_RAW)

可以对较低层次协议,如IP、ICMP直接访问。

主动套接字和被动套接字

创建方式相同,使用方式不同

  • 等待传入连接的套接字——被动,如服务器套接字
  • 发起连接的套接字——主动,如客户套接字

指明端点地址:创建时不指定,使用时指明:

  • TCP/IP需要指明协议端口号和IP地址
  • TCP/IP协议族:PF_INET
  • TCP/IP的地址族:AF_INET

Linux Socket API函数

  1. socket 创建套接字
  2. connect 建立连接
  3. bind 绑定本机端口
  4. listen 监听端口
  5. accept 接受连接
  6. recv, recvfrom 数据接收
  7. send, sendto 数据发送
  8. close, shutdown 关闭套接字

TCP下通信调用Linux Socket API流程如下:


UDP下通信调用Linux Socket API流程如下:

Linux Socket API函数详解

socket函数

int socket( int domain, int type, int protocol)

功能:创建一个新的套接字,返回套接字描述符
参数说明:

  • domain:域类型,指明使用的协议栈,如TCP/IP使用的是 PF_INET
  • type: 指明需要的服务类型, 如:
    • SOCK_DGRAM: 数据报服务,UDP协议
    • SOCK_STREAM: 流服务,TCP协议
  • protocol:一般都取0

举例:s=socket(PF_INET,SOCK_STREAM,0)

connect函数

int connect(int sockfd,struct sockaddr *server_addr,int sockaddr_len)

功能: 同远程服务器建立主动连接,成功时返回0,若连接失败返回-1。

参数说明:

  • Sockfd:套接字描述符,指明创建连接的套接字
  • Server_addr:指明远程端点:IP地址和端口号
  • sockaddr_len :地址长度

bind函数

int bind(int sockfd,struct sockaddr * my_addr,int addrlen)

功能:为套接字指明一个本地端点地址TCP/IP协议使用sockaddr_in结构,包含IP地址和端口号,服务器使用它来指明熟知的端口号,然后等待连接

参数说明:

  • Sockfd:套接字描述符,指明创建连接的套接字
  • my_addr:本地地址,IP地址和端口号
  • addrlen :地址长度

举例:bind(sockfd, (struct sockaddr *)&address, sizeof(address));

listen函数(TCP)

int listen(int sockfd,int input_queue_size)

功能:面向连接的服务器使用它将一个套接字置为被动模式,并准备接收传入连接。用于服务器,指明某个套接字连接是被动的

参数说明:

  • Sockfd:套接字描述符,指明创建连接的套接字
  • input_queue_size:该套接字使用的队列长度,指定在请求队列中允许的最大请求数

举例:listen(sockfd,20)

accept函数(TCP)

int accept(int sockfd, void *addr, int *addrlen);

功能:获取传入连接请求,返回新的连接的套接字描述符。为每个新的连接请求创建了一个新的套接字,服务器只对新的连接使用该套接字,原来的监听套接字接受其他的连接请求。新的连接上传输数据使用新的套接字,使用完毕,服务器将关闭这个套接字。

参数说明:

  • sockfd:套接字描述符,指明正在监听的套接字
  • addr:提出连接请求的主机地址
  • addrlen:地址长度

举例:new_sockfd = accept(sockfd, (struct sockaddr *)&address, &addrlen);

UDP下接收与发送

sendto函数

int sendto(int sockfd, const void * data, int data_len, unsigned int flags, struct sockaddr *remaddr,int remaddr_len)

功能:基于UDP发送数据报,返回实际发送的数据长度,出错时返回-1

参数说明:

  • sockfd:套接字描述符
  • data:指向要发送数据的指针
  • data_len:数据长度
  • flags:一直为0
  • remaddr:远端地址:IP地址和端口号
  • remaddr_len :地址长度

举例:sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&address, sizeof(address));

recvfrom函数

int recvfrom(int sockfd, void *buf, int buf_len,unsigned int flags,struct sockaddr *from,int *fromlen);

功能:从UDP接收数据,返回实际接收的字节数,失败时返回-1
参数说明:

  • sockfd:套接字描述符
  • buf:指向内存块的指针
  • buf_len:内存块大小,以字节为单位
  • flags:一般为0
  • from:远端的地址,IP地址和端口号
  • fromlen:远端地址长度

举例:recvfrom(sockfd,buf,8192,0, ,(struct sockaddr *)&address, &fromlen);

TCP下接收与发送

send函数

int send(int sockfd, const void * data, int data_len, unsigned int flags)

功能:在TCP连接上发送数据,返回成功传送数据的长度,出错时返回-1。send会将外发数据复制到OS内核中

参数说明:

  • sockfd:套接字描述符
  • data:指向要发送数据的指针
  • data_len:数据长度
  • flags:一直为0

举例:send(s,req,strlen(req),0);

recv函数

int recv(int sockfd, void *buf, int buf_len,unsigned int flags); 

功能:从TCP接收数据,返回实际接收的数据长度,出错时返回-1。服务器使用其接收客户请求,客户使用它接受服务器的应答。如果没有数据,将阻塞,如果收到的数据大于缓存的大小,多余的数据将丢弃。

参数说明:

  • sockfd:套接字描述符
  • Buf:指向内存块的指针
  • Buf_len:内存块大小,以字节为单位
  • flags:一般为0

举例:recv(sockfd,buf,8192,0)

close函数

close(int sockfd); 

功能:撤销套接字。如果只有一个进程使用,立即终止连接并撤销该套接字,如果多个进程共享该套接字,将引用数减一,如果引用数降到零,则撤销它。

参数说明:

  • Sockfd:套接字描述符

举例:close(socket_descriptor)

转换函数

1.IP地址转换函数

  • inet_addr() 点分十进制数表示的IP地址转换为网络字节序的IP地址
  • inet_ntoa() 网络字节序的IP地址转换为点分十进制数表示的IP地址
    2.字节排序函数
  • htonl 4字节主机字节序转换为网络字节序
  • ntohl  4字节网络字节序转换为主机字节序
  • htons 2字节主机字节序转换为网络字节序
  • ntohs 2字节网络字节序转换为主机字节序

域名解析等相关函数

  • gethostname 获得主机名
  • getpeername 获得与套接口相连的远程协议地址
  • getsockname 获得套接口本地协议地址
  • gethostbyname 根据主机名取得主机信息
  • gethostbyaddr 根据主机地址取得主机信息
  • getprotobyname 根据协议名取得主机协议信息
  • getprotobynumber 根据协议号取得主机协议信息
  • getservbyname 根据服务名取得相关服务信息
  • getservbyport 根据端口号取得相关服务信息
  • getsockopt/setsockopt 获取/设置一个套接口选项
  • ioctlsocket 设置套接口的工作方式

java下的TCP socket编程

java.net.Socket继承于java.lang.Object,有八个构造器,其方法并不多。套接字Socket已经写好封装在java.net.Socket包里。服务器端有特定的ServerSocket方法。

ServerSocket有以下3个属性:

  • SO_TIMEOUT:表示等待客户连接的超时时间。一般不设置,会持续等待。
  • SO_REUSEADDR:表示是否允许重用服务器所绑定的地址。一般不设置,经我的测试没必要,下面会进行详解。
  • SO_RCVBUF:表示接收数据的缓冲区的大小。一般不设置,用系统默认就可以了。

对于Socket类有如下常用方法:

  • accept方法用于产生”阻塞”,直到接受到一个连接,并且返回一个客户端的Socket对象实例。”阻塞”是一个术语,它使程序运行暂时”停留”在这个地方,直到一个会话产生,然后程序继续;通常”阻塞”是由循环产生的。
  • getInputStream方法获得网络连接输入,同时返回一个InputStream对象实例。
  • getOutputStream方法连接的另一端将得到输入,同时返回一个OutputStream对象实例。

注意:其中getInputStream和getOutputStream方法均会产生一个IOException,它必须被捕获,因为它们返回的流对象,通常都会被另一个流对象使用。

java与linux api调用关系

TCP服务器端Socket通信流程

  1. 创建ServerSocket对象,绑定监听端口。
  2. 通过accept()方法监听客户端请求。
  3. 连接建立后,通过输入流读取客户端发送的请求信息。
  4. 通过输出流向客户端发送响应信息。
  5. 关闭响应的资源。

TCP客户端通信流程

  1. 创建Socket对象,指明需要连接的服务器的地址和端口号。
  2. 连接建立后,通过输出流向服务器发送请求信息。
  3. 通过输入流获取服务器响应的信息。
  4. 关闭相应资源。

多线程实现服务器与多客户端之间通信步骤

  1. 服务器端创建ServerSocket,循环调用accept()等待客户端连接。
  2. 客户端创建一个socket并请求和服务器端连接。
  3. 服务器端接受客户端请求,创建socket与该客户建立专线连接。
  4. 建立连接的两个socket在一个单独的线程上对话。
  5. 服务器端继续等待新的连接。

多线程下的TCP服务器端:

package socketLearn;


import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServerChat {
    public static void main(String[] args) throws IOException {
        System.out.println("--------Server--------");
        ServerSocket server = new ServerSocket(8888);
        //1. 指定端口,使用ServerSocket创建服务器
        while(true){
            //2. 阻塞地等待连接
            Socket socket = server.accept();
            System.out.println("一个客户端建立了连接");
            new Thread(()->{
                DataInputStream dis = null;
                DataOutputStream dos = null;
                BufferedReader br= new BufferedReader(new InputStreamReader(System.in));;
                try {
                    dis = new DataInputStream(socket.getInputStream());
                    dos = new DataOutputStream(socket.getOutputStream());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                boolean isRunning = true;
                while(isRunning){
                    try {
                        //3. 接收消息
                        String msg = dis.readUTF();
                        System.out.println("客户端说:"+msg);

                        //4.返回消息
                        String reMsg = br.readLine();
                        dos.writeUTF(reMsg);
                        dos.flush();

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                try {
                    dis.close();
                    socket.close();
                    server.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        }
}

多线程下的TCP客户端:

package socketLearn;

import java.io.*;
import java.net.Socket;


public class TcpClientChat {
    public static void main(String[] args) throws IOException {
        System.out.println("--------Client--------");
        //1. 建立连接,使用Socket创建客户端
        Socket client = new Socket("localhost",8888);

        boolean isRunning = true;
        BufferedReader console = new BufferedReader((new InputStreamReader(System.in)));
        DataOutputStream dos = new DataOutputStream(client.getOutputStream());
        DataInputStream dis = new DataInputStream(client.getInputStream());

        while(isRunning){
            //2. 客户端发送消息
            String msg = console.readLine();
            dos.writeUTF(msg);
            dos.flush();
            //3. 获取消息
            msg = dis.readUTF();
            System.out.println("服务器说:"+msg);
        }
        dos.close();
        dis.close();
        client.close();

    }
}

正常来说,客户端打开一个输出流,如果不做约定,也不关闭它,那么服务端永远不知道客户端是否发送完消息,那么服务端会一直等待下去,直到读取超时。

  1. 通过socket关闭
    • 关闭客户端socket,服务器端可执行后续操作,弊端也很明显,客户端不能再次发送消息也不能接收消息,而且要再次发送必须再次创建socket连接
  2. socket关闭输出流

socket.shutdownOutput();

注意不能使用outputStream.close(),若用该法关闭了输出流,那么相应的Socket也将关闭,和直接关闭Socket一个性质。

3. 通过约定的符号
双方约定一个字符或者一个短语,来当做消息发送完成的标识,通常这么做就需要改造读取方法(读取的循环条件)。

4. 通过指定长度
5. 使用DataInputStream、DataOutputStream对socket.getOutputStream,socket.getInputStream进行包装

### UDP Socket编程
* DatagramSocket:用于发送或接收数据包的套接字
* DatagramPacket;数据包
* 不需要利用IO流实现数据的传输每个数据发送单元被统一封装成数据包的方式,发送方将数据包发送到网络中,数据包在网络中去寻找他的目的地。

#### UDP接收方
1、使用DatagramSocket 指定端口创建接收端
2、准备容器封装成DatagramPacket包裹
3、阻塞式接收包裹receive(DatagramPacket p)
4、分析数据byte[] getData();getLength();
5、释放资源

```java
public class UdpServer {
 public static void main(String[] args) throws Exception{
     System.out.println("接收方启动中...");
     DatagramSocket server = new DatagramSocket(9999);

     byte[] container  = new byte[1024*60];
     DatagramPacket packet = new DatagramPacket(container, 0,container.length);
     server.receive(packet);
     byte[] datas = packet.getData();
     int len = packet.getLength();
     System.out.println(new String(datas,0,len,"UTF-8"));

     server.close();
 }
}

UDP发送方

1、使用DatagramSocket 指定端口创建发送端
2、准备数据一定转成字节数组
3、封装成DatagramPacket包裹,需要指定目的地4、发送包裹send(DatagramPacket p)
5、释放资源

public class UdpClient {
    public static void main(String[] args) throws Exception{
        System.out.println("发送方启动中...");
        DatagramSocket client = new DatagramSocket(8888);
        String data = "改革春风吹满地";
        byte[] datas  = data.getBytes();

        DatagramPacket packet = new DatagramPacket(datas, 0,datas.length,
                new InetSocketAddress("localhost",9999));

        client.send(packet);
        client.close();
    }
}

javasocket编程

申明:本文摘自:http://www.cnblogs.com/rocomp/p/4790340.htmlJava最初是作为网络编程语言出现的,其对网络提供了高度的支持,使得客户端和服务器的沟通变成了现实,而在网络编程中,使用最多的就是Socket。像大家熟悉的QQ、MSN都使用... 查看详情

javasocket编程学习笔记

 1.Socket通信简介及模型  JavaSocket可实现客户端--服务器间的双向实时通信。java.net包中定义的两个类socket和ServerSocket,分别用来实现双向连接的client和server端。2.Socket通信实现方法 2.1 服务器端(非多线程)   用... 查看详情

javasocket编程--聊天小案例

  很久以前写过socket聊天室,都快忘完了,心血来潮又重新写一遍。服务器端:packagecom.fancy;importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStream;importjava.io.InputStreamReader;importjava.io.OutputStream;imp 查看详情

javasocket编程学习笔记

在上一篇中,使用了javaSocket+Tcp/IP 协议来实现应用程序或客户端--服务器间的实时双向通信,本篇中,将使用UDP协议来实现Socket的通信。1.关于UDP  UDP协议(用户数据报协议)是无连接的、不可靠的、无序的,速度快,进行... 查看详情

javasocket

转载自并发编程网–ifeve.com本文链接地址:Java网络教程之SocketJava网络教程-基础 Java提供了非常易用的网络API,调用这些API我们可以很方便的通过建立TCP/IP或UDP套接字,在网络之间进行相互通信,其中TCP要比UDP更加常用,但在本教... 查看详情

javasocket编程

一、网络编程概述网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。java.net包中J2SE的API包含有类和接口,它们提供低层次的通信细节。你可以直接使用这些类和接口,来专注于解决问题,而不... 查看详情

javasocket编程简介

#JavaTCPIp编程 其实下面几张图片就可以解释简单解释tcp-ip协议的大体流程了。###计算机网络,分组报文和协议网络是一组通过通信信道相互连接的机器组成。组与组之间通过路由器连接数据通过分组报文传递协议,传输报文... 查看详情

javasocket编程api基础

 Socket是Java网络编程的基础,深入学习socket对于了解tcp/ip网络通信协议很有帮助,  此文讲解Socket的基础编程。Socket用在哪里:①、主要用在进程间,②、网络间通信。文章目录如下:一、Socket通信基本示例二、消息通信优... 查看详情

javasocket编程(li)

一、网络编程中两个主要的问题  一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输。在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一地确定In... 查看详情

javasocket网络编程常见异常(转)

转:https://www.cnblogs.com/qq78292959/p/5085559.html 1.java.net.SocketTimeoutException  这个异常比较常见,socket超时。一般有2个地方会抛出这个:    a.一个是connect的时候,这个超时参数由connect(SocketAddressendpoint,inttimeout)中的后者来... 查看详情

javasocket编程----通信是这样炼成的

http://www.cnblogs.com/rocomp/p/4790340.htmlJava最初是作为网络编程语言出现的,其对网络提供了高度的支持,使得客户端和服务器的沟通变成了现实,而在网络编程中,使用最多的就是Socket。像大家熟悉的QQ、MSN都使用了Socket相关的技术... 查看详情

javasocket编程(代码片段)

Socket概念tcp协议全称是TransmissionControlProtocol,传输控制协议,是以字节流的方式发送数据的协议。ip全称为InternetProtocol互联网协议,tcp/ip协议在四层模型中的传输层。http、ftp、telnet(ssh)远程登录服务为应用层协议࿰... 查看详情

javasocket实现udp编程

UDP简介UDP是UserDatagramProtocol的简称,中文名是用户数据报协议,是OSI(OpenSystemInterconnection,开放式系统互联)参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETFRFC768是... 查看详情

javasocket编程实现群聊(超详细)(代码片段)

Javasocket编程实现群聊最终效果文末有完整代码,创作不易,点个赞再走吧~客户端之间的交流有人退出群聊时,减少在线人数实现流程1、项目结构即原理分析功能实现多客户之间聊天实时统计在线人数图形化界面创建... 查看详情

javasocket编程解决粘包和丢包问题

##socket丢包粘包解决方式采用固定头部长度(一般为4个字节),包头保存的是包体的长度header+body包头+包体 思路是:先读出一个包头,得到包体的长度,解析出包体 publicclassSocketServer{publicstaticvoidmain(Stringargs[]){ServerSockets... 查看详情

20192306实验四《数据结构与面向对象程序设计》实验报告

...志强实验日期:2020年10月22日必修/选修:必修1.实验内容JavaSocket编程学习蓝墨云上教材《Java和Android编程》“第16章输入/输出”和“第22章网络”,学习JavaSocket编程结对编程。结对伙伴A编写客户 查看详情

20192312吴欣欣实验四《数据结构与面向对象程序设计》实验报告

...验日期:2020年10月30日必修/选修:必修1.实验内容(一)JavaSocket编程1.学习蓝墨云上教材《Java和Android编程》“第16章输入/输出”和“第22章网络”,学习JavaSocket编程2.结对编程。 查看详情

javasocket编程简单的web服务器

简单的WEB服务器一个简单的WEB服务器将由列表9.2这样构建.当然,还必须要对方法和回应事件进行改进.简单的服务器不会分析和存储请求头.新的WEB服务器将分析和存储请求,为以后的处理作准备.为了达到这个目的,你必须有一个包... 查看详情