• Linux 套接字通信笔记(一)


    • 协议

        TCP(传输控制协议),UDP(用户数据包协议)为传输层重要的两大协议,向上为HTTP提供底层协议,向下为数据链路层封装底层接口,乃是通信重中之重。TCP是面向流传输的协议,在编程中形象化为Stream,如流水一般,读入读出。流的基本单位为byte。而UDP则为数据包协议,以数据包为单位。协议的细节不再赘述,本次提供两种协议的最基础套接字编程模型。

    • API

        服务端最基本的流程:新建套接字->绑定端口->开始监听....->建立连接->传输数据->关闭连接

        客户端最基本的流程:新建套接字->连接...->传输数据->关闭连接

    #服务端
    #这是新建套接字的API 第一个参数ARPA Internet地址格式,第二个参数是使用流协议,第三个参数是指定协议,并未遇到应用场景 通常 传0
    int server_socketfd = socket(PF_INET,SOCK_STREAM,0)
    #这是绑定函数 绑定已申请的socket描述符到端口上,第二个参数是ip地址结构体指针,第三个是地址描述结构体的长度
    bind(server_socketfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr)
    #接受连接函数 意味开始监听 第一个参数是服务器套接字 第二个参数返回的是客户端连接的地址 第三个是地址长度
    accept(server_socketfd, (struct sockaddr *)&remote_addr,&sin_size)
    
    #客户端
    #客户端连接函数 第一个参数是socket对象描述符,第二个是ip地址,第三个是ip地址的长度
    connect(client_fd,(struct sockaddr *)&remote_addr, sizeof(struct sockaddr))
    
    #io
    #发送函数 第一个是发送的套接字描述符 第二个是内容 第三个是发送长度 第四个是flags 一般0
    send(client_socketfd,"welcome to login
    ",21,0)
    #接收函数 接受client对象的数据到buf中,一次接受bufferSize个长度,最后一个参数是flags 和send对应
    recv(client_socketfd, buf, bufferSize, 0)
    • 完整程序

        为了方便测试,我使用了C++和Java做一对交互程序,C++做服务器的时候就用Java做客户端,反之也有。

        Linux socket API存在头文件sys/socket.h中,并且期间使用多个头文件的函数,大概使用的头文件如下。

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <memory.h>

        TCP服务端Demo

    #ifndef COMMUNICAITON_TCPSOCKET_H
    #define COMMUNICAITON_TCPSOCKET_H
    #include <stdio.h>
    #include <memory.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    
    #endif //COMMUNICAITON_TCPSOCKET_H
    void tcpServerStart();
    
    #include "TCPSocket.h"
    const int bufferSize = 1024;
    
    void tcpServerStart(){
        printf("server socket start init...
    ");
        int server_socketfd;//服务器套接字
        int client_socketfd;//客户端套接字
        int port = 8000;
        struct sockaddr_in my_addr;//服务器网络地址结构
        struct sockaddr_in remote_addr;//虚拟网络地址结构
        socklen_t sin_size;//此处须运用长度定义类型 socketlen_t
        char buf[bufferSize];//数据缓冲区
        long sendLen = 0;//发送接收长度
        memset(&my_addr,0,sizeof(my_addr));//数据初始化
        my_addr.sin_family=AF_INET;//设置为IP通信
        my_addr.sin_addr.s_addr=INADDR_ANY;//服务器ip
        my_addr.sin_port=htons(port);//设置端口为8000
    
        /*创建服务器端套接字--IPv4协议,TCP协议*/
        if((server_socketfd = socket(PF_INET,SOCK_STREAM,0))<0){
            perror("socket init error");
            return ;
        }
        /*将套接字绑定到服务器的网络地址上*/
        if(bind(server_socketfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0){
            perror("bind socket error");
            return;
        }
        printf("server socket start listen ,port:%d",port);
        /*监听连接请求--监听队列长度为5*/
        listen(server_socketfd,5);
    
        sin_size = sizeof(struct sockaddr_in);
    
        /*等待客户端连接请求到达*/
        client_socketfd=accept(server_socketfd, (struct sockaddr *)&remote_addr,&sin_size);
        if(client_socketfd < 0){
            perror("listen error");
            return;
        }
        printf("accept %s",inet_ntoa(remote_addr.sin_addr));//打印客户端ip地址
    
    
        sendLen=send(client_socketfd,"welcome to login
    ",21,0);//发送欢迎信息
        if(sendLen <= 0){
            perror("no send");
            return;
        }
        while(sendLen=recv(client_socketfd, buf, bufferSize, 0)>0){
            printf("accept msg===");
            printf("%s/n",buf);
        }
        close(client_socketfd);
        close(server_socketfd);
    
    
    }

        UDP服务端Demo

    #ifndef COMMUNICAITON_UDPSOCKET_H
    #define COMMUNICAITON_UDPSOCKET_H
    
    #include <stdio.h>
    #include <memory.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #endif //COMMUNICAITON_UDPSOCKET_H
    
    void udpServerStart();

    #include "UDPSocket.h"
    
    const int bufSize = 1024;
    void udpServerStart(){
        printf("udp server init...
    ");
        int server_sockfd;
        int len;
        int port = 8000;
        struct sockaddr_in my_addr;
        struct sockaddr_in remote_addr;
        socklen_t sin_size;//此处使用其自定义的socklen属性
        char buf[bufSize];
        memset(&my_addr,0,sizeof(my_addr)); //数据初始化--清零
        my_addr.sin_family = AF_INET;
        my_addr.sin_addr.s_addr = INADDR_ANY;
        my_addr.sin_port = htons(port);
    
        /*创建服务器端套接字--IPv4协议,面向无连接通信,UDP协议*/
        if((server_sockfd=socket(PF_INET,SOCK_DGRAM,0))<0){
            perror("create socket error");
            return;
        }
        /*将套接字绑定到服务器的网络地址上*/
        if(bind(server_sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0){
            perror("bind socket error");
            return;
        }
        sin_size = sizeof(struct sockaddr_in);
        printf("waiting for package");
        /*接收客户端的数据并将其发送给客户端--recvfrom是无连接的*/
        if((len=recvfrom(server_sockfd,buf,bufSize,0,(struct sockaddr *)&remote_addr,&sin_size))<0){
            perror("recv error");
            return;
        }
        printf("received packet from %s:
    ",inet_ntoa(remote_addr.sin_addr));
        printf("contents: %s
    ",buf);
        close(server_sockfd);
    }

        其中有某些函数调用非socket API中,比如memset,inet_ntoa等。

        TCP客户端 Demo

    #ifndef COMMUNICAITON_TCPCLIENT_H
    #define COMMUNICAITON_TCPCLIENT_H
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <memory.h>
    #endif //COMMUNICAITON_TCPCLIENT_H
    void tcpClient();
    
    
    #include "TCPClient.h"
    const int bufSize = 1024;
    void tcpClient(){
        int client_fd;
        int len;
        struct sockaddr_in remote_addr;
        char buf[bufSize];
        memset(&remote_addr,0, sizeof(remote_addr));
        remote_addr.sin_family=AF_INET;
        remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
        remote_addr.sin_port=htons(8000);
    
        /*创建客户端套接字--IPv4协议,面向连接通信,TCP协议*/
        if((client_fd=socket(PF_INET,SOCK_STREAM,0))<0){
            perror("socket error");
            return;
        }
        /*将套接字绑定到服务器的网络地址上*/
        if(connect(client_fd,(struct sockaddr *)&remote_addr, sizeof(struct sockaddr))<0){
            perror("connect error");
            return;
        }
        printf("connected to server
    ");
        len=recv(client_fd,buf,bufSize,0);
        printf("get msg :%s",buf);
        close(client_fd);
    
    }

        UDP客户端 Demo

    #ifndef COMMUNICAITON_UDPCLIENT_H
    #define COMMUNICAITON_UDPCLIENT_H
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <memory.h>
    #endif //COMMUNICAITON_UDPCLIENT_H
    
    void udpClient();
    
    /*
     * 向本地(localhost) 8000端口发送一个数据包
     */
    const int buffSize = 1024;
    void udpClient(){
        int client_sockfd;
        int len ;
        struct sockaddr_in remote_addr;
        int sin_size;
        char buf[buffSize];
        memset(&remote_addr,0, sizeof(remote_addr));
        remote_addr.sin_family=AF_INET;
        remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
        remote_addr.sin_port=htons(8000);
    
        /*创建客户端套接字--IPv4协议,面向无连接通信,UDP协议*/
        if((client_sockfd=socket(PF_INET,SOCK_DGRAM,0))<0)
        {
            perror("socket not found");
            return;
        }
        stpcpy(buf,"this is a C++ udp client");
        printf("send msg :%s
    ",buf);
    
        sin_size= sizeof(struct sockaddr_in);
        /*向服务器发送数据包*/
        if((len=sendto(client_sockfd,buf,strlen(buf),0,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr)))<0){
            perror("send error");
            return;
        }
        close(client_sockfd);
    }

        用于对应的Java客户端/服务端(比较熟悉Java,用于测试)

    public class TCPClient {
        public void tcpConnectReacServerFirst(String addr,int port){
            Socket socket = null;
            try{
                socket = new Socket(addr,port);
                InputStream ins = socket.getInputStream();
                OutputStream ous = socket.getOutputStream();
                BufferedReader bufReader = new BufferedReader(new InputStreamReader(ins));
                String msg = bufReader.readLine();
                System.out.println("接到服务器信息:"+msg);
                ous.write("hello server
    ".getBytes());
                ous.flush();
    
                ins.close();
                ous.close();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(socket != null){
                    if(socket.isClosed()){
                        try{
                            socket.close();
                        }catch (Exception e){
                            e.printStackTrace();;
                        }
                    }
                }
            }
        }
    
    }
    public class UDPClient {
        public void udpSender(String addr,int port){
            byte[] msg = "this is udp sender
    ".getBytes();
            try{
                //数据包
                //数据包byte数组 数组长度 包目的ip地址 端口
                DatagramPacket datagramPacket = new DatagramPacket(msg,msg.length,InetAddress.getByName(addr),port);
                DatagramSocket socket = new DatagramSocket();
                socket.send(datagramPacket);
                if(!socket.isClosed()){
                    socket.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    public class TCPServer {
        public void tcpServerStart(int port){
            ServerSocket server = null;
            final String welcome = "welcome to login,Java TCP server!
    ";
            try{
                server = new ServerSocket(port);
                Socket socket = server.accept();
                OutputStream ous = socket.getOutputStream();
                ous.write(welcome.getBytes());
                ous.close();
                socket.close();
                server.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    public class UDPServer {
        public void udpStartup(int port,int byteCount){
            System.out.println("udp server start ,port:"+port);
            try{
                InetAddress inet = InetAddress.getLoopbackAddress();
                DatagramSocket udp = new DatagramSocket(port);
                byte[] buf = new byte[byteCount];
                DatagramPacket packet = new DatagramPacket(buf,buf.length);
    
                udp.receive(packet);
    
                String getMsg = new String(buf,0,packet.getLength());
    
                System.out.println("收到客户端 "+packet.getAddress().getHostAddress()+" 发来信息:"+getMsg);
    
                udp.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    • 体会

        C++在unix中形成了一种对对象的描述,或为句柄、或为fd,将其理解为Java中的socket对象类比一下,就容易理解了。而传入地址以及长度,应该是操作系统需要新建空间申请内存而用。总而言之,在传统通信程序中,socket的模型就是连接对象,其最大的应用就是如下

    while(true){
       socket = accept();
       Thread handler = new Handler(socket);
       handler.start();
    }

        为此基础上优化,就是添加线程池,或者加请求队列。这种模式逻辑明了,编码简单,是最容易理解的编程模型。

  • 相关阅读:
    CentOS 6.3 下编译Nginx(笔记整理)
    XMPP协议相关基础概念(Strophe学习笔记)
    StackMapTable format error
    hibernate的子查询
    strophe与openfire模拟的XMPP简单hello程序
    Strophe.Status的所有值
    博客园添加SyntaxHighlighter
    P5395 【模板】第二类斯特林数·行
    test20191001
    test20190829
  • 原文地址:https://www.cnblogs.com/chentingk/p/8438973.html
Copyright © 2020-2023  润新知