目录

0. 准备知识

0.1 大小端概念

0.2 网络字节序和主机字节序的转换

0.3 点分十进制串转换(IP地址转换函数)

0.4 IPV4结构体:(man 7 ip)

0.5 IPV6套接字结构体:(man 7 ipv6)

0.6 通用套接字结构体

1. 网络套接字函数

1.1 socket

1.2 connect

1.3 bind

1.4 listen

1.5 accept

1.6 端口复用

2. 包裹函数

2.1 wrap.c

2.2 wrap.h

3.TCP服务器

3.1 简单版

3.2 多进程版

3.3 多线程版

4. UDP服务器

5. 本地套接字

总结:


0. 准备知识

0.1 大小端概念

大端存储模式:是指数据的低位字节序保存在内存的高地址中,而数据的高位字节序保存在内存的低地址中
小端存储模式:是指数据的低位字节序保存在内存的低地址中,而数据的高位字节序保存在内存的高地址中

当以不同的存储方式,存储数据为0x12345678时:
【socket编程】TCP服务器、UDP服务器、本地套接字【C语言代码实现】-LMLPHP

0.2 网络字节序和主机字节序的转换

        TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。如果主机是大端字节序的,发送和接收都不需要做转换。同理,32位的IP地址也要考虑网络字节序和主机字节序的问题。

        为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换

代码示例1:

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
    char buf[4] = {
        192, 168, 1, 2
    };
    unsigned int num = *(int*)buf;
    unsigned int sum = htonl(num);
    unsigned char* p = (unsigned char*)&sum;

    printf("%d %d %d %d\n", *p, *(p + 1), *(p + 2), *(p + 3));

    unsigned short a = 0x0102;
    unsigned short b = htons(a);
    printf("%#x\n", b);

    return 0;
}

执行截图:

【socket编程】TCP服务器、UDP服务器、本地套接字【C语言代码实现】-LMLPHP

 代码示例2:

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
    unsigned char buf[4] = {
        1, 1, 168, 192
    };
    int num = *(int*)buf;
    int sum = ntohl(num);
    unsigned char* p = (unsigned char*)&sum;

    printf("%d %d %d %d\n", *p, *(p + 1), *(p + 2), *(p + 3));

    return 0;
}

执行截图:

 【socket编程】TCP服务器、UDP服务器、本地套接字【C语言代码实现】-LMLPHP

0.3 点分十进制串转换(IP地址转换函数)

        我们通常见到的ip地址是字符串“192.168.1.2”这种类型的,需要进行转换才行。

代码案例:

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
    char buf[] = "192.168.1.2";
    unsigned int num = 0;

    inet_pton(AF_INET, buf, &num);
    unsigned char* p = (unsigned char*)&num;
    printf("%d %d %d %d\n", *p, *(p + 1), *(p + 2), *(p + 3));

    char ip[16] = "";
    inet_ntop(AF_INET, &num, ip, 16);
    printf("%s\n", ip);

    return 0;
}

执行截图:

【socket编程】TCP服务器、UDP服务器、本地套接字【C语言代码实现】-LMLPHP

网络通讯解决三大问题:协议,IP,端口

0.4 IPV4结构体:(man 7 ip)

0.5 IPV6套接字结构体:(man 7 ipv6)

0.6 通用套接字结构体

注意:通常用以下形式

1. 网络套接字函数

1.1 socket

1.2 connect

1.3 bind

1.4 listen

1.5 accept

1.6 端口复用

在server代码的socket和bind调用之间插入如下代码:

注意:程序中设置某个端口重新使用,在这个之前的其他网络程序将不能使用这个端口 

2. 包裹函数

2.1 wrap.c

#include <stdlib.h>                                                                                                                                       
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>

void perr_exit(const char* s)
{
    perror(s);
    exit(-1);
}

int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr)
{
    int n;

again:
    if ((n = accept(fd, sa, salenptr)) < 0) {
        if ((errno == ECONNABORTED) || (errno == EINTR))//如果是被信号中断和软件层次中断,不能退出
            goto again;
        else
            perr_exit("accept error");
    }
    return n;
}

int Bind(int fd, const struct sockaddr* sa, socklen_t salen)
{
    int n;

    if ((n = bind(fd, sa, salen)) < 0)
        perr_exit("bind error");

    return n;
}
int Connect(int fd, const struct sockaddr* sa, socklen_t salen)
{
    int n;

    if ((n = connect(fd, sa, salen)) < 0)
        perr_exit("connect error");

    return n;
}

int Listen(int fd, int backlog)
{
    int n;

    if ((n = listen(fd, backlog)) < 0)
        perr_exit("listen error");

    return n;
}

int Socket(int family, int type, int protocol)
{
    int n;

    if ((n = socket(family, type, protocol)) < 0)
        perr_exit("socket error");

    return n;
}
ssize_t Read(int fd, void* ptr, size_t nbytes)
{
    ssize_t n;

again:
    if ((n = read(fd, ptr, nbytes)) == -1) {
        if (errno == EINTR)//如果是被信号中断,不应该退出
            goto again;
        else
            return -1;
    }
    return n;
}

ssize_t Write(int fd, const void* ptr, size_t nbytes)
{
    ssize_t n;

again:
    if ((n = write(fd, ptr, nbytes)) == -1) {
        if (errno == EINTR)
            goto again;
        else
            return -1;
    }
    return n;
}

int Close(int fd)
{
    int n;
    if ((n = close(fd)) == -1)
        perr_exit("close error");

    return n;
}

/*参三: 应该读取固定的字节数数据*/
ssize_t Readn(int fd, void* vptr, size_t n)
{
    size_t  nleft;              //usigned int 剩余未读取的字节数
    ssize_t nread;              //int 实际读到的字节数
    char* ptr;

    ptr = vptr;
    nleft = n;

    while (nleft > 0) {
        if ((nread = read(fd, ptr, nleft)) < 0) {
            if (errno == EINTR)
                nread = 0;
            else
                return -1;
        }
        else if (nread == 0)
            break;

        nleft -= nread;
        ptr += nread;
    }
    return n - nleft;
}

/*:固定的字节数数据*/
ssize_t Writen(int fd, const void* vptr, size_t n)
{
    size_t nleft;
    ssize_t nwritten;
    const char* ptr;

    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        if ((nwritten = write(fd, ptr, nleft)) <= 0) {
            if (nwritten < 0 && errno == EINTR)
                nwritten = 0;
            else
                return -1;
        }

        nleft -= nwritten;
        ptr += nwritten;
    }
    return n;
}

static ssize_t my_read(int fd, char* ptr)
{
    static int read_cnt;
    static char* read_ptr;
    static char read_buf[100];

    if (read_cnt <= 0) {
    again:
        if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
            if (errno == EINTR)
                goto again;
            return -1;
        }
        else if (read_cnt == 0)
            return 0;
        read_ptr = read_buf;
    }
    read_cnt--;
    *ptr = *read_ptr++;

    return 1;
}

ssize_t Readline(int fd, void* vptr, size_t maxlen)
{
    ssize_t n, rc;
    char    c, * ptr;

    ptr = vptr;
    for (n = 1; n < maxlen; n++) {
        if ((rc = my_read(fd, &c)) == 1) {
            *ptr++ = c;
            if (c == '\n')
                break;
        }
        else if (rc == 0) {
            *ptr = 0;
            return n - 1;
        }
        else
            return -1;
    }
    *ptr = 0;

    return n;
}

int tcp4bind(short port, const char* IP)
{
    struct sockaddr_in serv_addr;
    int lfd = Socket(AF_INET, SOCK_STREAM, 0);
    bzero(&serv_addr, sizeof(serv_addr));
    if (IP == NULL) {
        //如果这样使用 0.0.0.0,任意ip将可以连接
        serv_addr.sin_addr.s_addr = INADDR_ANY;
    }
    else {
        if (inet_pton(AF_INET, IP, &serv_addr.sin_addr.s_addr) <= 0) {
            perror(IP);//转换失败
            exit(1);
        }
    }
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    //端口复用
    int opt = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    Bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    return lfd;
}

2.2 wrap.h

#ifndef __WRAP_H_                                                                                                                                         
#define __WRAP_H_
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>

void perr_exit(const char* s);
int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr);
int Bind(int fd, const struct sockaddr* sa, socklen_t salen);
int Connect(int fd, const struct sockaddr* sa, socklen_t salen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void* ptr, size_t nbytes);
ssize_t Write(int fd, const void* ptr, size_t nbytes);
int Close(int fd);
ssize_t Readn(int fd, void* vptr, size_t n);
ssize_t Writen(int fd, const void* vptr, size_t n);
ssize_t my_read(int fd, char* ptr);
ssize_t Readline(int fd, void* vptr, size_t maxlen);
int tcp4bind(short port, const char* IP);
#endif

3.TCP服务器

socket模型创建流程图:

【socket编程】TCP服务器、UDP服务器、本地套接字【C语言代码实现】-LMLPHP

3.1 简单版

client.c

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

#define SERVER_IP "192.168.0.105"
#define SERVER_PORT 8008
int main()
{
    //创建套接字
    int sock_fd;
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    //连接服务器
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &addr.sin_addr);
    connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr));
    //读写数据
    char buf[1024] = "";
    while (1)
    {
        int n = read(STDIN_FILENO, buf, sizeof(buf));
        write(sock_fd, buf, n);//发送数据
        n = read(sock_fd, buf, sizeof(buf));
        write(STDOUT_FILENO, buf, n);
    }
    //关闭
    close(sock_fd);

    return 0;
}

server.c

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

#define SERVER_PORT 8008
#define SERVER_IP "192.168.0.106"
#define BACKLOG 128
int main()
{
    //创建套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    //绑定
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    //addr.sin_addr.s_addr = INADDR_ANY;  //绑定的是通配地址
    inet_pton(AF_INET, SERVER_IP, &addr.sin_addr.s_addr);
    bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
    //监听
    listen(lfd, BACKLOG);
    //提取
    struct sockaddr_in cliaddr;
    socklen_t len = sizeof(cliaddr);
    int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len);
    char ip[16] = "";
    printf("new client ip = %s port = %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, 16), ntohs(cliaddr.sin_port));
    //读写
    char buf[1024] = "";
    while (1)
    {
        bzero(buf, sizeof(buf));
        int n = read(STDIN_FILENO, buf, sizeof(buf));
        write(cfd, buf, n);
        n = read(cfd, buf, sizeof(buf));
        printf("%s\n", buf);
    }
    //关闭
    close(lfd);
    close(cfd);

    return 0;
}

客户端和服务器启动后可以使用netstat命令查看链接情况:

3.2 多进程版

server.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include "wrap.h"

#define SERVER_PORT 8000
#define SERVER_IP "192.168.0.106" 
#define BACKLOG 128

void free_process(int sig)
{
    pid_t pid;
    while ((pid = waitpid(-1, NULL, WNOHANG)) > 0)
    {
        printf("child pid = %d has exited\n", pid);
    }
}
void handle_client(int cfd)
{
    char buf[1024];
    ssize_t n;

    while ((n = read(cfd, buf, sizeof(buf))) > 0)
    {
        printf("from clent :%s\n", buf);
        if (write(cfd, buf, n) < 0)
        {
            perror("Fail to sedn response to client");
            Close(cfd);
            exit(1);
        }
    }

    if (n < 0)
    {
        perror("Fail to read from client");
    }
    printf("Client closed connection\n");
    Close(cfd);
    exit(0);
}
int main()
{
    struct sigaction act;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    act.sa_handler = free_process;
    if (sigaction(SIGCHLD, &act, NULL) < 0)
    {
        perror("fail to sigaction");
        exit(1);
    }
    //创建套接字
    int lfd = tcp4bind(SERVER_PORT, NULL);
    //监听
    Listen(lfd, BACKLOG);
    //提前
    struct sockaddr_in cliaddr;
    socklen_t len = sizeof(cliaddr);

    while (1)
    {
        char ip[16] = "";
        //提取连接
        int cfd = Accept(lfd, (struct sockaddr*)&cliaddr, &len);
        printf("new client ip = %s port = %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, 16), ntohs(cliaddr.sin_port));
        //fork创建子进程
        pid_t pid;
        pid = fork();

        if (pid < 0)
        {
            perror("fail to fork");
            Close(cfd);
            continue;
        }
        else if (pid == 0)
        {
            Close(lfd);
            handle_client(cfd);
            break;
        }

        Close(cfd);
    }
    //关闭
    Close(lfd);

    return 0;
}

3.3 多线程版

server.c

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include "wrap.h"
#include <arpa/inet.h>

typedef struct c_info {
    int cfd;
    struct sockaddr_in cliaddr;
}CINFO;


void* client_fun(void* arg)
{
    CINFO* info = (CINFO*)arg;
    char ip[16] = "";
    printf("new client ip =%s port =%d\n", inet_ntop(AF_INET, &(info->cliaddr.sin_addr.s_addr), ip, 16), ntohs(info->cliaddr.sin_port));

    while (1)
    {
        char buf[1024] = "";
        int count = 0;
        count = read(info->cfd, buf, sizeof(buf));
        if (count < 0)
        {
            perror("");
            break;
        }
        else if (count == 0)
        {
            printf("client close\n");
            break;
        }
        else
        {
            printf("%s\n", buf);
            write(info->cfd, buf, count);
        }
    }
    Close(info->cfd);
    free(info);
    pthread_exit(NULL);
}
int main(int argc, char* argv[])
{
    if (argc < 2)
    {
        perr_exit("argc < 2\n ./a.out 8000\n");
    }
    pthread_attr_t attr;
    int s = pthread_attr_init(&attr);
    if (s != 0)
    {
        perr_exit("pthread_attr_init error");
    }
    s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (s != 0)
    {
        perr_exit("pthread_attr_setdetachstate error");
    }
    short port = atoi(argv[1]);
    int lfd = tcp4bind(port, NULL);

    Listen(lfd, 128);
    struct sockaddr_in cliaddr;
    socklen_t len = sizeof(cliaddr);
    CINFO* info;

    while (1)
    {
        int cfd = Accept(lfd, (struct sockaddr*)&cliaddr, &len);
        char ip[16] = "";

        pthread_t pthid;
        info = (CINFO*)malloc(sizeof(CINFO));
        if (NULL == info)
        {
            perr_exit("malloc error");
        }
        info->cfd = cfd;
        info->cliaddr = cliaddr;
        pthread_create(&pthid, &attr, client_fun, info);
    }
    return 0;
}

4. UDP服务器

        相较于TCP而言,UDP通信的形式更像是发短信。不需要在数据传输之前建立、维护连接。只专心获取数据就好。省去了三次握手的过程,通信速度可以大大提高,但与之伴随的通信的稳定性和正确率便得不到保证。因此,我们称UDP为“无连接的不可靠报文传递”。

        由于无需创建连接,所以UDP开销较小,数据传输速度快,实时性较强。多用于对实时性要求较高的通信场合,如视频会议、电话会议等。但随之也伴随着数据传输不可靠,传输数据的正确率、传输顺序和流量都得不到控制和保证。所以,通常情况下,使用UDP协议进行数据传输,为保证数据的正确性,我们需要在应用层添加辅助校验协议来弥补UDP的不足,以达到数据可靠传输的目的。

        与TCP类似的,UDP也有可能出现缓冲区被填满后,再接收数据时丢包的现象。由于它没有TCP滑动窗口的机制,通常采用如下两种方法解决:

  1. 服务器应用层设计流量控制,控制发送数据速度。
  2. 借助setsockopt函数改变接收缓冲区大小。如:

C/S模型UDP:

【socket编程】TCP服务器、UDP服务器、本地套接字【C语言代码实现】-LMLPHP 

server.c

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

#define SERVER_PORT 8001
#define MAXLINE 1024

int main()
{
    int sockfd;
    char buf[MAXLINE];
    struct sockaddr_in seraddr, cliaddr;

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("fail to socket");
        exit(1);
    }

    memset(&seraddr, 0, sizeof(seraddr));
    memset(&cliaddr, 0, sizeof(cliaddr));

    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SERVER_PORT);
    seraddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(sockfd, (const struct sockaddr*)&seraddr, sizeof(seraddr)) < 0)
    {
        perror("fail to bind");
        exit(1);
    }
    socklen_t len = sizeof(cliaddr);
    int n;

    while (1)
    {
        memset(buf, 0, sizeof(buf));
        n = recvfrom(sockfd, buf, MAXLINE, MSG_WAITALL, (struct sockaddr*)&cliaddr, &len);
        if (n < 0)
        {
            perror("fail to recvfrom");
            break;
        }
        else
        {
            printf("From Client data:%s\n", buf);
            if (sendto(sockfd, buf, n, 0, (const struct sockaddr*)&cliaddr, len) == -1)
            {
                perror("fail to sendto");
                break;
            }
        }
    }

    close(sockfd);
    return 0;
}

 client.c

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

#define SERVER_PORT 8001
#define MAXLINE 1024    

int main()
{
    int sockfd;
    char buf[MAXLINE];
    struct sockaddr_in seraddr;

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("fail to socket");
        exit(1);
    }

    memset(&seraddr, 0, sizeof(seraddr));

    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);

    int n;
    socklen_t len = sizeof(seraddr);

    while (1)
    {
        n = read(STDIN_FILENO, buf, sizeof(buf));
        if(sendto(sockfd, buf, n, 0, (const struct sockaddr*)&seraddr, len) == -1)
        {
            perror("fail to sendto");
            break;
        }

        memset(buf, 0, sizeof(buf));
        n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&seraddr, &len);
        if (n < 0)
        {
            perror("fail to recvfrom");
            break;
        }
        else
        {
            printf("From Server data:%s\n", buf);
        }
    }
    close(sockfd);

    return 0;
}

5. 本地套接字

        socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。

        UNIX Domain Socket是全双工的,API接口语义丰富,相比其它IPC机制有明显的优越性,目前已成为使用最广泛的IPC机制,比如X Window服务器和GUI程序之间就是通过UNIXDomain Socket通讯的。

        使用UNIX Domain Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。

        UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。

        对比网络套接字地址结构和本地套接字地址结构:

以下程序将UNIX Domain socket绑定到一个地址。 

 service:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

char *socket_path = "/tmp/demo_socket";

int main(void) {
    struct sockaddr_un addr;
    char buf[100];
    int fd,cl,rc;

    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket error");
        exit(-1);
    }

    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);

    unlink(socket_path);

    if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        perror("bind error");
        exit(-1);
    }

    if (listen(fd, 5) == -1) {
        perror("listen error");
        exit(-1);
    }

    while (1) {
        if ((cl = accept(fd, NULL, NULL)) == -1) {
            perror("accept error");
            continue;
        }

        while ((rc=read(cl,buf,sizeof(buf))) > 0) {
            printf("read %u bytes: %.*s\n", rc, rc, buf);
            write(cl, buf, rc);
        }
        if (rc == -1) {
            perror("read");
            exit(-1);
        }
        else if (rc == 0) {
            printf("EOF\n");
            close(cl);
        }
    }

    return 0;
}

client:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

char *socket_path = "/tmp/demo_socket";

int main(void) {
    struct sockaddr_un addr;
    char buf[100];
    int fd,rc;

    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket error");
        exit(-1);
    }

    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);

    if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        perror("connect error");
        exit(-1);
    }

    while(1) {
        printf("Enter message to send: ");
        fgets(buf, sizeof(buf), stdin);
        if ((rc = write(fd, buf, strlen(buf))) > 0) {
            printf("Message sent\n");
            read(fd, buf, sizeof(buf));
            printf("Server replied : %s\n", buf);
        }
        else {
            printf("Error or connection closed\n");
            break;
        }
    }

    return 0;
}

总结:

        这些都是 C语言实现的代码,建议理解并自行敲出来。

07-14 13:40