網絡編程: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