C语言Socket编程TCP简单聊天室

这篇具有很好参考价值的文章主要介绍了C语言Socket编程TCP简单聊天室。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C语言Socket编程TCP简单聊天室

简介

这是一个使用C语言进行套接字编程实现的简单聊天室, 使用Pthread库进行多线程执行

代码

服务端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>  //Unix/Linux系统的基本系统数据类型的头文件,含有size_t,time_t,pid_t等类型
#include <sys/socket.h> //套接字基本函数相关
#include <netinet/in.h> //IP地址和端口相关定义,比如struct sockaddr_in等
#include <arpa/inet.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#include <pthread.h>
#define MAX_MSG_SIZE 128
#define MAX_CLIENT_CONNECTION 128
#define MAX_FORWARD_MSG_SIZE (2 * MAX_MSG_SIZE + 32)
#define LOGIN 0
#define LOGOUT 1
int sigint_flag = 0;

typedef struct _client
{
    int clientfd;
    char nickname[MAX_MSG_SIZE];
    struct sockaddr_in clientsock;
    struct _client *next;
} Client;

pthread_mutex_t client_list_lock;

// 创建空白的节点
Client *createNode();
// 按照文件描述符删除节点
int deleteNode(int targetfd);
// 将节点插入链表(头部)
void insertNode(Client *newNode);
// 显示链表中所有的节点
void displayAllClients();
// 全局广播
void broadcast(const char *content);
// 获取当前时间字符串
char *getCurrentTime();
// 去除末尾的换行符
void trim_linefeed(char *str);
// 获取登录消息
char *getInfo(const char *nickname, int mode);

Client *head;

void *clientHandler(void *arg);

void sigpipe_handler(int signum, siginfo_t *info, void *context)
{
    printf("SIGPIPE detected!\n");
}

void sigint_handler(int signum, siginfo_t *info, void *context)
{
    printf("[svr] Server is shutting down...\n");
    sigint_flag = 1;
}

int main(int argc, char const *argv[])
{

    struct sigaction sig_pipe, sig_int;
    sig_pipe.sa_flags = SA_SIGINFO;
    sig_pipe.sa_sigaction = sigpipe_handler;
    sigfillset(&sig_pipe.sa_mask);

    sig_int.sa_flags = SA_SIGINFO;
    sig_int.sa_sigaction = sigint_handler;
    sigfillset(&sig_int.sa_mask);

    // 为 SIGPIPE 和 SIGINT 设置信号处理函数
    if (sigaction(SIGPIPE, &sig_pipe, NULL) < 0)
    {
        perror("sigaction");
        return 1;
    }
    if (sigaction(SIGINT, &sig_int, NULL) < 0)
    {
        perror("sigaction");
        return 1;
    }

    // 处理命令行参数
    if (argc != 3)
    {
        printf("Usage: %s <ip_address> <port>\n", argv[0]);
        exit(1);
    }

    int port = atoi(argv[2]);
    int listenfd, connectfd;
    struct sockaddr_in server, client;
    int sin_size;

    if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("Create socket failed.");
        exit(-1);
    }

    int opt = SO_REUSEADDR;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[2]));
    inet_pton(AF_INET, argv[1], &server.sin_addr);

    if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1)
    {
        perror("bind");
        exit(1);
    }

    if (listen(listenfd, MAX_CLIENT_CONNECTION) == -1)
    {
        perror("listen");
        exit(1);
    }

    // 初始化链表
    head = createNode();
    pthread_mutex_init(&client_list_lock, NULL);

    printf("[svr](%d)[svr_sa](%s:%d) Server has initialized!\n", getpid(), inet_ntoa(server.sin_addr), ntohs(server.sin_port));
    sin_size = sizeof(client);
    while (!sigint_flag)
    {

        if ((connectfd = accept(listenfd, (struct sockaddr *)&client, &sin_size)) < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            perror("accept error");
            exit(1);
        }

        else
        {
            printf("[svr](%d)[cli_sa](%s:%d) Client is accepted!\n", getpid(), inet_ntoa(client.sin_addr), ntohs(client.sin_port));

            Client *newClient = createNode();
            newClient->clientfd = connectfd;
            newClient->clientsock = client;
            newClient->next = NULL;
            read(connectfd, newClient->nickname, MAX_MSG_SIZE);
            trim_linefeed(newClient->nickname);
            printf("[svr](%d)[cli_sa](%s:%d)[nickname](%s) User logged in!!\n", getpid(), inet_ntoa(client.sin_addr), ntohs(client.sin_port), newClient->nickname);
            char *info = getInfo(newClient->nickname, LOGIN);
            broadcast(info);
            free(info);
            insertNode(newClient);
            displayAllClients();

            pthread_t tid;
            if (pthread_create(&tid, NULL, clientHandler, newClient) != 0)
            {
                perror("pthread_create");
                exit(EXIT_FAILURE);
            }
            pthread_detach(tid);
        }
    }
    pthread_mutex_destroy(&client_list_lock);
    close(listenfd);
    return 0;
}

void *clientHandler(void *arg)
{
    Client *cli = (Client *)arg;

    pthread_t self = pthread_self();
    printf("[chd](%ld) Child thread is created!\n", self);
    int size;
    char buffer[MAX_MSG_SIZE + 2];
    char forward_buffer[MAX_FORWARD_MSG_SIZE];
    while (!sigint_flag)
    {
        bzero(buffer, sizeof(buffer));
        if ((size = read(cli->clientfd, buffer, MAX_MSG_SIZE + 2)) < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            perror("read");
            exit(1);
        }
        else if (size == 0)
        {
            printf("[chd](%ld)[cli_sa](%s:%d)[nickname](%s) Client is closed!\n", self, inet_ntoa(cli->clientsock.sin_addr), ntohs(cli->clientsock.sin_port), cli->nickname);
            break;
        }
        else
        {
            bzero(forward_buffer, sizeof(forward_buffer));
            char *currentTime = getCurrentTime();
            snprintf(forward_buffer, MAX_FORWARD_MSG_SIZE - 1, "%s\n[%s]: %s", currentTime, cli->nickname, buffer);
            printf("[chd](%ld)[cli_sa](%s:%d)\n%s", self, inet_ntoa(cli->clientsock.sin_addr), ntohs(cli->clientsock.sin_port), forward_buffer);
            free(currentTime);
            fflush(stdout);
            broadcast(forward_buffer);
        }
    }
    char tmp[MAX_MSG_SIZE];
    bzero(tmp, sizeof(tmp));
    strcpy(tmp, cli->nickname);
    close(cli->clientfd);
    printf("[chd](%ld)[cli_sa](%s:%d) Client file descriptor is closed!\n", self, inet_ntoa(cli->clientsock.sin_addr), ntohs(cli->clientsock.sin_port));

    deleteNode(cli->clientfd);

    char *info = getInfo(tmp, LOGOUT);
    broadcast(info);
    free(info);

    displayAllClients();
    printf("[chd](%ld) Child thread is to return!\n", self);
    pthread_exit(NULL);
}

Client *createNode()
{
    Client *__client = (Client *)malloc(sizeof(Client));
    __client->next = NULL;
    bzero(__client->nickname, sizeof(__client->nickname));
    return __client;
}
int deleteNode(int targetfd)
{
    pthread_mutex_lock(&client_list_lock);
    Client *current = head->next, *prev = head;
    while (head != NULL)
    {
        if (current->clientfd == targetfd)
        {
            prev->next = current->next;
            free(current);
            pthread_mutex_unlock(&client_list_lock);
            return 1;
        }
        else
        {
            prev = current;
            current = current->next;
        }
    }
    pthread_mutex_unlock(&client_list_lock);
    return 0;
}
void insertNode(Client *newNode)
{
    pthread_mutex_lock(&client_list_lock);
    newNode->next = head->next;
    head->next = newNode;
    pthread_mutex_unlock(&client_list_lock);
}

void displayAllClients()
{
    pthread_mutex_lock(&client_list_lock);
    printf("====================\nClients:\n");
    for (Client *current = head->next; current != NULL; current = current->next)
    {
        printf("{nickname: %s, fd: %d, socket address: %s:%d}", current->nickname, current->clientfd, inet_ntoa(current->clientsock.sin_addr), ntohs(current->clientsock.sin_port));
        if (current->next != NULL)
        {
            printf("->");
        }
        fflush(stdout);
    }
    printf("\n====================\n");
    pthread_mutex_unlock(&client_list_lock);
}

void broadcast(const char *content)
{
    pthread_mutex_lock(&client_list_lock);
    Client *current = head->next;
    while (current != NULL)
    {
        write(current->clientfd, content, strlen(content));
        current = current->next;
    }
    pthread_mutex_unlock(&client_list_lock);
}

char *getCurrentTime()
{
    time_t currentTime;
    struct tm *localTime;
    char *timeString = (char *)malloc(sizeof(char) * 128);

    // 获取当前时间
    currentTime = time(NULL);

    // 转换为本地时间
    localTime = localtime(&currentTime);

    // 格式化时间字符串
    strftime(timeString, 128, "%Y-%m-%d %H:%M:%S", localTime);

    return timeString;
}

void trim_linefeed(char *str)
{
    if (str[strlen(str) - 1] == '\n')
    {
        str[strlen(str) - 1] = '\0';
    }
}

char *getInfo(const char *nickname, int mode)
{
    char *str = (char *)malloc(MAX_FORWARD_MSG_SIZE);
    bzero(str, sizeof(str));
    char *currentTime = getCurrentTime();
    ;
    if (mode == LOGIN)
    {
        snprintf(str, MAX_FORWARD_MSG_SIZE, "%s\n[svr] User [%s] logged in\n", currentTime, nickname);
    }
    else
    {
        snprintf(str, MAX_CLIENT_CONNECTION, "%s\n[svr] User [%s] logged out\n", currentTime, nickname);
    }
    free(currentTime);
    return str;
}
svr.c

客户端:

#include <stdio.h>
#include <stdlib.h>		//exit()函数,atoi()函数
#include <unistd.h>		//C 和 C++ 程序设计语言中提供对 POSIX 操作系统 API 的访问功能的头文件
#include <sys/types.h>	//Linux系统的基本系统数据类型的头文件,含有size_t,time_t,pid_t等类型
#include <sys/socket.h> //套接字基本函数
#include <netinet/in.h> //IP地址和端口相关定义,比如struct sockaddr_in等
#include <arpa/inet.h>	//inet_pton()等函数
#include <string.h>		//bzero()函数
#include <pthread.h>

#define MAX_MSG_SIZE 128
#define MAX_FORWARD_MSG_SIZE (MAX_MSG_SIZE + 64)

int exit_flag = 0;
int serverfd;
pthread_t sendTid, rcvdTid;

typedef struct _arg
{
	int serverfd;
} Arg;

void handleMessage(int socket);
void *sendHandler();
void *rcvdHandler();

int main(int argc, char *argv[])
{

	struct sockaddr_in server_addr; // 存放服务器端地址信息,connect()使用
	if (argc != 3)
	{ // 如果命令行用法不对,则提醒并退出
		printf("usage: %s  <server IP address>  <server port>\n", argv[0]);
		exit(0);
	}

	char nickname[MAX_MSG_SIZE];

	while (!exit_flag)
	{
		bzero(nickname, sizeof(nickname));
		printf("enter your nickname to log in...\n>");
		fgets(nickname, MAX_MSG_SIZE, stdin);
		if (strlen(nickname) == 1)
		{
			printf("nickname cannot be null!\n");
		}
		else
		{
			break;
		}
	}

	if ((serverfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("Create socket failed.");
		exit(1);
	}

	bzero(&server_addr, sizeof(server_addr));
	server_addr.sin_family = AF_INET;

	// argv[1] 为服务器IP字符串,需要用inet_pton转换为IP地址
	if (inet_pton(AF_INET, argv[1], &server_addr.sin_addr) == 0)
	{
		perror("Server IP Address Error");
		exit(1);
	}

	// argv[2] 为服务器端口,需要用atoi及htons转换
	server_addr.sin_port = htons(atoi(argv[2]));

	if (connect(serverfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
	{
		perror("connect failed");
		exit(1);
	}

	printf("[cli](%d)[srv_sa](%s:%s) Server is connected!\n", getpid(), argv[1], argv[2]);
	write(serverfd, nickname, strlen(nickname));
	handleMessage(serverfd);
	close(serverfd);
	printf("[cli] serverfd is closed!\n");
	printf("[cli] client is going to exit!\n");
	return 0;
}

void handleMessage(int socket)
{

	pthread_create(&sendTid, NULL, sendHandler, NULL);
	pthread_create(&rcvdTid, NULL, rcvdHandler, NULL);

	pthread_join(sendTid, NULL);
	pthread_join(rcvdTid, NULL);
}

void *sendHandler()
{
	pthread_t self = pthread_self();

	char buffer[MAX_MSG_SIZE + 2];
	int size, msgLen;
	while (!exit_flag)
	{
		bzero(buffer, sizeof(buffer));
		fflush(stdout);
		fgets(buffer, MAX_MSG_SIZE, stdin);
		if (strlen(buffer) == 1)
		{
			continue;
		}
		if (strcmp(buffer, "EXIT\n") == 0)
		{
			exit_flag = 1;
			break;
		}
		printf("[cli] Sending: %s", buffer);
		write(serverfd, buffer, strlen(buffer));
	}
	pthread_cancel(rcvdTid);
	pthread_exit(NULL);
}

void *rcvdHandler()
{
	pthread_t self = pthread_self();

	char buffer[MAX_FORWARD_MSG_SIZE];
	int size;
	while (!exit_flag)
	{
		bzero(buffer, sizeof(buffer));
		if ((size = read(serverfd, buffer, sizeof(buffer))) == -1)
		{
			perror("read");
			break;
		}
		else if (size == 0)
		{
			printf("[cli] server is closed!\n");
			exit_flag = 1;
			break;
		}
		else
		{
			printf("%s", buffer);
		}
	}
	pthread_cancel(sendTid);
	pthread_exit(NULL);
}
cli.c

Makefile:

.PHONY : all clean test

server=svr
client=cli
tarfile=chat.tar

all : $(server).c $(client).c
	gcc -o $(server) $(server).c -lpthread
	gcc -o $(client) $(client).c -lpthread

clean :
	rm -f ./$(server)
	rm -f ./$(client)
	rm -f ./$(tarfile)

package: clean all
	tar -cvf $(tarfile) $(server).c $(client).c
	
Makefile

演示

  1. 执行编译

c语言聊天室,c语言,tcp/ip

  1. 启动服务器

c语言聊天室,c语言,tcp/ip

  1. 启动客户端

c语言聊天室,c语言,tcp/ip

  1. 聊天

c语言聊天室,c语言,tcp/ip

  1. 退出

c语言聊天室,c语言,tcp/ip文章来源地址https://www.toymoban.com/news/detail-778088.html

到了这里,关于C语言Socket编程TCP简单聊天室的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • LinuxC TCP实现简易聊天室

    目录 1.概述 1.1聊天室设计内容 2.系统设计 2.1系统功能设计 2.1.1用户管理 2.1.2聊天室管理 2.1.3聊天管理 2.2系统数据结构设计 2.3系统主要函数设计 3.系统实现 3.2功能模块的程序流程图及运行界面 3.2.1功能模块流程图  3.2.2运行界面 4.源代码 4.1客户端 4.2服务器 注:存在问题 1

    2024年02月09日
    浏览(39)
  • 基于TCP协议的聊天室详细教学(C++)

    服务器只转发消息不参到信息交流中 客户端输入用户名后默认进入群聊模式,输入“获取用户列表”可以获取在线用户用户名列表,如果需要私聊某一个用户需要输入“私聊+用户名”可进入私聊模式,输入“退出私聊”可退出私聊重新进入群聊模式。 基本群聊展示 获取用户

    2024年02月08日
    浏览(55)
  • QT C++ 基于TCP通信的网络聊天室

    .ui .pro 在pro文件中添加network库 .h .main .cpp .ui .pro 在pro文件中添加network库 .h .main .cpp        

    2024年02月09日
    浏览(55)
  • .NET编程——利用C#实现基于Socket类的聊天室(WinForm)

    在学习C#和MySQL实现注册登录和TCP协议的Socket通信后,本文将介绍如何利用Socket类中的异步通信函数来实现本地聊天室功能, Socket通信限制了客户端与客户端之间的通信,客户端只能接收来自服务器的消息而不能接收到客户端发送的消息,因此服务器最佳的选择是起到一个中

    2023年04月21日
    浏览(71)
  • 计算机网络 TCP/UDP程序开发网络聊天室

    TCP/UDP程序开发 开发TCP/UDP协议应用程序,掌握网络应用程序的工作原理。通过该实验,深入理解UDP和TCP协议的异同点,了解网络协议的工作过程,学会网络通信编程的基本方法,能够编制网络应用程序。 (1)了解和掌握“基于UDP-面向无连接的应用程序/基于TCP-面向连接的

    2024年02月05日
    浏览(85)
  • 【Unity 3D】利用C#、Unity和Socket实现简单的在线聊天室工具(附源码 简单易懂)

    需要源码请点赞关注收藏后评论区留言并且私信~~~ 下面利用Unity和C#语言做一个简单的聊天室程序,主要用到的技术就是Socket通信连接,需要一个客户端和一个服务器端,服务器端就使用C#语言的控制台完成 下面就开始搭建C#语言服务器端 1:新建一个C#语言控制台程序 2:命名

    2024年02月05日
    浏览(56)
  • QT程序设计多人聊天室(基于QT、sqlite3、TCP/IP)

    目录 技术路线 效果展示 程序主体 sqoperator.h mylogin.h myenroll.h chatinterface.h tips.h myapp.h ******************* sqoperator.cpp mylogin.cpp myenroll.cpp chatinterface.cpp tips.cpp myapp.cpp main.cpp widget.h widget.cpp main.cpp QT程序设计、sqlite数据库调用、TCP/IP客户端与服务端的搭建 通过次程序代码,可以学习如

    2024年02月09日
    浏览(59)
  • 使用Linux系统IO多路复用中eopll创建基于TCP通信协议的多人聊天室

    一.1.搭建好TCP的通信模型 2.创建红黑树根节点 3.将套接字事件添加到红黑树中,使其被监听 4.当套接字事件发生,表示有客户端连接,将连接事件加入到红黑树节点当中 5.每当连接事件发生时,表示客户端发送信息到服务器 6.每当有事件准备就绪时,将对应的红黑树节点信息

    2024年02月13日
    浏览(41)
  • 【网络编程】UDP简单实现翻译软件与网络聊天室

    在上一章【网络编程】demo版UDP网络服务器实现实现了客户端和服务端之间的数据的发送与接收,上一章我们是直接让服务端把接收到的数据打印出来。 但是服务端并不是只接收到数据就完了,它还要 处理任务 。 所以我们可以在服务端设置一个回调函数: 用来处理接收到的

    2024年02月05日
    浏览(72)
  • 计算机网络技术与JAVA网络编程手写Socket聊天室-----JAVA入门基础教程-----计算机网络经典

    import java.io.*; import java.net.Socket; import java.util.Scanner; public class ChatClient { public static void main(String[] args) { try { Socket socket = new Socket(\\\"127.0.0.1\\\",9090); new Thread(new Runnable() { @Override public void run() { InputStream inputStream = null; while(true) { try { inputStream = socket.getInputStream(); } catch (IOException e)

    2024年02月15日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包