1.实验项目名称:基于Linux的TCP网络聊天室

2.实验目的:通过TCP完成多用户群聊和私聊功能。

3.实验过程:

通过socket建立用户连接并传送用户输入的信息,分别来写客户端和服务器端,利用多线程来实现多用户模式,服务器端随时准备接收客户端发送的消息,并判断该消息类型(私聊或群聊)来进行对应的转发工作,客户端随时接受来自服务器端的消息,从而实现消息的同步。

(1)开启服务器。

(2)开启客户端,输入用户昵称,客户端开始与服务器建立连接。

(3)群聊功能,一名用户发送消息,聊天室的其他成员均可收到此消息。

(4)私聊功能,一位用户@其他用户,则此消息只有被@的用户可以收到。

用户的信息借助结构体来存放,群聊功能通过遍历结构体,把消息发送给每一名用户,私聊功能通过对用户所发消息进行判断,若消息中存在@其他用户则把该消息私发给指定用户。

另外,私聊功能函数可以进行对各种@操作的判断,如用户所@的人不存在,则把此消息作为群消息发出,@其他用户后没有添加空格,则把此消息视为群发消息。

源码:

头文件

  1. 1 //header.h
  2. 2 #ifndef _A_H
  3. 3 #define _A_H
  4. 4 #include <stdio.h>
  5. 5 #include <pthread.h>
  6. 6 #include <sys/socket.h>
  7. 7 #include <netinet/in.h>
  8. 8 #include <arpa/inet.h>
  9. 9 #include <stdlib.h>
  10. 10 #include <unistd.h>
  11. 11 #include <string.h>
  12. 12 #include <signal.h>
  13. 13 #include <pthread.h>
  14. 14 struct client{
  15. 15 char name[30]; //客户端名字
  16. 16 int fds; //客户端socket描述符
  17. 17 };
  18. 18 struct client c[100]={0};//接收客户端100个
  19. 19 int in; //数组下标
  20. 20 char *IP ="127.0.0.1"; //ip
  21. 21 short PORT=10222;
  22. 22 char NAME[20]={}; //@的名字
  23. 23 int FLAG=0; //标记是群发还是私聊
  24. 24 #endif

服务器端

  1. 1 //tcp_s.c
  2. 2 #include "header.h"
  3. 3 int sockfd; //服务器socket
  4. 4 char tempName[20]={};
  5. 5 //初始化服务器网络
  6. 6 void init(){
  7. 7 printf("start chat room...");
  8. 8 sockfd = socket(PF_INET,SOCK_STREAM,0);
  9. 9 if(sockfd == -1){
  10. 10 perror("create socket false\n");
  11. 11 exit(-1);
  12. 12 }
  13. 13 struct sockaddr_in addr; //网络通信地址结构
  14. 14 addr.sin_family = PF_INET; //协议簇
  15. 15 addr.sin_port = htons(PORT); //端口
  16. 16 addr.sin_addr.s_addr = inet_addr(IP); //IP地址
  17. 17 if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) == -1){
  18. 18 perror("bind false\n");
  19. 19 exit(-1);
  20. 20 }
  21. 21 if(listen(sockfd,100) == -1){
  22. 22 perror("Setup listening failed\n");
  23. 23 exit(-1);
  24. 24 }
  25. 25 printf("Server initialization succeeded!\n");
  26. 26 }

分发消息

  1. 1 void SendMessage(char *msg){
  2. 2 int flag=0; //标记@符号是否为私聊
  3. 3 int exit=0; //比较@的用户是否存在
  4. 4 int i=0,j=0;
  5. 5 while(msg[i]!=':'){
  6. 6 if(i>=strlen(msg))
  7. 7 break;
  8. 8 i++;
  9. 9 } //找到@符号位置
  10. 10 if(msg[i+1] == '@'){
  11. 11 i++;
  12. 12 while(msg[i]!=32){
  13. 13 if(i>=strlen(msg)){ //@符号不是私聊作用
  14. 14 flag=1;
  15. 15 break;
  16. 16 }
  17. 17 NAME[j]=msg[i+1];
  18. 18 i++;
  19. 19 j++;
  20. 20 }
  21. 21 if(flag){ //群发
  22. 22 for(i=0;i<in;i++){
  23. 23 printf("send to %d\n",c[i].fds);
  24. 24 send(c[i].fds,msg,strlen(msg),0);
  25. 25 }
  26. 26 }
  27. 27 else{ //私发
  28. 28 for(i=0;i<in;i++){
  29. 29 int k=0;
  30. 30 for(k=0;k<strlen(NAME)-1;k++){
  31. 31 tempName[k]=NAME[k];
  32. 32 }
  33. 33 if(strcmp(c[i].name,tempName)==0){
  34. 34 send(c[i].fds,msg,strlen(msg),0);
  35. 35 exit=1;
  36. 36 break;
  37. 37 }
  38. 38 }
  39. 39 }
  40. 40 }
  41. 41 else{
  42. 42 exit = 1;
  43. 43 for(i=0;i<in;i++){
  44. 44 printf("send to %d\n",c[i].fds);
  45. 45 send(c[i].fds,msg,strlen(msg),0);
  46. 46 }
  47. 47 }
  48. 48 if(exit == 0){
  49. 49 for(i=0;i<in;i++){
  50. 50 printf("send to %d\n",c[i].fds);
  51. 51 send(c[i].fds,msg,strlen(msg),0);
  52. 52 }
  53. 53 }
  54. 54 }

线程函数中进行通信/接收客户端消息,分发给所有客户端 

  1. 1 void *server_thread(void *p){
  2. 2 char name[30]={};
  3. 3 if(recv(c[in].fds,name,sizeof(name),0)>0){
  4. 4 name[strlen(name)]='\0';
  5. 5 strcpy(c[in].name,name);
  6. 6 }
  7. 7 in++;
  8. 8 char tip[100]={};
  9. 9 sprintf(tip,"%s join in the chat room\n",c[in-1].name);
  10. 10 SendMessage(tip);
  11. 11 int fd = *(int*)p;
  12. 12 printf("pthread = %d\n",fd);
  13. 13 while(1){
  14. 14 char buf[100]={};
  15. 15 if(recv(fd,buf,sizeof(buf),0) == 0){
  16. 16 //表示退出连接
  17. 17 printf("fd = %d left the chat room\n",fd);
  18. 18 int i,j;
  19. 19 char name[20]={};
  20. 20 int flag = 1;
  21. 21 for(i=0;i<in;i++){
  22. 22 if(c[i].fds == fd){
  23. 23 strcpy(name,c[i].name);
  24. 24 i++;
  25. 25 flag = 0;
  26. 26 }
  27. 27 if(flag != 1){
  28. 28 c[i-1].fds = c[i].fds;
  29. 29 strcpy(c[i-1].name,c[i].name);
  30. 30 }
  31. 31 }
  32. 32 c[i].fds = 0;
  33. 33 strcpy(c[i].name,"");
  34. 34 in--;
  35. 35 char msg[100];
  36. 36 sprintf(msg,"%s left the chat room\n",name);
  37. 37 SendMessage(msg);
  38. 38 close(fd);
  39. 39 break;
  40. 40 }
  41. 41 SendMessage(buf);
  42. 42 }
  43. 43 }

等待客户端连接,启动服务器的服务

  1. 1 void server(){
  2. 2 printf("Server starts service!\n");
  3. 3 while(1){
  4. 4 struct sockaddr_in fromaddr; //存储客户端通信地址
  5. 5 socklen_t len = sizeof(fromaddr);
  6. 6 int fd = accept(sockfd,(struct sockaddr*)&fromaddr,&len);
  7. 7 if(fd == -1){
  8. 8 perror("Client connection failed!\n");
  9. 9 continue;
  10. 10 }
  11. 11 c[in].fds = fd;
  12. 12 pthread_t pid;
  13. 13 pthread_create(&pid,0,server_thread,&fd);
  14. 14 }
  15. 15 }
  16. 16 void sig_close(){
  17. 17 close(sockfd);
  18. 18 printf("Server close\n");
  19. 19 exit(0);
  20. 20 }
  21. 21 int main()
  22. 22 {
  23. 23 signal(SIGINT,sig_close);
  24. 24 init();
  25. 25 server();
  26. 26 return 0;
  27. 27 }

客户端

  1. 1 //tcp_c.c
  2. 2 #include "header.h"
  3. 3 char name[30]; //name
  4. 4 int sockfd; //客户端socket
  5. 5 int mutax=0; //互斥变量
  6. 6 void init(){
  7. 7 printf("Client starts\n");
  8. 8 sockfd =socket(PF_INET,SOCK_STREAM,0);
  9. 9 struct sockaddr_in addr;
  10. 10 addr.sin_family =PF_INET;
  11. 11 addr.sin_port=htons(PORT);
  12. 12 addr.sin_addr.s_addr=inet_addr(IP);
  13. 13 if(connect(sockfd,(struct sockaddr*)&addr,sizeof (addr))==-1){
  14. 14 perror("connect failed\n");
  15. 15 exit(-1);
  16. 16 }
  17. 17 }

通信

  1. 1 void start(){
  2. 2 pthread_t pid;
  3. 3 void* receive_thread(void*);
  4. 4 pthread_create(&pid,0,receive_thread,0);
  5. 5 while(1){
  6. 6 char buf[100]={};
  7. 7 gets(buf);
  8. 8 char msg[100]={};
  9. 9 if(mutax){
  10. 10 mutax=0;
  11. 11 continue;
  12. 12 }
  13. 13 sprintf(msg,"%s said:%s",name,buf);
  14. 14 send(sockfd,msg,strlen(msg),0);
  15. 15 }
  16. 16
  17. 17 }
  18. 18 void* receive_thread(void *p){
  19. 19 while(1){
  20. 20 char buf[100]={};
  21. 21 if(recv(sockfd,buf,sizeof(buf),0)<=0){
  22. 22 break;
  23. 23 }
  24. 24 printf("%s\n",buf);
  25. 25 }
  26. 26 }
  27. 27 void sig_close(){
  28. 28 close(sockfd);
  29. 29 exit(0);
  30. 30 }
  31. 31 int main(){
  32. 32 signal(SIGINT,sig_close);
  33. 33 printf("Please input your name:");
  34. 34 scanf("%s",name);
  35. 35 mutax=1;
  36. 36 init();
  37. 37 if(mutax)
  38. 38 send(sockfd,name,strlen(name),0);
  39. 39 start();
  40. 40 return 0;
  41. 41 }

基于Linux的TCP网络聊天室的更多相关文章

  1. 基于LINUX的多功能聊天室

    原文:基于LINUX的多功能聊天室 基于LINUX的多功能聊天室 其实这个项目在我电脑已经躺了多时,最初写完项目规划后,我就认认真真地去实现了它,后来拿着这个项目区参加了面试,同样面试官也拿这个项目来 ...

  2. Qt NetWork即时通讯网络聊天室(基于TCP)

    本文使用QT的网络模块来创建一个网络聊天室程序,主要包括以下功能: 1.基于TCP的可靠连接(QTcpServer.QTcpSocket) 2.一个服务器,多个客户端 3.服务器接收到某个客户端的请求 ...

  3. 网络编程TCP协议-聊天室

    网络编程TCP协议-聊天室(客户端与服务端的交互); <span style="font-size:18px;">1.客户端发数据到服务端.</span> ...

  4. Linux下TCP网络编程与基于Windows下C#socket编程间通信

    一.linux下TCP网络编程基础,需要了解相关函数 Socket():用于套接字初始化. Bind():将 socket 与本机上的一个端口绑定,就可以在该端口监听服务请求. Listen():使s ...

  5. php websocket-网页实时聊天之PHP实现websocket(ajax长轮询和websocket都可以时间网络聊天室)

    php websocket-网页实时聊天之PHP实现websocket(ajax长轮询和websocket都可以时间网络聊天室) 一.总结 1.ajax长轮询和websocket都可以时间网络聊天室 ...

  6. 基于EPOLL模型的局域网聊天室和Echo服务器

    一.EPOLL的优点 在Linux中,select/poll/epoll是I/O多路复用的三种方式,epoll是Linux系统上独有的高效率I/O多路复用方式,区别于select/poll.先说sel ...

  7. 基于 OpenResty 实现一个 WS 聊天室

    基于 OpenResty 实现一个 WS 聊天室 WebSocket WebSocket 协议分析 WebSocket 协议解决了浏览器和服务器之间的全双工通信问题.在WebSocket出现之前,浏览 ...

  8. Python3 网络通信 网络聊天室 文件传输

    Python3 网络通信 网络聊天室 文件传输 功能描述 该项目将实现一个文字和文件传输的客户端和服务器程序通信应用程序.它将传输和接收视频文件. 文本消息必须通过TCP与服务器通信,而客户端自己用U ...

  9. 基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍。最后我们将会实现一个基于Server-Sent Event和Flask简单的在线聊天室。

    基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍.最后我们将会实现一个基于S ...

随机推荐

  1. hdu3234 带权并查集(XOR)

    题意:       给你n个未知的正整数,有三总操作       I P V            P的值是V       I P Q V          P XOR Q = V       Q K ...

  2. 从苏宁电器到卡巴斯基第28篇:难忘的三年硕士时光 VI

    想要毕业,还需满足一个要求 像我们这种三年制的工科硕士,想要毕业的话,除了需要按照要求完成毕业论文以外,还需要在相关期刊上发表一篇与毕业论文相关的小论文,或者申请软件著作权,或者申请专利.我不知道别的 ...

  3. 从苏宁电器到卡巴斯基第15篇:我在苏宁电器当营业员 VII

    我们苹果的倒班制度 当年我在苏宁的时候,实行的是单休制度,而且只能选择在周一到周五其中的某一天,因为周六周日顾客比较多,是不允许休息的.尽管是单休,但并不表示我们在上班的时候每天都要完完整整地上八小时 ...

  4. POJ2239简单二分匹配

    题意:       一周有7天,每天可以上12节课,现在给你每科课的上课时间,问你一周最多可以上几科课,一科课只要上一节就行了. 思路:       简单题目,直接二分就行了,好久没写二分匹配了,练习 ...

  5. postgresql高级应用之合并单元格

    postgresql高级应用之合并单元格 转载请注明出处https://www.cnblogs.com/funnyzpc/p/14732172.html 1.写在前面✍ 继上一篇postgresql高 ...

  6. 一、Github+Pycharm基础

    GitHub为版本管理工具 常用的版本管理工具:本地化版本管理系统.集中式版本管理系统SVN.分布式版本管理系统 一.安装git(自行百度) 二.文件操作与分支管理基础 1.版本控制系统分类 集中化版 ...

  7. 并发容器-CopyOnWriteArrayList

    并发容器一览 图源:https://time.geekbang.org/column/article/90201?utm_term=pc_interstitial_938 CopyOnWriteArr ...

  8. opencv——几何变换原理与实现

    摘要 图像几何变换又称为图像空间变换, 它将一幅图像中的坐标位置映射到另一幅图像中的新坐标位置.几何变换不改变图像的像素值, 只是在图像平面上进行像素的重新安排. 几何变换大致分为仿射变换.投影变换. ...

  9. JS String总结

    String常用总结 1.字符 length属性:表示字符串包含多少16位码元 charAt():方法返回给定索引位置的字符 charCodeAt() :可以查看指定码元的字符编码 String.fr ...

  10. opencv打开摄像头获取视频程序

    // // main.cpp // opencv3 // // Created by PKU on 14-9-16. // Copyright (c) 2014年 PKU. All rights re ...