【Linux Socket编程】——使用多线程,实现简单的聊天室(TCP协议),手把手教你如何使用

操作系统:Linux

编程语言:c语言

应用技术:socket编程(TCP协议)、多线程

实现功能:简单的聊天室(服务器端与客户端可以相互收发消息)

简要概述

如何建立连接,即socket编程的创建流程,请查看socket编程流程

(完整代码在最后)

服务器端与客户端收发数据相同

建立好连接后,创建两个线程

  • send_thread:用于发送数据,执行发送函数
  • recv_thread:用于接收数据,执行接收函数

然后创建两个函数,分别用于以上两个线程的执行

  • send_func:发送函数,实现手动从键盘输入数据,然后发送
  • recv_func:接收函数,实现不断检测并接收对方发来的消息
  • (每次发送或接收,都会在屏幕上打印出来,见代码)

测试

因为是在同一台电脑上进行测试,所以需要开启两个终端,一个终端运行服务器端,另一个运行客户端

一、编译

由于 pthread 库不是标准的 Linux 库,所以在编译时需要加上 -lpthread 

服务器端

gcc service.c -o service -lpthread

客户端

gcc client.c -o client -lpthread

二、执行

先执行服务器端

再执行 客户端

此时服务器端也会显示连接成功

三、发送、接收数据

1、服务器端发送数据 Hello my name is service(回车发送)

此时服务器端会显示

客户端会显示

2、客户端发送数据 Hi my name is client

此时客户端显示

服务器端显示

四、退出聊天室

当双方都输入quit时,会结束聊天

服务器端发送 quit

客户端接收到服务器端发送的quit数据,并也返回发送 quit

此时客户端己经退出,服务器端收到客户端回的消息quit,也已经退出

一次愉快的聊天就这样结束咯!

 

完整代码

服务器端 (service.c)

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

#define BUFFER_SIZE 1024

void *send_func();
void *recv_func();

/*服务器端*/
int main(){
        pthread_t send_thread, recv_thread;
        //创建套接字
        int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        //将套接字和IP、端口绑定
        struct sockaddr_in serv_addr;
        memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
        serv_addr.sin_family = AF_INET;  //使用IPv4地址
        serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
        serv_addr.sin_port = htons(1258);  //端口
        bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
        //进入监听状态,等待用户发起请求
        listen(serv_sock, 20);
        printf("等待连接......\n");
        //接收客户端请求
        struct sockaddr_in clnt_addr;
        socklen_t clnt_addr_size = sizeof(clnt_addr);
        int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
        if (clnt_sock == -1)
        {
                printf("连接失败\n");
                exit(1);
        } else {
                printf("连接成功\n");
                printf("----------网络聊天室----------\n");
        }
        //向客户端发送和接收数据,使用线程方式
        int send_result, recv_result;
        send_result = pthread_create(&send_thread, NULL, send_func, (void*)(long)clnt_sock);
        recv_result = pthread_create(&recv_thread, NULL, recv_func, (void*)(long)clnt_sock);
        if (send_result != 0)
        {
                printf("send_thread create failure\n");
                exit(1);
        } else {
                //printf("send_thread create success\n");
        }
        if (recv_result != 0)
        {
                printf("recv_thread create failure\n");
                exit(1);
        } else {
                //printf("recv_thread create success\n");
        }

        send_result = pthread_join(send_thread, NULL);
        if (send_result == 0)
        {
                //printf("send_thread quit\n");
        }
        recv_result = pthread_join(recv_thread, NULL);
        if (recv_result == 0)
        {
                //printf("recv_thread quit\n");
        }
        printf("聊天结束!\n");
        //关闭套接字
        close(clnt_sock);
        close(serv_sock);
        return 0;
}
/*发送数据*/
void *send_func(void *arg)
{
        int sockfd = (int)(long)arg;
        char send_buf[BUFFER_SIZE];
        while(1)
        {
                // 从键盘输入数据
                if(fgets(send_buf, BUFFER_SIZE, stdin) == NULL)
                {
                        puts("no message\n");
                        continue;
                } else {
                        // 发送数据
                        send(sockfd, send_buf, sizeof(send_buf), 0);
                        printf("-----> I : %s\n", send_buf);
                }
                // 当输入quit时退出
                if (strncmp(send_buf, "quit", 4) == 0)
                {
                        break;
                }
        }
        pthread_exit(0);
}
/*接收数据*/
void *recv_func(void *arg)
{
        int sockfd = (int)(long)arg;
        char recv_buf[BUFFER_SIZE];
        int recv_result;
        while(1)
        {
                // 接收数据
                recv_result = recv(sockfd, recv_buf, sizeof(recv_buf), 0);
                if (recv_result > 0)
                {
                        printf("-----> You : %s\n", recv_buf);
                }
                // 当接收到quit时退出
                if (strncmp(recv_buf, "quit", 4) == 0)
                {
                        break;
                }
        }
        pthread_exit(0);
}

 客户端(client.c)

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

#define BUFFER_SIZE 1024

void *send_func();
void *recv_func();

/*客户端*/
int main(){
        int send_result, recv_result;
        int temp_result;
        pthread_t send_thread, recv_thread;
        //创建套接字
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        //向服务器(特定的IP和端口)发起请求
        struct sockaddr_in serv_addr;
        memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
        serv_addr.sin_family = AF_INET;  //使用IPv4地址
        serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
        serv_addr.sin_port = htons(1258);  //端口
        temp_result = connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
        if (temp_result == -1)
        {
                printf("连接失败\n");
                exit(1);
        } else {
                printf("连接成功\n");
                printf("----------网络聊天室----------\n");
        }

        //发送和接收数据,使用多线程
        send_result = pthread_create(&send_thread, NULL, send_func, (void*)(long)sock);
        recv_result = pthread_create(&recv_thread, NULL, recv_func, (void*)(long)sock);
        if (send_result != 0)
        {
                printf("send_thread create failure\n");
                exit(1);
        } else {
                //printf("send_thread create success\n");
        }
        if (recv_result != 0)
        {
                printf("recv_thread create failure\n");
                exit(1);
        } else {
                //printf("recv_thread create success\n");
        }
        send_result = pthread_join(send_thread, NULL);
        if (send_result == 0)
        {
                //printf("send_thread quit\n");
        }
        recv_result = pthread_join(recv_thread, NULL);
        if (recv_result == 0)
        {
                //printf("recv_thread quit\n");
        }
        printf("聊天结束!\n");

        close(sock);
        return 0;
}
/*发送数据*/
void *send_func(void *arg)
{
        int sockfd = (int)(long)arg;
        char send_buf[BUFFER_SIZE];

        while(1)
        {
                // 从键盘输入数据
                if (fgets(send_buf, BUFFER_SIZE, stdin) == NULL)
                {
                        printf("no message\n");
                        continue;
                } else {
                        // 发送数据
                        if(send(sockfd, send_buf, sizeof(send_buf), 0) == -1)
                        {
                                printf("send error\n");
                                continue;
                        } else {
                                printf("-----> I : %s\n", send_buf);
                        }
                }
                // 当输入quit时退出
                if (strncmp(send_buf, "quit", 4) == 0)
                {
                        break;
                }
        }
        pthread_exit(0);
}
/*接收数据*/
void *recv_func(void *arg)
{
        int sockfd = (int)(long)arg;
        char recv_buf[BUFFER_SIZE];
        while (1)
        {
                // 接收数据
                if (recv(sockfd, recv_buf, sizeof(recv_buf), 0) > 0)
                {
                        printf("-----> You : %s\n", recv_buf);
                }
                // 当接收到quit时退出
                if (strncmp(recv_buf, "quit", 4) == 0)
                {
                        break;
                }
        }
}

总结

以上代码使用多线程编程,实现了一个简单的聊天室功能

有兴趣可以在此基础上增加更多功能,比如界面、登录功能等

  • 14
    点赞
  • 100
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值