socket 编程-简单介绍

使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。

socket进程通信与网络通信使用的是统一套接口,只是地址结构与某些参数不同。

其主要流程如下:

 

1.服务端首先初始化Socket(),然后和接口进行绑定bind()和监听listen(),然后调用accept()进行阻塞。
2.客户端初始化socket(),然后调用connect()与服务端进行连接,然后客户端负责发送请求,服务端接收并且处理请求。write(),read().在 UNIX/Linux 系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。

记录常用的函数接口

一. 创建socket
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:创建套接字
参数:domain: 通信域,协议族,可取如下值:
    PF_LOCAL/PF_UNIX -本地套接字,进程间通信
    PF_INET -基于IPV4的网络通信
    PF_INET6 -基于IPV4的网络通信
    PF_PACKET -基于底层包的网络通信

type:SOCK_STREAM SOCK_DGRAM SOCK_RAW
protocol:指定具体的协议,应被设置为0

返回值:
为生成的套接字描述符


二. 设置socket参数

SOCK_STREAM式本地套接字的通信双方均需要有本地地址,其中服务器端的本地地址需要明确指定,指定方法是使用struct sockaddr_un类型的变量
struct sockaddr_un{
    sa_family_t sun_family; // AF_UNIX
    char sun_path[UNIX_PATH_MAX]; // 路径名
}

struct sockaddr_in {
    sa_family sin_family; //AF_
    in_port_t sin_port;
    struct in_addr sin_addr;
};
struct in_addr {
    in_addr_t s_addr;
};

三. 绑定
int bind(int socket, const struct sockaddr *address, size_t address_len);

参数
socket:服务端套接字描述符
address:需要绑定的服务端本地地址
address_len:本地地址的字节长度

 

四. 监听
服务器端套接字创建完毕并赋予本地地址值(名称,本例中为CAN_SERVICE)后,需要进行监听,等待客户端连接并处理请求,监听使用 listen 系统调用,
int listen(int socket, int backlog);
参数
socket:表示服务器端的套接字描述符;
backlog 表示排队连接队列的长度(若有多个客户端同时连接,则需要进行排队);

五、阻塞等待
接受客户端连接使用accept系统调用,它们的原形如下:
int accept(int socket, struct sockaddr *address, size_t *address_len);
参数
address 表示当前连接客户端的本地地址,该参数为输出参数,是客户端传递过来的关于自身的信息;
address_len 表示当前连接客户端本地地址的字节长度,这个参数既是输入参数,又是输出参数。实现监听、接受和处理。


六. 连接
客户端需要 调用connect()连接到服务端,其函数原型如下:

int connect(int socket, const struct sockaddr *address, size_t address_len);
参数
socket:客户端的套接字描述符
address:当前客户端的本地地址,是一个 struct sockaddr_un 类型的变量
address_len:表示本地地址的字节长度

七. 数据交互

无论客户端还是服务器,都要和对方进行数据上的交互。一个进程扮演客户端的角色,另外一个进程扮演服务器的角色,
两个进程之间相互发送接收数据,这就是基于本地套接字的进程通信。

循环读取客户端发送的消息,当客户端没有发送数据时会阻塞直到有数据到来。如果想要多个连接并发处理,需要创建线程,
将每个连接交给相应的线程并发处理。接收到数据后,进行相应的处理,将结果返回给客户端。发送和接收数据要使用 write 和 read 系统调用,它们的原形为:
int read(int socket, char *buffer, size_t len);
int write(int socket, char *buffer, size_t len);

八. 字节序转化

tcp_addr.sin_addr.s_addr = inet_addr(“192.168.101.128”);
printf("s_addr=%u, 0x%x\n", tcp_addr.sin_addr.s_addr, tcp_addr.sin_addr.s_addr);
#打印如下
s_addr=2221254848, 0x8465a8c0

192.168.101.128 转换后的字节序是 0x8065a8c0  个人理解 已经转化成网络字节序了

代码示例:

服务端代码

#include "stdio.h"
#include "stdlib.h"
#include <unistd.h>
#include "sys/types.h"
#include <sys/stat.h>
#include "string.h"
#include <arpa/inet.h>
#include <sys/un.h>

int main(int argc, char *argv[])
{
    int lfd ,ret, cfd;
    struct sockaddr_un serv, client;
    socklen_t len = sizeof(client);
    char buf[1024] = {0};
    int recvlen;

    //创建socket
    lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (lfd == -1) {
        perror("socket error");
        return -1;
    }

    //初始化server信息
    serv.sun_family = AF_LOCAL;
    strcpy(serv.sun_path, "server.sock");
    unlink("server.sock");
    //绑定
    ret = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
    if (ret == -1) {
        perror("bind error");
        return -1;
    }

    //设置监听,设置能够同时和服务端连接的客户端数量
    ret = listen(lfd, 36);
    if (ret == -1) {
        perror("listen error");
        return -1;
    }

    //等待客户端连接
    cfd = accept(lfd, (struct sockaddr *)&client, &len);
    if (cfd == -1) {
        perror("accept error");
        return -1;
    }
    printf("=====client bind file:%s\n", client.sun_path);

    while (1) {
        recvlen = recv(cfd, buf, sizeof(buf), 0);
        if (recvlen == -1) {
            perror("recv error");
            return -1;
        } else if (recvlen == 0) {
            printf("client disconnet...\n");
            close(cfd);
            break;
        } else {
            printf("ser:recv buf %s\n", buf);
            send(cfd, buf, recvlen, 0);
        }
    }

    close(cfd);
    close(lfd);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    int lfd ,ret;
    struct sockaddr_un serv, client;
    socklen_t len = sizeof(client);
    char buf[1024] = {0};
    int recvlen;

    //创建socket
    lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (lfd == -1) {
        perror("socket error");
        return -1;
    }

    //给客户端绑定一个套接字文件
    client.sun_family = AF_LOCAL;
    strcpy(client.sun_path, "client.sock");
    unlink("client.sock");
    ret = bind(lfd, (struct sockaddr *)&client, sizeof(client));
    if (ret == -1) {
        perror("bind error");
        return -1;
    }

    //初始化server信息
    serv.sun_family = AF_LOCAL;
    strcpy(serv.sun_path, "server.sock");
    //连接
    connect(lfd, (struct sockaddr *)&serv, sizeof(serv));

    while (1) {
        fgets(buf, sizeof(buf), stdin);
        send(lfd, buf, strlen(buf)+1, 0);

        recv(lfd, buf, sizeof(buf), 0);
        printf("cli:recv buf %s\n", buf);
    }

    close(lfd);
    return 0;
}

 

posted @ 2024-10-15 12:53  靖意风  Views(38)  Comments(0)    收藏  举报