#include<string.h>
#include<signal.h>
#include<stdio.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netdb.h>
#include<pthread.h>
#include<memory.h>
#include<semaphore.h>
int Thread_num=,count=; //定义客户端计数器,写线程计数器
int sockfd;
sem_t sem,sem2;
pthread_mutex_t tmutex,cmutex;
pthread_attr_t pattr; //定义要创建的线程属性
void ct_thread(char* arg,int acpfd);
char chatct[]; //定义聊天缓存
void get_sys_time(char* tbuf) //函数功能:得到当前系统时间并进行裁剪
{
long t=time();
char *stime=ctime(&t);
int i=;
tbuf[]='(';
for(;i<;i++)
tbuf[i-]=stime[i];
tbuf[i-]=')';
tbuf[i-]='\0';
}
void* do_read(void* arg) //函数功能:读线程,用于接收客户端发来的信息,保存到缓冲区
{
int acfd=(int)arg;
char timeb[];
// char name[7];
// memset(name,0,sizeof(name));
// read(acfd,name,sizeof(name));
// name[sizeof(name)-1]='\0';
// puts(name);
while()
{
int ret;
char buf[];
memset(buf,,sizeof(buf));
ret=read(acfd,buf,sizeof(buf));
sem_wait(&sem2);
if(ret<)
{
perror("do_read errro");
continue;
}
else if(ret==)
{
close(acfd);
sem_post(&sem2);
printf("a person exit\n");
break;
}
else
{
memset(timeb,,sizeof(timeb));
// strcpy(chatct,name);
// puts(chatct);
// strcat(chatct,":");
// puts(chatct);
get_sys_time(timeb);
strcpy(chatct,buf);
strcat(chatct,timeb);
// puts(chatct);
// printf("do_read %lu\n",pthread_self());
sem_post(&sem);
}
// puts(chatct);
}
return (void*);
}
void* do_write(void* arg) //函数功能:写线程,用于将缓冲区的数据发送到连接上来的每个客户端,当所有客户端接收到消息后,清空缓冲区。
{
int fd=(int)arg;
while()
{
sem_wait(&sem);
if(write(fd,chatct,sizeof(chatct))<)
{
Thread_num--;
if(count==Thread_num)
{
memset(chatct,,sizeof(chatct));
pthread_mutex_lock(&cmutex);
count=;
pthread_mutex_unlock(&cmutex);
sem_post(&sem2); }
else if(count<Thread_num)
{
sem_post(&sem);
}
close(fd);
break;
}
else
{
pthread_mutex_lock(&cmutex);
count++;
pthread_mutex_unlock(&cmutex);
}
if(count<Thread_num)
{
sem_post(&sem);
usleep();
}
else if(count==Thread_num)
{
memset(chatct,,sizeof(chatct));
pthread_mutex_lock(&cmutex);
count=;
pthread_mutex_unlock(&cmutex);
sem_post(&sem2);
}
}
return (void*);
}
void do_thread(int acpfd) //函数功能:对于每个连接上来的客户端 创建一个读线程,一个写线程
{
// char* start="------------------welcome my chatroom--------------------\n";
// write(acpfd,start,strlen(start));
pthread_mutex_lock(&cmutex);
Thread_num++; //每创建一个 线程个数计数加1
pthread_mutex_unlock(&cmutex);
ct_thread("read",acpfd);
ct_thread("write",acpfd);
}
void ct_thread(char* arg,int acpfd) //函数功能,根据传参不同,创建不同类型的函数。
{
pthread_t pt;
/* if(!strcmp(arg,"create"))
{
if(pthread_create(&pt,&pattr,thread_ct,(void*)acpfd)<0)
{
perror("thread creat error");
exit(1);
}
}
*/ //以分离方式创建线程可避免资源无法回收
if(!strcmp(arg,"read"))
{
if(pthread_create(&pt,&pattr,do_read,(void*)acpfd)<)
{
perror("thread creat error");
exit();
}
}
else if(!strcmp(arg,"write"))
{
if(pthread_create(&pt,&pattr,do_write,(void*)acpfd)<)
{
perror("thread creat error");
exit();
}
}
}
void mysighand(int signo) //函数功能:当程序结束,处理返回的信号,释放资源
{
if(signo==SIGINT)
{
printf("server close!\n");
close(sockfd);
sem_destroy(&sem);
sem_destroy(&sem2);
pthread_mutex_destroy(&cmutex);
pthread_mutex_destroy(&tmutex);
pthread_attr_destroy(&pattr);
exit();
}
}
int main(int argc,char* argv[])
{
if(argc<)
{
perror("argc error");
exit();
}
if(signal(SIGINT,mysighand)==SIG_ERR) //登记信号处理函数
{
perror("signal error");
exit();
}
/* 1 初始化
sem_init(&sem,,);
sem_init(&sem2,,);
pthread_mutex_init(&tmutex,NULL);
pthread_mutex_init(&cmutex,NULL);
pthread_attr_init(&pattr);
*/
  if(pthread_attr_setdetachstate(&pattr,PTHREAD_CREATE_DETACHED)<) //设置分离属性
{
perror("setdetached error");
exit();
}
memset(chatct,,sizeof(chatct)); //初始化缓存区
sockfd=socket(AF_INET,SOCK_STREAM,);//以TCP方式创建socket
if(sockfd<)
{
perror("sockfd error");
exit();
}
struct sockaddr_in ser;
ser.sin_family=AF_INET;        //IP类型:IPV4
ser.sin_port=htons(atoi(argv[])); //主机字节序转换成网络字节序
ser.sin_addr.s_addr=INADDR_ANY;  //主机所有可访问IP 
if(bind(sockfd,(struct sockaddr*)&ser,sizeof(ser))<) //socket和IP绑定
{
perror("bind error");
exit();
}
if(listen(sockfd,)<) //进行监听等待客户端连接
{
perror("listen error");
exit();
}
while()
{
int acpfd;
if((acpfd=accept(sockfd,NULL,NULL))<) //处理每个连接上来的客户端,如果无客户端连接,则阻塞
break;
else
{
do_thread(acpfd); //执行处理连接函数
}
}
return ;
}

本服务端的主要思想:

为每一个连接上来的客户端创建一个读线程和写线程(分离状态启动的线程,线程进行自我资源回收),服务端和客户端的通信实际就是多读者多写者的模型(利用信号量和客户端计数器,线程计数器,实现线程的同步和互斥)

不同点在于 当客户端断开连接后,服务端应当及时改变客户端计数器,并进行逻辑处理。

服务端运行在云服务器上,客户端可以用QT 或者android 等来实现。

不足点:

仅仅是匿名聊天。功能较简单。只是显示聊天内容和数据发送的时间

优点:

可以支持大量客户端同时进行连接。并且在网络速度不健康的情况下数据不会出错(非网络在信道传递时的错误)

												

socket聊天室(服务端)(多线程)(TCP)的更多相关文章

  1. Socket聊天程序——服务端

    写在前面: 昨天在博客记录自己抽空写的一个Socket聊天程序的初始设计,那是这个程序的整体设计,为了完整性,今天把服务端的设计细化记录一下,首页贴出Socket聊天程序的服务端大体设计图,如下图: ...

  2. Winfrom 基于TCP的Socket服务端 多线程(进阶版)

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  3. 用c++语言socket库函数实现服务端客户端聊天室

    客户端 /* * 程序名:client.cpp,此程序用于演示socket的客户端 * 作者:C语言技术网(www.freecplus.net) 日期:20190525 */ #include < ...

  4. TCP/IP以及Socket聊天室带类库源码分享

    TCP/IP以及Socket聊天室带类库源码分享 最近遇到个设备,需要去和客户的软件做一个网络通信交互,一般的我们的上位机都是作为客户端来和设备通信的,这次要作为服务端来监听客户端,在这个背景下,我查 ...

  5. Java Socket聊天室编程(一)之利用socket实现聊天之消息推送

    这篇文章主要介绍了Java Socket聊天室编程(一)之利用socket实现聊天之消息推送的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 网上已经有很多利用socket实现聊天的例子了 ...

  6. python socket 聊天室

    socket 发送的时候,使用的是全双工的形式,不是半双工的形式.全双工就是类似于电话,可以一直通信.并且,在发送后,如果又接受数据,那么在这个接受到数据之前,整个过程是不会停止的.会进行堵塞,堵塞就 ...

  7. Asp.Net - 9.socket(聊天室)

    9.1 Socket相关概念 IP地址 每台联网的电脑都有一个唯一的IP地址. 长度32位,分为四段,每段8位,用十进制数字表示,每段范围 0 ~ 255 特殊IP:127.0.0.1 用户本地网卡测 ...

  8. Java Socket聊天室编程(二)之利用socket实现单聊聊天室

    这篇文章主要介绍了Java Socket聊天室编程(二)之利用socket实现单聊聊天室的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 在上篇文章Java Socket聊天室编程(一)之 ...

  9. Socket通信时服务端无响应,客户端超时设置

    背景:在写一个客户端的socket程序,服务端没有返回消息,客户端一直在等待. 目标:我需要设置一个时间,如果超过这个时间客户端自动断开连接.最好是在服务端实现,客户端对我来说不可控.

随机推荐

  1. 使用gulp编译sass

    之前写了一篇在ruby环境下如何编译sass的文章:<css预处理器sass使用教程(多图预警)>,随着现在前端构建工具的兴起,也学着使用这些工具来编译sass.webpack存在一个CS ...

  2. 【转】JDBC学习笔记(10)——调用函数&存储过程

    转自:http://www.cnblogs.com/ysw-go/ 如何使用JDBC调用存储在数据库中的函数或存储过程: * 1.通过COnnection对象的prepareCall()方法创建一个C ...

  3. CF CROC 2016 Intellectual Inquiry

    题目链接:http://codeforces.com/contest/655/problem/E 大意是Bessie只会英文字母表中的前k种字母,现在有一个长度为m+n的字母序列,Bessie已经知道 ...

  4. JavaWeb的国际化(17/4/8)

      国际化的缺点: 因为文字不同,所以带来的排版问题一样严重,通常都是重新在写一个网站反而更加清晰,快捷 1:需要从浏览器中获取到浏览器语言(Accept-Language) 2:利用locale获取 ...

  5. Java泛型知识点:泛型类、泛型接口和泛型方法

    有许多原因促成了泛型的出现,而最引人注意的一个原因,就是为了创建容器类. 泛型类 容器类应该算得上最具重用性的类库之一.先来看一个没有泛型的情况下的容器类如何定义: public class Cont ...

  6. 统计学习方法:核函数(Kernel function)

    作者:桂. 时间:2017-04-26  12:17:42 链接:http://www.cnblogs.com/xingshansi/p/6767980.html 前言 之前分析的感知机.主成分分析( ...

  7. extern用法详解

    1 基本解释 extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义. 另外,extern也可用来进行链接指定. 2 问题:ext ...

  8. 关于phpMyAdmin表数据不能编辑更改的问题

    今天在使用phpMyAdmin更改表中的数据时, 发现表的前面没有编辑.复制.删除这些内容, 于是我很奇怪,上网查了查,终于找到了一个解决的办法, 原来是我的这个表没有主键,于是我点击表 的 结构 , ...

  9. SSM框架搭建——我的第一个SSM项目

    转载自:http://blog.csdn.net/tmaskboy/article/details/51464791 作者使用MyEclipse 2014版本 本博客所编写程序源码为: http:// ...

  10. druid抛出的异常------javax.management.InstanceAlreadyExistsException引发的一系列探索

    最近项目中有个定时任务的需求,定时检查mysql数据与etcd数据的一致性,具体实现细节就不说了,今天要说的就是实现过程中遇到了druid抛出的异常,以及解决的过程 异常 异常详细信息 五月 05, ...