开发环境:Linux,GCC

相关知识:TCP(博客:传送门),线程

附加:项目可能还有写不足之处,有些bug没调出来(如:对在线人数的控制),希望大佬赐教。

那么话不多说,放码过来:

码云:传送门,GitHub:传送门

服务端:server.c

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h> struct sockaddr_in addr = {};
int clifd_index = ; // clifd的下标
char buf[] = {}; // 存储客户端发来的字符串
char str[] = {}; // 存储带clifd的回传信息
int clifd[] = {}; // 存储clifd
int online_num = ; // 连接人数
int max_num = ; // 最大人数 // 项目有bug,连接人数的限制控制不住,有待改进 void* start_read(void *arg) // 读取信息的子线程
{
// printf("arg:%d\n",*(int*)arg);
int clifd1 = *(int*)arg;
printf("run_clifd:%d\n",clifd1); for(;;)
{
// printf("before read\n"); int ret = read(clifd1,buf,sizeof(buf));
printf("\nip:%s,port:%hu,size:%d\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port),ret); // 获取相关信息
printf("say:%s\n",buf); char id[] = {};
sprintf(id,"%d说:",clifd1); if(strlen(buf) != )
{
strcpy(str,id);
strcat(str,buf);
} // 存入str中 if( == strcmp("quit",buf)) // 如果收到quit
{
online_num--; // 在线人数-1
for(int i=; i<clifd_index; i++)
{
if(clifd1 == clifd[i])
{
int *die = &clifd1;
clifd[i] = ;
pthread_exit(die); // 终止线程
break;
}
}
}
//usleep(1000);
}
} void* start_write(void *arg) // 写回的子线程
{
// printf("arg:%d\n",*(int*)arg); // usleep(500); int clifd1 = *(int*)arg; printf("run_clifd:%d\n",clifd1); for(;;)
{
int flag = ;
for(int i=; i<clifd_index; i++) // 因为读到quit的原因,clifd被置0
{
if(clifd1 == clifd[i])
{
break;
}
if(i == clifd_index-)
{
int *die = &clifd1;
flag = ;
pthread_exit(die); // 终止此写回的子线程
}
}
if(flag == )
{
break;
} if(strlen(str) == ) // 空消息不写入
continue;
printf("before write\n");
printf("str:%s\n",str);
write(clifd1,str,strlen(str)+);
usleep(); // 最快的子线程等待其他子线程
memset(str,,); // 清空str
}
} int main()
{
printf("服务器创建socket...\n");
int sockfd = socket(AF_INET,SOCK_STREAM,);
if( > sockfd)
{
perror("socket");
return -;
} printf("准备地址...\n"); addr.sin_family = AF_INET;
addr.sin_port = htons();//端口号
addr.sin_addr.s_addr = inet_addr("10.0.2.15");//你的ip地址(或服务器的私网ip)
socklen_t len = sizeof(addr); printf("绑定socket与地址...\n");
if(bind(sockfd,(struct sockaddr*)&addr,len))
{
perror("bind");
return -;
} printf("设置监听...\n");
if(listen(sockfd,))
{
perror("listen");
return -;
} printf("等待客户端连接...\n");
for(;;)
{
if(online_num < max_num)
{
struct sockaddr_in addrcli = {};
clifd[clifd_index] = accept(sockfd,(struct sockaddr*)&addrcli,&len); int flag = ;
for(int i=; i<clifd_index; i++)
{
if(clifd[clifd_index] == clifd[i])
{
flag = ;
break;
}
} if(flag == )
{
clifd_index--;
continue;
}
else
{
char link[] = {};
char link1[] = "您已经成功连接";
sprintf(link,"您的id是:%d,",clifd[clifd_index]);
strcat(link,link1);
write(clifd[clifd_index],link,strlen(link)+);
online_num++;
}
}
else
{
continue;
} if( > clifd[clifd_index])
{
perror("accept");
continue;
} printf("clifd:%d\n",clifd[clifd_index]); // 创建子线程
pthread_t pid1,pid2;
pthread_create(&pid1,NULL,start_read,&clifd[clifd_index]);
pthread_create(&pid2,NULL,start_write,&clifd[clifd_index]); usleep(); // printf("clifd:%d\n",clifd[index]); clifd_index++; // 下标逐渐+1,这样写不是很合适 }
return ;
}

客户端:client.c

#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h> void* start_read(void* arg) // 读取信息
{
int sockfd = *(int*)arg;
char buf[] = {};
for(;;)
{
read(sockfd,buf,sizeof(buf));
if(strlen(buf) != )
{
printf("\n>%s\n",buf);
}
}
} int main()
{
printf("服务器创建socket...\n");
int sockfd = socket(AF_INET,SOCK_STREAM,);
if( > sockfd)
{
perror("socket");
return -;
} printf("准备地址...\n");
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons();//设置的端口号
addr.sin_addr.s_addr = inet_addr("10.0.2.15");//你的ip地址(或服务器的公网ip)
socklen_t len = sizeof(addr); printf("绑定连接服务器...\n");
if(connect(sockfd,(struct sockaddr*)&addr,len))
{
perror("connect");
return -;
} char link[] = {};
read(sockfd,link,sizeof(link));
// printf("link:%s\n",link);
if(strstr(link,"您已经成功连接")==NULL)
{
printf("连接人数已满,请稍后重试\n");
return ;
}
else
{
printf("link:%s\n",link);
} // 创建读取子线程
pthread_t pid;
pthread_create(&pid,NULL,start_read,&sockfd); for(;;)
{
char buf[] = {};
usleep();
//printf(">我说:");
gets(buf);
write(sockfd,buf,strlen(buf)+);
if( == strcmp("quit",buf))
{
printf("通信结束!\n");
break;
}
} close(sockfd);
}
 

Linux下c语言TCP多线程聊天室的更多相关文章

  1. TCP多线程聊天室

    TCP协议,一个服务器(ServerSocket)只服务于一个客户端(Socket),那么可以通过ServerSocket+Thread的方式,实现一个服务器服务于多个客户端. 多线程服务器实现原理— ...

  2. Linux以下基于TCP多线程聊天室(server)

    接上篇博文,本文是server端的实现,主要实现的功能,就是现实client的连接.转发client发送的消息.以及client掉线提示等功能,同一时候能够在这这上面扩展和TCP以及线程相关的功能木块 ...

  3. Linux以下基于TCP多线程聊天室(client)

    不怎么会弄这个博客的排版,就直接将代码附上: 主要是使用多线程去等待接受数据和发送数据.以下是client的代码: tcpsed.h文件 1 2 3 4 5 6 7 8 9 10 11 12 13 1 ...

  4. linux下c语言的多线程编程

    我们在写linux的服务的时候,经常会用到linux的多线程技术以提高程序性能 多线程的一些小知识: 一个应用程序可以启动若干个线程. 线程(Lightweight Process,LWP),是程序执 ...

  5. linux下C语言实现多线程通信—环形缓冲区,可用于生产者(producer)/消费者(consumer)【转】

    转自:http://blog.chinaunix.net/uid-28458801-id-4262445.html 操作系统:ubuntu10.04 前言:     在嵌入式开发中,只要是带操作系统的 ...

  6. linux下c语言实现多线程文件复制【转】

    转自:https://www.cnblogs.com/zxl0715/articles/5365989.html .具体思路 把一个文件分成N份,分别用N个线程copy, 每个线程只读取指定长度字节大 ...

  7. 基于Linux的TCP网络聊天室

    1.实验项目名称:基于Linux的TCP网络聊天室 2.实验目的:通过TCP完成多用户群聊和私聊功能. 3.实验过程: 通过socket建立用户连接并传送用户输入的信息,分别来写客户端和服务器端,利用 ...

  8. linux下C语言多线程编程实例

    用一个实例.来学习linux下C语言多线程编程实例. 代码目的:通过创建两个线程来实现对一个数的递加.代码: //包含的头文件 #include <pthread.h> #include ...

  9. linux 下C语言学习路线

    UNIX/Linux下C语言的学习路线.一.工具篇“公欲善其事,必先利其器”.编程是一门实践性很强的工作,在你以后的学习或工作中,你将常常会与以下工具打交道, 下面列出学习C语言编程常常用到的软件和工 ...

随机推荐

  1. WorkFlow四:添加用户决策步骤

    沿用之前的例子,做个用户决策步骤. 1.事物代码SWDD: 进入抬头,点击类的绑定按钮. 2.选择类的绑定,点击继续. 这是类的绑定已经变色了.这时候点击保存,再点击返回到图片逻辑流界面. 3.在发送 ...

  2. Activity + 基础UI

    目录 Activity + 基础UI 1. 返回键退出进入应用: 2. home键以后 3. home键退出切换字体 二:AndroidManifest配置theme改变UI效果 三:UI 报错: A ...

  3. Oracle分析函数FIRST_VALUE、LAST_VALUE

    FIRST_VALUE.LAST_VALUE分析函数可以按照特定分组和排序取出组内首尾值,语法 FIRST_VALUE { (expr) [ {RESPECT | IGNORE} NULLS ] | ...

  4. 201871010135-张玉晶《面向对象程序设计(Java)》第四周学习总结

    201871010135-张玉晶<面向对象程序设计(Java)>第四周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这 ...

  5. Java多线程编程核心技术-第7章-拾遗增补-读书笔记

    第 7 章 拾遗增补 本章主要内容 线程组的使用. 如何切换线程状态. SimpleDataFormat 类与多线程的解决办法. 如何处理线程的异常. 7.1 线程的状态 线程对象在不同的运行时期有不 ...

  6. linux 以导入文件形式添加定时任务(crontab)时需要注意的坑

    在实际操作过程中发现,使用导入文件形式添加定时任务时,会将用户已有的定时任务全部覆盖清理(先清空,再重新导入),所以在使用文件导入定时任务时,需要先将已有定时任务导出,然后将新任务进行追加到已有定时任 ...

  7. JSPDF支持中文(思源黑体)采坑之旅,JSPDF中文字体乱码解决方案

    我拍个砖,通常标称自己文章完美解决何种问题的,往往就是解决不了任何问题! 众所周知,JSPDF是一个开源的,易用的,但是对中文支持非常差的PDF库. 下面,我教大家,如何在pdf中使用思源黑体.思源黑 ...

  8. 11-散列4 Hashing - Hard Version (30 分)

    Given a hash table of size N, we can define a hash function H(x)=x%N. Suppose that the linear probin ...

  9. tensorflow数据加载、模型训练及预测

    数据集 DNN 依赖于大量的数据.可以收集或生成数据,也可以使用可用的标准数据集.TensorFlow 支持三种主要的读取数据的方法,可以在不同的数据集中使用:本教程中用来训练建立模型的一些数据集介绍 ...

  10. [Noip2018]填数游戏

    传送门 Description 耳熟能详,就不多说了 Solution 对于一个不会推式子的蒟蒻,如何在考场优雅地通过此题 手玩样例,发现对于 \(n=1\) , \(ans=2^m\) .对于 \( ...