【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;
                }
        }
}

總結

以上代碼使用多線程編程,實現了一個簡單的聊天室功能

有興趣可以在此基礎上增加更多功能,比如界面、登錄功能等

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章