基于TCP的多线程双向通信

基于TCP的多线程双向通信

今天我们来简单实现一下TCP的双向通信

1. 服务器端的搭建流程

1.先获取套接字(socket)
2.绑定套接字(bind),公布自己的网路信息(IP地址,端口号,家族协议)
3.开始监听(listen),设置决连数(设置连接队列的客户端个数)
4.等待客户端连接,获取到客户端的对等套接字,该套接字用于通信(accept)
5.连接成功后开始通信(write,read)
6.关闭套接字(close)

整体代码采用的是总分总格式
废话不多说,先上代码
这个是程序中所用到的结构体:

typedef struct S_Inf
{
    int s_fd;//服务器自己的套接字
    int c_fd;//客户端的对等套接字
    pthread_t send_id;//用于发送信息的线程id号
    pthread_t recv_id;//用于接收信息的线程id号
}SI,*P_SI;

mian函数:

int main(void)
{
    //初始化
    P_SI p_si = Ser_Init();
    if(p_si == (P_SI)-1)
    {
        perror("初始化失败\n");
        return -1;
    }
    
    //客户端与服务端通信
    if(Speaking(p_si) == -1)
    {
        printf("通信失败\n");
        return -1;
    }

    //释放内存
    if(Free(p_si) == -1)
    {
        printf("释放失败\n");
        return -1;
    }
    
    //主进程挂起
    //线程是依赖于该主进程的,进程生命周期已结束,线程也跟着结束,所以这里挂起,不让他死掉
    pause();

    return 0;
}

初始化函数:

P_SI Ser_Init()
{
    P_SI p_si = (P_SI)malloc(sizeof(SI));
    memset(p_si,0,sizeof(SI));

    //创建套接字
    p_si->s_fd = socket(AF_INET,SOCK_STREAM,0);
    if(p_si->s_fd == -1)
    {
        perror("socket");
        return (P_SI)-1;
    }
    printf("创建套接字成功\n");

    //定义服务器结构体,用于存放服务器IP,端口号等信息
    struct sockaddr_in s_inf;
    memset(&s_inf,0,sizeof(s_inf));
    s_inf.sin_family = AF_INET;   // 家族协议,用的是IPV4的,所以用AF_INET
    s_inf.sin_addr.s_addr = htonl(INADDR_ANY);
    s_inf.sin_port = htons(8888);//端口号,

    //绑定套接字
    int bind_ret = bind(p_si->s_fd,(struct sockaddr *)&s_inf,sizeof(s_inf));
    if(bind_ret == -1)
    {
        perror("bind");
        return (P_SI)-1;
    }
    printf("绑定成功\n");

    //开始监听
    int listen_ret = listen(p_si->s_fd,1);//这里的1表示决连数
    if(listen_ret == -1)
    {
        perror("listen");
        return (P_SI)-1;
    }
    printf("正在监听\n");
    printf("正在等待连接\n");

    struct sockaddr_in u_inf;
    memset(&u_inf,0,sizeof(u_inf));
    int len = sizeof(u_inf);

    p_si->c_fd = accept(p_si->s_fd,(struct sockaddr *)&u_inf,&len);
    if(p_si->c_fd == -1)
    {
        perror("accept");
        return (P_SI)-1;
    }

    printf("%s已连接\n",inet_ntoa(u_inf.sin_addr));


    return p_si;
}

通信:

int Speaking(P_SI p_si)
{
    //创建线程给客户端发送信息
    if(pthread_create(&p_si->send_id,NULL,send_msg,(void *)&p_si->c_fd)!=0)
    {
        perror("pthread_create");
        return -1;
    }
    //创建线程接收客户端信息
    if(pthread_create(&p_si->recv_id,NULL,recv_msg,(void *)&p_si->c_fd)!=0)
    {
        perror("pthread_create");
        return -1;
    }

    return 0;
}

发送信息:

void *send_msg(void *arg)
{
    int u_fd = *((int *)arg);

    char buf[1024];
    while(1)
    {
        memset(buf,0,sizeof(buf));
        printf("请输入要发送的信息:");
        scanf("%s",buf);
        if(write(u_fd,&buf,strlen(buf)) == -1)
        {
            perror("write");
            pthread_exit(NULL);
        }
    }
}

接收信息:

void *recv_msg(void *arg)
{
    int u_fd = *((int *)arg);
    char buf[1024];
    while(1)
    {
        memset(buf,0,sizeof(buf));
        int read_ret = read(u_fd,&buf,sizeof(buf));
        if(read_ret==-1)
        {
            perror("read");
            pthread_exit(NULL);
        }else if(read_ret == 0)//当读取字节数为0时,证明客户端掉线了
        {
            printf("客户端掉线\n");
            pthread_exit(NULL);
        }
        else{
        //打印客户端发来的信息
            printf("客户端说:%s\n",buf);
        }

    }
}

释放:

int Free(P_SI p_si)
{
    if(pthread_join(p_si->send_id,NULL)!=0)
    {
        perror("pthread_join");
        return -1;
    }

    if(pthread_join(p_si->recv_id,NULL)!=0)
    {
        perror("pthread_join");
        return -1;
    }
    
    close(p_si->s_fd);//关闭套接字

    free(p_si);

    return 0;
}

2. 客户端的搭建流程:

1.先获取套接字(socket)
2.连接服务器(connect)
3.开始通信(write,read)
4.关闭套接字(close)

以下是客户端的代码
这是用于存放客户端信息的结构体:

typedef struct C_Inf
{
    int c_id;
    pthread_t send_id;
    pthread_t recv_id;
}CI,*P_CI;

main函数:

int main(void)
{
    P_CI p_ci = Cli_Init();
    if(p_ci == (P_CI)-1)
    {
        printf("初始化失败\n");
        return -1;
    }

    if(Speaking(p_ci) == -1)
    {
        printf("通信失败\n");
        return -1;
    }

    if(Free(p_ci) == -1)
    {
        printf("内存释放失败\n");
        return -1;
    }

    pause();

    return 0;
}

初始化函数:

P_CI Cli_Init()
{
    P_CI p_ci = (P_CI)malloc(sizeof(CI));
    if(p_ci == NULL)
    {
        return (P_CI)-1;
    }

    //获取套接字
    p_ci->c_id = socket(AF_INET,SOCK_STREAM,0);
    if(p_ci->c_id == -1)
    {
        perror("socket");
        return (P_CI)-1;
    }

    //创建结构体用于存放服务器的信息
    struct sockaddr_in ser_inf;
    memset(&ser_inf,0,sizeof(ser_inf));

    ser_inf.sin_addr.s_addr = inet_addr("192.168.244.134");//这个IP是自己你所要连接的服务器的IP,要自己修改
    ser_inf.sin_family = AF_INET;//IPv4用的是AF_INET这个家族协议
    ser_inf.sin_port = htons(8888);//端口号要和服务器的端口号一致

    //开始连接服务器
    int connect_ret = connect(p_ci->c_id,(struct sockaddr *)&ser_inf,sizeof(ser_inf));
    if(connect_ret == -1)
    {
        perror("connect");
        return (P_CI)-1;
    }

    return p_ci;
}

通信:

int Speaking(P_CI p_ci)
{
    //创建两条线程用于收发信息
    if(pthread_create(&p_ci->send_id,NULL,send_msg,&p_ci->c_id) != 0)
    {
        perror("pthread_create");
        return -1;
    }
    if(pthread_create(&p_ci->recv_id,NULL,recv_msg,&p_ci->c_id) != 0)
    {
        perror("pthread_create");
        return -1;
    }
    return 0;
}

发送信息:

void *send_msg(void *arg)
{
    int c_id = *((int *)arg);

    char buf[1024];
    while(1)
    {
        memset(buf,0,sizeof(buf));
        printf("请输入要发送的信息:\n");
        scanf("%s",buf);
        if(write(c_id,&buf,strlen(buf)) == -1)
        {
            perror("write");
            pthread_exit(NULL);
        }
    }
}

接收信息:

void *recv_msg(void *arg)
{
    int c_id = *((int *)arg);
    char buf[1024];
    while(1)
    {
        memset(buf,0,sizeof(buf));
        int read_ret = read(c_id,&buf,sizeof(buf));
        if(read_ret==-1)
        {
            perror("read");
            pthread_exit(NULL);
        }else if(read_ret == 0)
        {
            printf("服务器掉线\n");
            pthread_exit(NULL);
        }
        else{
            printf("服务器说:%s\n",buf);
        }
    }
}

释放:

int Free(P_CI p_ci)
{
    //回收线程
    pthread_join(p_ci->send_id,NULL);
    pthread_join(p_ci->recv_id,NULL);

    close(p_ci->c_id);//关闭套接字

    //释放内存
    free(p_ci);

    return 0;
}

服务器和客户端之间通信的代码都差不多,就是初始化的时候有点区别,客户端的步骤少一点,他只需要向服务器发送连接请求,连接成功后便能通信。

运行结果

代码运行结果

百度网盘

代码链接:https://pan.baidu.com/s/1FvHVdkNbMf2ef1l1arao-A
提取码:abcd

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
多线程TCP双向通信Python是指使用Python编写程序,通过Socket套接字实现多个客户端与服务端之间的双向通信,并且使用多线程技术实现同时连接多个客户端的程序。具体实现过程如下: 1. 服务端创建一个Socket对象,绑定IP地址和端口号,并监听客户端的连接请求。 2. 客户端创建一个Socket对象,连接服务端的IP地址和端口号。 3. 服务端接收到客户端的连接请求后,创建一个新的线程处理该客户端的请求。 4. 客户端向服务端发送数据,服务端接收到数据后进行处理,并向客户端发送响应数据。 5. 客户端接收到服务端的响应数据后进行处理。 下面是一个简单的多线程TCP双向通信Python的示例代码: ```python # 服务端代码 import socket import threading def handle_client(client_socket, client_address): while True: data = client_socket.recv(1024) if not data: break print("Received data from {}: {}".format(client_address, data.decode())) client_socket.send("Hello, client!".encode()) client_socket.close() def main(): server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('127.0.0.1', 8888)) server_socket.listen(5) print("Server is listening on {}:{}".format('127.0.0.1', 8888)) while True: client_socket, client_address = server_socket.accept() print("Accepted connection from {}:{}".format(client_address[0], client_address[1])) client_thread = threading.Thread(target=handle_client, args=(client_socket, client_address)) client_thread.start() if __name__ == '__main__': main() # 客户端代码 import socket def main(): client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect(('127.0.0.1', 8888)) while True: data = input("Please input data to send to server: ") client_socket.send(data.encode()) response = client_socket.recv(1024) print("Received response from server: {}".format(response.decode())) if __name__ == '__main__': main() ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值