Linux下C语言实现socket两端简易即时通讯

简易的c/s端对端通信工具

简易思路:

将接收信息(recv或read)放在死循环中,没收到信息则阻塞,而只要对端有信息发送则会结束阻塞,将信息输出后自动再阻塞;因此,得再加一个线程,来获取键盘的输入流后进行信息发送。
代码中已有相应注释,修改客户端要连接的服务端IP地址,运行服务端后再运行客户端即可连接。

tcp/udp服务端代码

pthread_create(&stPid, NULL, tcp_connect_recv, NULL);中的tcp_connect_recv改成udp_connect_recv,即为udp模式。udp模式下没办法像tcp一样事先通过accept来获取客户端IP地址和端口,必须接收信息时才获取,所以在收到信息拿到stRemoteAddr后再开启输入信息的线程工作,并且参数是指针传参。

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

#include <pthread.h>

#define PORT 23 // 端口号
#define BACKLOG 5 // 最大监听数
char send_buf[4096] = {0};

typedef struct st_tcp_args_t {
    int new_fd;
} tcp_args_t;

void * tcp_connect_send(void *args)
{
    tcp_args_t *tcp_arg = (tcp_args_t *)args;
    while (1) {
        scanf("%s", send_buf);
        // 发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)
        // write和send两种方式都可以
        write(tcp_arg->new_fd, send_buf, sizeof(send_buf));
        //send(tcp_arg->new_fd, send_buf, sizeof(send_buf), 0);
    }
    return NULL;
}

void * tcp_connect_recv(void *args)
{
    int iSocketFD = 0; // socket句柄
    // SOCK_STREAM流式套接字,表示tcp
    iSocketFD = socket(AF_INET, SOCK_STREAM, 0); // 建立socket
    if (iSocketFD < 0) {
        printf("创建socket失败:%d\n", errno);
        return 0;
    }

    struct sockaddr_in stLocalAddr = {0}; // 本地地址信息结构图,下面有具体的属性赋值
    stLocalAddr.sin_family = AF_INET;  // 该属性表示接收本机或其他机器传输
    stLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY); // IP,INADDR_ANY表示本机IP
    stLocalAddr.sin_port = htons(PORT); // 端口号

    int flag = 1;
    if (setsockopt(iSocketFD, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0) {
        printf("设置socket选项失败:%d\n", errno);
        close(iSocketFD);
    }

    // 绑定地址结构体和socket
    if (bind(iSocketFD, (struct sockaddr *)&stLocalAddr, sizeof(stLocalAddr)) < 0) {
        printf("绑定socket失败:%d\n", errno);
        close(iSocketFD);
        return 0;
    }

    // 开启监听,第二个参数是最大监听数
    if (listen(iSocketFD, BACKLOG) < 0) {
        printf("监听socket失败:%d\n", errno);
        shutdown(iSocketFD, SHUT_RDWR);
        close(iSocketFD);
        return 0;
    }

    printf("开启监听, 监听的tcp socket句柄为:%d\n", iSocketFD);

    int new_fd = 0; // 建立连接后的句柄
    struct sockaddr_in stRemoteAddr = {0}; // 保存客户端地址信息
    socklen_t socklen = sizeof(stRemoteAddr); // 千万不能为0,否则获取不到stRemoteAddr
    // 开启accept后会阻塞直到接收到消息,参数分别是socket句柄,接收到的地址信息以及大小
    new_fd = accept(iSocketFD, (struct sockaddr *)&stRemoteAddr, &socklen);
    if (new_fd < 0) {
        printf("接收信息失败:%d\n", errno);
        shutdown(iSocketFD, SHUT_RDWR);
        close(iSocketFD);
        return 0;
    } else {
        printf("接收成功!连接句柄为:%d\n", new_fd);
        printf("确认客户端ip addr is %s\n", inet_ntoa(stRemoteAddr.sin_addr));
        printf("确认客户端port:%d \n", ntohs(stRemoteAddr.sin_port));
        strncpy(send_buf, "服务器确认连接成功!", sizeof("服务器确认连接成功!"));
        send(new_fd, send_buf, sizeof(send_buf), 0);
    }

    pthread_t child_tid = 0;
    tcp_args_t tcp_arg;
    tcp_arg.new_fd = new_fd;
    pthread_create(&child_tid, NULL, tcp_connect_send, (void *)&tcp_arg);
    pthread_detach(child_tid);

    int iRecvLen = 0; // 接收成功后的返回值
    char recv_buf[4096] = {0};
    while (1) {
        // recv和read两种方式都可以
        //iRecvLen = recv(new_fd, recv_buf, sizeof(recv_buf), 0);
        iRecvLen = read(new_fd, recv_buf, sizeof(recv_buf));
        // 对端关闭连接 返回0
        if (iRecvLen <= 0) {
            printf("接收失败或者对端关闭连接:%d\n", errno);
            shutdown(iSocketFD, SHUT_RDWR);
            close(new_fd);
            close(iSocketFD);
            return 0;
        } else {
            printf("收到客户端信息:%s\n", recv_buf);
            printf("收到客户端ip addr is %s\n", inet_ntoa(stRemoteAddr.sin_addr));
            printf("收到客户端port:%d \n", ntohs(stRemoteAddr.sin_port));
        }
    }
    shutdown(iSocketFD, SHUT_RDWR);
    close(new_fd);
    close(iSocketFD);
    return NULL;
}



typedef struct st_udp_args_t {
    int iSocketFD;
    // udp过程客户端可能会更新端口号等,需要使用指针
    struct sockaddr_in *stRemoteAddr;
} udp_args_t;

void * udp_connect_send(void *args)
{
    printf("收到客户端地址信息,服务端可以开始回信了!\n");
    udp_args_t *udp_arg = (udp_args_t *)args;
    while (1) {
        scanf("%s", send_buf);
        printf("udp_arg->iSocketFD %d\n", udp_arg->iSocketFD);
        sendto(udp_arg->iSocketFD, send_buf, sizeof(send_buf), 0, (struct sockaddr *)(udp_arg->stRemoteAddr), sizeof(*(udp_arg->stRemoteAddr)));
        printf("发送客户端ip addr is %s\n", inet_ntoa(udp_arg->stRemoteAddr->sin_addr));
        printf("发送客户端port:%d \n", ntohs(udp_arg->stRemoteAddr->sin_port));
    }
    return NULL;
}

void * udp_connect_recv(void *args)
{
    int iSocketFD = 0; // socket句柄
    // SOCK_DGRAM数据报式套接字,表示udp
    iSocketFD = socket(AF_INET, SOCK_DGRAM, 0); // 建立socket
    if (iSocketFD < 0) {
        printf("创建socket失败:%d\n", errno);
        return 0;
    }

    struct sockaddr_in stLocalAddr = {0}; // 本地地址信息结构图,下面有具体的属性赋值
    stLocalAddr.sin_family = AF_INET;  // 该属性表示接收本机或其他机器传输
    stLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY); // IP,INADDR_ANY表示本机IP
    stLocalAddr.sin_port = htons(PORT); // 端口号

    // 绑定地址结构体和socket
    if (bind(iSocketFD, (struct sockaddr *)&stLocalAddr, sizeof(stLocalAddr)) < 0) {
        printf("绑定socket失败:%d\n", errno);
        close(iSocketFD);
        return 0;
    }

    printf("绑定udp socket成功,socket句柄为:%d,开始接收消息。。。\n", iSocketFD);

    struct sockaddr_in stRemoteAddr = {0}; // 保存客户端地址信息

    int iRecvLen = 0; // 接收成功后的返回值
    char recv_buf[4096] = {0};
    socklen_t socklen = sizeof(stRemoteAddr);
    int is_first = 1;
    while (1) {
        iRecvLen = recvfrom(iSocketFD, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&stRemoteAddr, &socklen);
        if (iRecvLen <= 0) {
            printf("接收失败或者对端关闭连接:%d\n", errno);
            shutdown(iSocketFD, SHUT_RDWR);
            close(iSocketFD);
            return 0;
        } else {
            printf("收到客户端信息:%s\n", recv_buf);
            printf("收到客户端ip addr is %s\n", inet_ntoa(stRemoteAddr.sin_addr));
            printf("收到客户端port:%d \n", ntohs(stRemoteAddr.sin_port));
        }
        // 首次收到客户端udp报文后才能拿到客户端ip地址和端口,才能有回信条件
        if (is_first == 1) {
            pthread_t child_tid = 0;
            udp_args_t udp_arg;
            udp_arg.iSocketFD = iSocketFD;
            udp_arg.stRemoteAddr = &stRemoteAddr;
            pthread_create(&child_tid, NULL, udp_connect_send, (void *)&udp_arg);
            pthread_detach(child_tid);
            is_first = 0;
        }
    }
    shutdown(iSocketFD, SHUT_RDWR);
    close(iSocketFD);
    return NULL;
}

int main(void)
{
    pthread_t stPid = 0;
    pthread_create(&stPid, NULL, tcp_connect_recv, NULL);
    pthread_join(stPid, NULL);
    return 0;
}

tcp/udp客户端代码

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

#include <pthread.h>

#define ADDR "172.200.48.54" // 目标地址IP
#define PORT 23 //目标地址端口号

char send_buf[4096] = {0};

typedef struct st_tcp_args_t {
    int iSocketFD;
} tcp_args_t;

void * tcp_connect_send(void *args)
{
    tcp_args_t *tcp_arg = (tcp_args_t *)args;
    while (1) {
        scanf("%s", send_buf);
        // 发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)
        // write和send两种方式都可以
        write(tcp_arg->iSocketFD, send_buf, sizeof(send_buf));
        //send(tcp_arg->iSocketFD, send_buf, sizeof(send_buf), 0);
    }
    return NULL;
}

void * tcp_connect_recv(void *args)
{
    int iSocketFD = 0; // socket句柄
    iSocketFD = socket(AF_INET, SOCK_STREAM, 0); // 建立socket
    if (iSocketFD < 0) {
        printf("创建socket失败:%d\n", errno);
        return 0;
    }

    unsigned int iRemoteAddr = 0;
    struct sockaddr_in stRemoteAddr = {0}; // 服务端地址信息
    stRemoteAddr.sin_family = AF_INET;
    inet_pton(AF_INET, ADDR, &iRemoteAddr);
    stRemoteAddr.sin_addr.s_addr = iRemoteAddr;
    stRemoteAddr.sin_port = htons(PORT);

    // 连接方法:传入句柄,目标地址,和大小
    if (connect(iSocketFD, (struct sockaddr *)&stRemoteAddr, sizeof(stRemoteAddr)) < 0) {
        printf("连接socket失败:%d\n", errno);
        close(iSocketFD);
        return 0;
    }

    printf("连接服务端socket成功!\n");

    pthread_t child_tid = 0;
    tcp_args_t tcp_arg;
    tcp_arg.iSocketFD = iSocketFD;
    pthread_create(&child_tid, NULL, tcp_connect_send, (void *)&tcp_arg);
    pthread_detach(child_tid);

    int iRecvLen = 0; // 接收成功后的返回值
    char recv_buf[4096] = {0};
    while (1) {
        // recv和read两种方式都可以
        //iRecvLen = recv(iSocketFD, recv_buf, sizeof(recv_buf), 0);
        iRecvLen = read(iSocketFD, recv_buf, sizeof(recv_buf));
        // 对端关闭连接 返回0
        if (iRecvLen <= 0) {
            printf("接收失败或者对端关闭连接:%d\n", errno);
            close(iSocketFD);
            return 0;
        } else {
            printf("收到服务端信息:%s\n", recv_buf);
        }
    }
    close(iSocketFD);
    return NULL;
}



typedef struct st_udp_args_t {
    int iSocketFD;
    struct sockaddr_in stRemoteAddr;
} udp_args_t;

void * udp_connect_send(void *args)
{
    udp_args_t *udp_arg = (udp_args_t *)args;
    while (1) {
        scanf("%s", send_buf);
        sendto(udp_arg->iSocketFD, send_buf, sizeof(send_buf), 0, (struct sockaddr *)&(udp_arg->stRemoteAddr), sizeof(udp_arg->stRemoteAddr));
        printf("服务端ip addr is %s\n", inet_ntoa(udp_arg->stRemoteAddr.sin_addr));
        printf("服务端port:%d \n", ntohs(udp_arg->stRemoteAddr.sin_port));
    }
    return NULL;
}

void * udp_connect_recv(void *args)
{
    int iSocketFD = 0; // socket句柄
    // SOCK_DGRAM数据报式套接字,表示udp
    iSocketFD = socket(AF_INET, SOCK_DGRAM, 0); // 建立socket
    if (iSocketFD < 0) {
        printf("创建socket失败:%d\n", errno);
        return 0;
    }

    unsigned int iRemoteAddr = 0;
    struct sockaddr_in stRemoteAddr = {0}; // 服务端地址信息
    stRemoteAddr.sin_family = AF_INET;
    inet_pton(AF_INET, ADDR, &iRemoteAddr);
    stRemoteAddr.sin_addr.s_addr = iRemoteAddr;
    stRemoteAddr.sin_port = htons(PORT);

    pthread_t child_tid = 0;
    udp_args_t udp_arg;
    udp_arg.iSocketFD = iSocketFD;
    udp_arg.stRemoteAddr = stRemoteAddr;
    pthread_create(&child_tid, NULL, udp_connect_send, (void *)&udp_arg);
    pthread_detach(child_tid);
    int iRecvLen = 0; // 接收成功后的返回值
    char recv_buf[4096] = {0};
    socklen_t socklen = 0;
    while (1) {
        iRecvLen = recvfrom(iSocketFD, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&stRemoteAddr, &socklen);
        if (iRecvLen <= 0) {
            printf("接收失败或者对端关闭连接:%d\n", errno);
            shutdown(iSocketFD, SHUT_RDWR);
            close(iSocketFD);
            return 0;
        } else {
            printf("收到服务端信息:%s\n", recv_buf);
        }
    }
    close(iSocketFD);
    return NULL;
}

int main(void)
{
    pthread_t stPid = 0;
    pthread_create(&stPid, NULL, tcp_connect_recv, NULL);
    pthread_join(stPid, NULL);
    return 0;
}

UDS实现单一主机通信

UDS(Unix domain socket)又叫 IPC socket,用于实现同一主机上的进程间通信。

uds服务端代码

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

#include <pthread.h>

#define BACKLOG 5 // 最大监听数
char *socket_path = "server.socket";

char send_buf[4096] = {0};

typedef struct st_uds_args_t {
    int new_fd;
} uds_args_t;

void * uds_connect_send(void *args)
{
    uds_args_t *uds_arg = (uds_args_t *)args;
    while (1) {
        scanf("%s", send_buf);
        // 发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)
        send(uds_arg->new_fd, send_buf, sizeof(send_buf), 0);
    }
    return NULL;
}

void * uds_connect_recv(void *args)
{
    int iSocketFD = 0; // socket句柄
    // SOCK_STREAM流式套接字,表示tcp
    iSocketFD = socket(AF_UNIX, SOCK_STREAM, 0); // 建立socket
    if (iSocketFD < 0) {
        printf("创建socket失败:%d\n", errno);
        return 0;
    }

    // 网络编程的 socket 地址是 IP 地址加端口号,而 UNIX domain socket 的地址是一个 socket 类型的文件在文件系统中的路径
    struct sockaddr_un stLocalAddr = {0};
    // 使用 AF_UNIX 会在系统上创建一个 socket 文件,不同进程通过读写这个文件来实现通信。
    stLocalAddr.sun_family = AF_UNIX;
    // 指定uds路径
    strncpy(stLocalAddr.sun_path, socket_path, strlen(socket_path) + 1);

    // bind会创建uds文件,如果已存在会bind失败,所以得先删除
    unlink(socket_path);
    // 绑定地址结构体和socket
    if (bind(iSocketFD, (struct sockaddr *)&stLocalAddr, sizeof(stLocalAddr)) < 0) {
        printf("绑定socket失败:%d\n", errno);
        close(iSocketFD);
        return 0;
    }

    // 开启监听,第二个参数是最大监听数
    if (listen(iSocketFD, BACKLOG) < 0) {
        printf("监听socket失败:%d\n", errno);
        shutdown(iSocketFD, SHUT_RDWR);
        close(iSocketFD);
        return 0;
    }

    printf("开启监听, 监听的uds socket句柄为:%d\n", iSocketFD);

    int new_fd = 0; // 建立连接后的句柄
    struct sockaddr_un stRemoteAddr = {0}; // 保存客户端地址信息
    socklen_t socklen = sizeof(stRemoteAddr);
    // 开启accept后会阻塞直到接收到消息,参数分别是socket句柄,接收到的地址信息以及大小
    new_fd = accept(iSocketFD, (struct sockaddr *)&stRemoteAddr, &socklen);
    if (new_fd < 0) {
        printf("接收信息失败:%d\n", errno);
        shutdown(iSocketFD, SHUT_RDWR);
        close(iSocketFD);
        return 0;
    } else {
        printf("接收成功!连接句柄为:%d\n", new_fd);
        strncpy(send_buf, "服务器确认连接成功!", sizeof("服务器确认连接成功!"));
        send(new_fd, send_buf, sizeof(send_buf), 0);
    }

    pthread_t child_tid = 0;
    uds_args_t uds_arg;
    uds_arg.new_fd = new_fd;
    pthread_create(&child_tid, NULL, uds_connect_send, (void *)&uds_arg);
    pthread_detach(child_tid);

    int iRecvLen = 0; // 接收成功后的返回值
    char recv_buf[4096] = {0};
    while (1) {
        iRecvLen = recv(new_fd, recv_buf, sizeof(recv_buf), 0);
        // 对端关闭连接 返回0
        if (iRecvLen <= 0) {
            printf("接收失败或者对端关闭连接:%d\n", errno);
            shutdown(iSocketFD, SHUT_RDWR);
            close(new_fd);
            close(iSocketFD);
            return 0;
        } else {
            printf("收到客户端信息:%s\n", recv_buf);
        }
    }
    shutdown(iSocketFD, SHUT_RDWR);
    close(new_fd);
    close(iSocketFD);
    return NULL;
}

int main(void)
{
    pthread_t stPid = 0;
    pthread_create(&stPid, NULL, uds_connect_recv, NULL);
    pthread_join(stPid, NULL);
    return 0;
}

uds客户端代码

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

#include <pthread.h>

char *server_path = "server.socket";

char send_buf[4096] = {0};

typedef struct st_uds_args_t {
    int iSocketFD;
} uds_args_t;

void * uds_connect_send(void *args)
{
    uds_args_t *uds_arg = (uds_args_t *)args;
    while (1) {
        scanf("%s", send_buf);
        // 发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)
        // write和send两种方式都可以
        write(uds_arg->iSocketFD, send_buf, sizeof(send_buf));
        //send(uds_arg->iSocketFD, send_buf, sizeof(send_buf), 0);
    }
    return NULL;
}

void * uds_connect_recv(void *args)
{
    int iSocketFD = 0; // socket句柄
    iSocketFD = socket(AF_UNIX, SOCK_STREAM, 0); // 建立socket
    if (iSocketFD < 0) {
        printf("创建socket失败:%d\n", errno);
        return 0;
    }

    unsigned int iRemoteAddr = 0;
    struct sockaddr_un stRemoteAddr = {0}; // 服务端地址信息
    stRemoteAddr.sun_family = AF_UNIX;
    strncpy(stRemoteAddr.sun_path, server_path, strlen(server_path) + 1);

    // 连接方法:传入句柄,目标地址,和大小
    if (connect(iSocketFD, (struct sockaddr *)&stRemoteAddr, sizeof(stRemoteAddr)) < 0) {
        printf("连接socket失败:%d\n", errno);
        close(iSocketFD);
        return 0;
    }

    printf("连接服务端socket成功!\n");

    pthread_t child_tid = 0;
    uds_args_t uds_arg;
    uds_arg.iSocketFD = iSocketFD;
    pthread_create(&child_tid, NULL, uds_connect_send, (void *)&uds_arg);
    pthread_detach(child_tid);

    int iRecvLen = 0; // 接收成功后的返回值
    char recv_buf[4096] = {0};
    while (1) {
        // recv和read两种方式都可以
        //iRecvLen = recv(iSocketFD, recv_buf, sizeof(recv_buf), 0);
        iRecvLen = read(iSocketFD, recv_buf, sizeof(recv_buf));
        // 对端关闭连接 返回0
        if (iRecvLen <= 0) {
            printf("接收失败或者对端关闭连接:%d\n", errno);
            close(iSocketFD);
            return 0;
        } else {
            printf("收到服务端信息:%s\n", recv_buf);
        }
    }
    close(iSocketFD);
    return NULL;
}

int main(void)
{
    pthread_t stPid = 0;
    pthread_create(&stPid, NULL, uds_connect_recv, NULL);
    pthread_join(stPid, NULL);
    return 0;
}
  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值