网络编程:Linux平台下聊天室程序的实现
1.直接跳转到Linux端代码
一、实验目的
- 学习第18章“多线程服务器端的实现”,掌握线程创建、线程同步的原理和实现方法。
- 在Linux操作系统上编写基于多线程的聊天室程序。
二、实验内容
1、在Linux操作系统上编写多线程并发服务器端:
(1)参考第18章相应程序,实现基于多线程的聊天室程序。
(2)为每一个接入的客户端创建线程并适当运用线程同步技术,让多个客户端之间可以交换信息。
(3)启动服务器后创建两个以上客户端并建立连接,验证客户端发送的消息是否可以被所有客户端收到。
Linux端效果图如下:
Linux端的(采用UOS+VScode+g++):
Linux端代码如下:
1. 服务器端:
#include<bits/stdc++.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#define listennum 20 //最大监听队列
int sock,i;
int fds[100];//客户端的socketfd,100个元素,fds[0]~fds[99]
static int maxi=0;//maxi表示当前client数组中最大的用户的i值
static int client[10];//最大的在线用户数量
void* recvandsend(void* p){
int fd = *(int*)p;
cout<<"客户端:"<<fd<<" 进入聊天室";
while(1){
char buf[100] = {};
if (recv(fd,buf,sizeof(buf),0) <= 0){
int i;
for (i = 0;i < 10;i++){
if (fd == fds[i]){
fds[i] = 0;
break;
}
}
cout<<fd<<" 已退出!"<<endl;
pthread_exit(NULL);
}
//把服务器接受到的信息发给所有的客户端
for (int m = 0;m < 10;m++){
if (fds[m] != 0){
cout<<buf<<endl;
send(fds[m],buf,strlen(buf),0);
}
}
}
}
int main()
{
struct sockaddr_in server_addr;
int portnumber;
int thr_id;
bzero(client,sizeof(client));//置字节字符串所有字节为零且包括'\0'
//创建套接字
if((sock=socket(AF_INET,SOCK_STREAM,0))==-1)
{
cout<<"套接字连接出错!"<<strerror(errno)<<endl;
exit(1);
}
//设置套接字相关属性
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(1234);
/* 捆绑 sock 描述符 */
if(bind(sock,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
{
cout<<"捆绑出错!"<<strerror(errno)<<endl;
exit(1);
}
/* 监听 sock 描述符 */
if(listen(sock,listennum)==-1)
{
cout<<"监听出错!"<<strerror(errno)<<endl;
exit(1);
}
cout<<"欢迎来到戈小戈聊天室^-^"<<endl;
while(1){
struct sockaddr_in fromaddr;
socklen_t len = sizeof(fromaddr);
int fd = accept(sock,(struct sockaddr *)(&fromaddr),&len);
if (fd == -1){
cout<<"客户端连接出错...\n";
continue;
}
for (int i = 0;i < 10;i++){
if (fds[i] == 0){
//记录客户端的socket
fds[i] = fd;
//有客户端连接之后,启动线程给此客户服务
pthread_t tid;
pthread_create(&tid,0,recvandsend,&fd);
break;
}
if (i==10){
//发送给客户端说聊天室满了
string str = "聊天室已满!";
send(fd,&str,sizeof(str),0);
close(fd);
}
}
}
}
//g++ 网络编程作业7服务器端.cpp -o test2 -lpthread
2. 客户端:
#include<bits/stdc++.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include<pthread.h>
#include<unistd.h>
using namespace std;
static int sock;
void *recvfromserver(void *) //接受服务器消息线程入口函数
{
char mes[1024];
int nbytes=0;
while(1)
{
bzero(mes,sizeof(mes));//置字节字符串所有字节为零且包括'\0'
nbytes=read( sock,mes,sizeof(mes));
if(nbytes>0)
{
mes[nbytes]='\0';
cout<<mes<<endl;
}
}
pthread_exit(NULL);
}
int main()
{
char buffer[1024];
struct sockaddr_in sock_addr;
char clientname[1024];//客户端昵称
char mes[1024];
pthread_t p_thread;
//设置套接字相关属性
bzero(& sock_addr,sizeof( sock_addr));//置字节字符串所有字节为零且包括'\0'
sock_addr.sin_family=AF_INET;
sock_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
sock_addr.sin_port = htons(1234);
//创建套接字
sock = socket(AF_INET,SOCK_STREAM,0);
if(connect( sock,(struct sockaddr *)(& sock_addr),sizeof(struct sockaddr))==-1)
{
cout<<"连接失败!"<<strerror(errno)<<endl;
exit(1);
}
/* 连接成功*/
cout<<"欢迎来到聊天室"<<endl<<"请输入用户昵称"<<endl;
fgets(clientname, sizeof( clientname), stdin);
cout<<endl<<"开始聊天吧(输入Q/q退出聊天室)"<<endl;
int thr_id =pthread_create(&p_thread, NULL, recvfromserver, NULL);//创建线程
while(1)
{
bzero(buffer,sizeof( buffer));//置字节字符串所有字节为零且包括'\0'
bzero(mes,sizeof( mes));//置字节字符串所有字节为零且包括'\0'
fgets(buffer, sizeof( buffer), stdin);
if(!strcmp(buffer, "q\n") || !strcmp(buffer, "Q\n"))
break;
strcat(mes,clientname);
strcat(mes,buffer);
if((write( sock,mes,strlen(mes)))==-1)
{
cout<<"传输错误!"<<strerror(errno)<<endl;
exit(1);
}
}
/* 结束 */
close(sock);
exit(0);
}
//g++ 网络编程作业7客户端.cpp -o test1 -lpthread