socket聊天室(服务端)(多线程)(TCP)

#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=0,count=0; //定义客户端计数器,写线程计数器
int sockfd;
sem_t sem,sem2;
pthread_mutex_t tmutex,cmutex;
pthread_attr_t pattr; //定义要创建的线程属性
void ct_thread(char* arg,int acpfd);
char chatct[1024]; //定义聊天缓存
void get_sys_time(char* tbuf)  //函数功能:得到当前系统时间并进行裁剪
{
    long t=time(0);
    char *stime=ctime(&t);
    int i=11;
    tbuf[0]=‘(‘;
    for(;i<20;i++)
        tbuf[i-10]=stime[i];
    tbuf[i-11]=‘)‘;
    tbuf[i-10]=‘\0‘;
}
void* do_read(void* arg)    //函数功能:读线程,用于接收客户端发来的信息,保存到缓冲区{
    int acfd=(int)arg;
    char timeb[13];
//    char name[7];
//    memset(name,0,sizeof(name));
//    read(acfd,name,sizeof(name));
//    name[sizeof(name)-1]=‘\0‘;
//    puts(name);
    while(1)
    {
        int ret;
        char buf[1024];
        memset(buf,0,sizeof(buf));
        ret=read(acfd,buf,sizeof(buf));
        sem_wait(&sem2);
        if(ret<0)
        {
            perror("do_read errro");
            continue;
        }
        else if(ret==0)
        {
            close(acfd);
            sem_post(&sem2);
            printf("a person exit\n");
            break;
        }
        else
        {
            memset(timeb,0,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*)0;
}
void* do_write(void* arg)  //函数功能:写线程,用于将缓冲区的数据发送到连接上来的每个客户端,当所有客户端接收到消息后,清空缓冲区。
{
    int fd=(int)arg;
    while(1)
    {
        sem_wait(&sem);
        if(write(fd,chatct,sizeof(chatct))<0)
        {
            Thread_num--;
            if(count==Thread_num)
            {
                memset(chatct,0,sizeof(chatct));
                pthread_mutex_lock(&cmutex);
                count=0;
                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(1);
        }
        else if(count==Thread_num)
        {
            memset(chatct,0,sizeof(chatct));
            pthread_mutex_lock(&cmutex);
            count=0;
            pthread_mutex_unlock(&cmutex);
            sem_post(&sem2);
        }
    }
    return (void*)0;
}
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)<0)
        {
            perror("thread creat error");
            exit(1);
        }
    }
    else if(!strcmp(arg,"write"))
    {
        if(pthread_create(&pt,&pattr,do_write,(void*)acpfd)<0)
        {
            perror("thread creat error");
            exit(1);
        }
    }
}
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(1);
    }
}
int main(int argc,char* argv[])
{
    if(argc<2)
    {
        perror("argc error");
        exit(1);
    }
    if(signal(SIGINT,mysighand)==SIG_ERR) //登记信号处理函数
    {
        perror("signal error");
        exit(1);
    }/*  1 初始化
    sem_init(&sem,0,0);
    sem_init(&sem2,0,1);
    pthread_mutex_init(&tmutex,NULL);
    pthread_mutex_init(&cmutex,NULL);
    pthread_attr_init(&pattr);*/   if(pthread_attr_setdetachstate(&pattr,PTHREAD_CREATE_DETACHED)<0) //设置分离属性
    {
        perror("setdetached error");
        exit(1);
    }
    memset(chatct,0,sizeof(chatct)); //初始化缓存区
     sockfd=socket(AF_INET,SOCK_STREAM,0);//以TCP方式创建socket
    if(sockfd<0)
    {
        perror("sockfd error");
        exit(1);
    }
    struct sockaddr_in ser;
    ser.sin_family=AF_INET;        //IP类型:IPV4
    ser.sin_port=htons(atoi(argv[1]));  //主机字节序转换成网络字节序
    ser.sin_addr.s_addr=INADDR_ANY;     //主机所有可访问IP 
    if(bind(sockfd,(struct sockaddr*)&ser,sizeof(ser))<0) //socket和IP绑定
    {
        perror("bind error");
        exit(1);
    }
    if(listen(sockfd,10)<0)         //进行监听等待客户端连接
    {
        perror("listen error");
        exit(1);
    }
    while(1)
    {
        int acpfd;
        if((acpfd=accept(sockfd,NULL,NULL))<0)  //处理每个连接上来的客户端,如果无客户端连接,则阻塞
            break;
        else
        {
             do_thread(acpfd); //执行处理连接函数
        }
    }
    return 0;
}

本服务端的主要思想:

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

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

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

不足点:

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

优点:

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

				
时间: 2024-10-09 22:36:04

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

聊天室服务端的实现

该服务端是一个客户端连接进来新建一个线程,性能有待优化. socket服务端 public class MyServer { private int port; private ServerHandler serverHandler; public MyServer(int port,ServerHandler serverHandler) throws IOException{ System.out.println("初始化服务....."); this.setPort(port);

goland 简单聊天室服务端

package main import ( "fmt" "net" "strings" "time" ) type Client struct { C chan string //用于发送数据的管道 Name string //用户名 Address string //IP地址 } //保存在线用户 var onLineMap map[string]Client var message = make(chan string)

python socket 聊天室

import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #绑定端口 s.bind(("127.0.0.1", 8888)) while True: data = s.recvfrom(1024) print(str(data[0].decode("gbk"))) send_data = input("请输入聊天内容") if "exit" in se

Web Socket 聊天室

Web sockets test Web Socket 聊天室 按下连接按钮,会通过WebSocket发起一个到聊天浏览器的连接. 服务器地址: 用户名: 连接 发送 来自网上.............

socket 网络编程快速入门(二)教你编写基于UDP/TCP的服务端多线程通信

在上一篇博文中,我们介绍了利用socket进行简单的UDP/TCP的服务端和客户端的通信. (一) 在基于UDP的程序中,你有没有想过,如果我的这台主机在通讯的时候要求既能够收到别的主机发来的数据,又能够自己向目的主机发出数据,该怎样实现?也就是说需要两个while循环同时进行.答案是使用多线程,一个线程用于接受数据,另一个线程用来发送数据.接下来我们介绍WinSock的多线程编程. 多线程的实现我们使用_beginthread()函数: uintptr_t _beginthread( void

c++ Socket客户端和服务端示例版本三(多线程版本)

客户端 #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <string.h> #include<arpa/inet.h> #include <error.h> #includ

【Unity3D自学记录】Unity3D网络之Socket聊天室初探

首先创建一个服务端程序,这个程序就用VS的控制台程序做即可了. 代码例如以下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; namespace SocketServer { class Program { const int Port = 20000; //设置连接port static void Main(strin

socket聊天室

1 #服务端 2 from socket import * 3 import json 4 def recvMsg(s): 5 while True: 6 #接收用户的信息 7 data,address = s.recvfrom(1024) 8 data = json.loads(data) 9 print(data,address) 10 11 if data['type'] == 'enter': 12 # 将用户进入聊天室的信息发给其它所有在线用户 13 sendToAll(('>>系统

PHP的socket连接到服务端模版

在整理新框架的时候,发现对于一些缓存数据,需要对外的接口访问,而比较方便的是php的接口,所以临时研究了下php如何连接java服务端. 先贴上代码: <?php require_once 'CRC16.php'; /*----------------------------- | 向服务器发送数据包 ------------------------------*/ classServer{ //发送数据包 publicstaticfunction sendPacket($packet, $ho