进程间通信——Socket

共享内存、管道、信号量、消息队列,他们都是多个进程在一台主机之间的通信,那两个相隔几千里的进程能够进行通信吗?答是必须的,这个时候 Socket 这家伙就派上用场了,例如我们平时通过浏览器发起一个 http 请求,然后服务器给你返回对应的数据,这种就是采用 Socket 的通信方式了。

socket编程即网络编程,有着悠久的历史,因此有一个非常固定的编程套路。

基于TCP的网络编程:
基于连接,在交互过程中,服务器和客户端要保持连接,不能断开。重发一切出错数据,数据验证,保证数据的正确性,完整性和顺序性。
缺点是消耗的资源比较大。

基于UPD的网络编程:
无连接协议,在网络交互过程中不保持连接,只需要在发送数据时连接一下,不重发,验证数据。优点是资源消耗少,数据的可靠性完整性顺序性得不到保证。

1.编程步骤:
    服务器:
        1.创建socket(套接字的)
        2.准备通信地址
        3.将创建的socket和通信地址绑定 bind()
        4.监听端口 listen()
        5.等待客户端连接 accpet()
        6.通信双方收发数据 read() / write() / send() /recv()
        7.关闭socket
    客户端:
        1.创建socket(套接字)socket()
        2.准备通信地址
        3.连接服务器connect()
        4.收发数据 read() / write()  /send() / recv()
        5.关闭socket

2.API详解:
    1.socket()函数
    int socket(int domain, int type, int protocol)
    第一个参数domain:AF_UNIX/AF_LOCAL/AF_FILE:本地通信   AF_INET:网络通信ipv4   AF_INET6:网络通信ipv6   注:如果AF换成PF效果一样 (Ubuntu是AF MacOS是PF)
    第二个参数type: 选择通信类型,主要包括:SOCK_STREAM:TCP,   SOCK_DGRAM:UDP
    第三个参数protocal: 本来应该指定通信协议,但现在基本废弃,因为协议已经在前面两个参数指定完成,给0即可。

    2.bind()函数
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
    第一个参数sockfd:要绑定的套接字描述符。
    第二个参数add:涉及三个数据结构struct sockaddr, sockaddr_un, sockaddr_in
                sockaddr,主要用于函数参数,不负责存储数据
                sockaddr_un, 当本地通信时,用于本地通信使用的地址(sys/un.h)
                sockaddr_in, 当网络通信时,负责存储网络通信的地址数据
                struct sockaddr_in {
                    sin_family, //用于指定协议族,和socket()的参数保持一致
                    sin_port, //网络通信使用的端口号
                    sim_addr,//存储网络通信的ip地址
                }
    第三个参数addrlen:参数占据的内存空间大小

    3.htons

    4.inet_aton

    5.listen()函数
    int listen(int sockfd, int backlog)
    第一个参数sockfd:将sockfd参数所标识的套接字为被动模式,使之可以接受连接请求
    第二个参数backlog:表示未决连接请求队列的最大长度,即允许最多有多少个未决连接请求存在。若服务器的未决连接请求已达到该值,
                    则客房端通过connect()连接服务器的操作将返回-1,且error为ECONNERFUSED。
                    (backlog的大小 是表示内核最大能并发处理的正在进行3次握手,还没有真正建立好连接的socket数量)

    6.accpet()函数
    int accept(int sockfd, struct sockaddr* addr, socklen_t *addrlen)
    从sockfd参数所标识套接字对应未决连接请求队列中取出一个连接请求,同时创建一个新的套接字,用于该连接通信,返回套按字的描述符
    addr和addrlen用于输出连接请求发起者的地址信息
    返回值:为新创建用于和客户端通信的套接字描述符 失败-1, error

    7.inet_ntoa

    8.recv() 函数
    ssize_t recv(int socket, void *buffer, size_t length, int flags);
    flags,通常取0,阻塞收到数据
        O_NONBLOCK:不阻塞,如果未收到数据,返回错误信息
    返回值:
        >0,实际接受数据字节数
        -1, 出错,error
        0, 通信的另一端关闭。

    9.send()函数
    ssize_t send(int socket, const void *buffer, size_t length, int flags);
    flags:通常取0,阻塞发送
    O_NONBLOCK:不阻塞,如果未收到数据,返回错误信息

    10 connect()函数
    int connect(int socket, const struct sockaddr *address, socklen_t address_len);
    也bind()一样。

 

下面看具体代码实现:

tcpClient.c 

//
//  tcpClient.c
//  socket
//
//  Created by chenyijun on 2020/2/8.
//  Copyright © 2020 chenyijun. All rights reserved.
//

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

#define PORT 5555

int main(int argc, const char * argv[]) {
    // 1.创建socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd)
    {
        printf("client socket failed========\n");
        exit(-1);
    }
    else
    {
        printf("client socket success========\n");
    }
    
    //2.准备通信地址
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    //设置为服务器进程的端口号
    addr.sin_port = htons(PORT);
    //服务器所在主机的IP地址
    inet_aton("192.168.1.102", &addr.sin_addr);
    //3.连接服务器
    int res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
    if(-1 == res)
    {
        printf("connect failed========\n");
        exit(-1);
    }
    printf("connect server success=======\n");
    //4.和服务器交换数
    char buf[100] = {0};
    char *str = "this is scott";
    write(sockfd, str, strlen(str));
    read(sockfd, buf, sizeof(buf));
    printf("server say:%s\n", buf);
    
    printf("Hello, tcpClient!\n");
    return 0;
}

tcpServer.c

//
//  tcpServer.c
//  socket
//
//  Created by chenyijun on 2020/2/8.
//  Copyright © 2020 chenyijun. All rights reserved.
//

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

#define PORT 5555

int main(int argc, const char * argv[]) {
    // 1.创建socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd)
    {
        printf("server socket failed====\n");
        exit(-1);
    }
    else
    {
        printf("server socket success====\n");
    }
    //2.准备通信地址
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    inet_aton("192.168.1.102", &addr.sin_addr);
    
    //3.绑定socket和通信地址
    int res = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
    if(-1 == res)
    {
        printf("server bind failed===========\n");
        exit(-1);
    }
    //4.监听端口
    res = listen(sockfd, 100);
    if(res == -1)
    {
        printf("sever listen failed=======\n");
        exit(-1);
    }
    printf("start listen %d port, wait client connect.....\n", PORT);
    //5.处理接收客户端的连接请求,
    //用于保存客户端地址信息
    struct sockaddr_in fromaddr;
    //len取0会是什么效果?
    socklen_t len = sizeof(fromaddr);
    int clientfd = accept(sockfd, (struct sockaddr *)&fromaddr, &len);
    if(-1 == clientfd)
    {
        printf("sever accept failed=========\n");
        exit(-1);
    }
    printf("have a client connent sever , is : %s\n", inet_ntoa(fromaddr.sin_addr));
    //6.处理客户端数据
    char buf[100] = {0};
    ssize_t count = recv(clientfd, buf, sizeof(buf), 0);
    printf("from client read %ld byte : %s\n", count, buf);
    char *str = "welcome you client";
    send(clientfd, str, strlen(str), 0);
    //关闭连接
    close(clientfd);
    close(sockfd);
    
    
    printf("Hello, World!\n");
    return 0;
}

运行结果:

参考:

https://www.cnblogs.com/jiangson/p/5977601.html
https://blog.csdn.net/weixin_30706507/article/details/97202925
https://www.cnblogs.com/developing/articles/10979088.html

 

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值