linux 多线程客户端服务端通信,[转载]多线程实现服务器和客户端、客户端和客户端通信;需要代码,留言...
一、實驗名稱
動手打造自己的?IM
二、實驗目的
1本次實驗旨在鍛煉大家的Socket編程能力,以日常生活中廣泛使用的IM軟件為背景,培養(yǎng)大家對于網(wǎng)絡編程的興趣。
2、通過本次實驗,培養(yǎng)linux環(huán)境下網(wǎng)絡編程能力,使得我們對網(wǎng)絡應用層的網(wǎng)絡應用軟件有了一個深入的理解,這對網(wǎng)絡課程的學習起到了很好的實踐作用。
三、實驗內(nèi)容及步驟
實驗內(nèi)容:本作業(yè)要求同學們完成一個簡陋的IM的客戶端和服務器端,實現(xiàn)一些最基本和最常用的IM功能:
客戶端功能:
1、4個以!開頭命令,!login登錄命令!list列出在線用戶命令!file傳輸文件命令!logoff注銷命令。
2、回顯功能:當其他用戶登錄或者登出時,客戶端會收到服務器的提示信息,當向另一個客戶端發(fā)送文件之后,也會收到該客戶端的提示信息。
3、聊天功能:兩個客戶端可以進行聊天:
以[username]:?開頭的行為其他用戶向你發(fā)送的聊天信息。
以>>[username]:?開頭的行為你向其他用戶發(fā)送的聊天信息。
服務器功能:
Server端基本架構:多進程并發(fā)服務器實現(xiàn),為每個在線用戶分配一個進程用以和在線用戶保持通信,并在子進程退出(某用戶下線)時,回收子進程資源。
ü服務器端固定端口:8081
ü用戶登錄成功,反饋提示信息
ü維護在線用戶信息:包括IP地址和端口號,用戶名字,用戶密碼等
ü向在線用戶發(fā)出其他用戶上/下線提示信息
實驗步驟:
1、熟悉Linux下C語言的編程環(huán)境和編譯方法,學會用gedit來編寫并修改代碼及命令行工具。
2、學習有關socket方面的有關知識,嘗試著去編寫服務器端和客戶端的實現(xiàn)連接的代碼,最重要的是學會使用socket()、listen()、bind()、connect()、accept()等函數(shù)的使用方法,在此基礎上實現(xiàn)客戶端和服務器端的連接。
3、在實現(xiàn)客戶端和服務器端連接的基礎上編寫處理!login、!logoff、!list等簡單的命令(無需客戶端與客戶端交互),這個過程最重要的是學會send()、recv()函數(shù)的使用,傳遞套接字時要注意接收和發(fā)送的對應。
4、在實現(xiàn)了單個客戶端與服務器的用簡單命令交互的基礎上,學習線程的有關知識在服務器端創(chuàng)建多線程,實現(xiàn)服務器可以和多個客戶端交互的功能。此過程最重要的是學會線程創(chuàng)建和使用,最重要的是pthread_create(4個參數(shù))的使用,其中關鍵的地方是線程函數(shù)的建立及其參數(shù)的傳遞,參數(shù)是socket,這樣才能在線程中實現(xiàn)與客戶端的交互。
5、在實現(xiàn)了多個客戶端與服務器進行交互的基礎上,就有可能實現(xiàn)客戶端與客戶端的交互,編程時一個很關鍵的地方就是所使用的數(shù)據(jù)結構,沒有一個存儲logined客戶端的數(shù)據(jù)結構,就很難實現(xiàn)客戶端與客戶端的交互。因此需要建立一個結構體來存儲客戶端的socket、port(可選)、username、flag(狀態(tài))。
兩種方案:
1)從服務器獲得另一個客戶端的端口號和IP后,讓客戶端與客戶端建立連接,實現(xiàn)直接交互,這就是P2P結構,編程時我試著使用了這種方法,但是連接總是出問題。
2)通過服務器實現(xiàn)客戶端之間的交互,簡單來說就是客戶端把信息通過socket發(fā)送到服務器,服務器再把信息通過不同的socket發(fā)送到客戶端,這是建立在每個客戶端都有自己的socket的基礎上的。
我選擇了第二種方案來實現(xiàn)客戶端的交互。
6、編程進行到這里,我突然意識到了如果客戶端沒有監(jiān)聽服務器的代碼,無法接收和識別服務器的信息是客戶端要發(fā)送的信息、還是服務其本身要發(fā)送的信息,思考良久,無奈之下只能大篇幅的修改代碼,在客戶端使用雙線程,一個用于監(jiān)聽鍵盤的輸入,一個用于監(jiān)聽服務器是否發(fā)送信息;這樣當鍵盤輸入時一個線程接收并負責向服務器發(fā)送信息,當收到服務器發(fā)送的信息時,另一個線程接收并把信息輸出到終端上。
7、在以上的基礎上,就可以實現(xiàn)客戶端的通話及文件的發(fā)送,最后就是調(diào)試程序并整理輸出的格式。
四、實驗分析
1,server.c編譯及運行結果:
圖1處于監(jiān)聽狀態(tài)的服務器
2,開啟另外終端運行客戶端程序:
第一個用戶:Me,運行客戶端程序之后,使用命令!login登錄,成功登錄后會收到服務器的發(fā)送的信息,結果如下:
圖2?一個client登陸成功后的服務器
圖3第一個客戶端Me登陸成功
另起兩個終端第二個客戶端You和第三個客戶端Him登錄后結果如下:
圖4多個客戶端登陸后的服務器
3,指令!list的使用:
圖5?Me客戶端上執(zhí)行!list命令
4,!logoff命令:當用戶Me執(zhí)行!logoff后,客戶端顯示服務器發(fā)送的信息,此時使用!list指令,結果如下:
圖6客戶端Him執(zhí)行!logoff命令
圖7客戶端Him執(zhí)行!logoff命令后Me執(zhí)行!list的結果
5,聊天功能演示:
圖8?Me客戶端和You客戶端進行通話
圖9You客戶端和Me客戶端通話
6,指令!file的使用,文件傳輸功能演示:
圖10You客戶端向Me發(fā)送lilufeng.pdf文件顯示成功
圖11?Me客戶端接收文件成功信息
7、幾種錯誤處理,結果如下:
圖12用戶名錯誤登錄失敗
圖13發(fā)送文件時用戶不在線、發(fā)送失敗
圖14Me想和You對話,You沒有登錄,連接失敗
五、實驗結論
難點:
1、本實驗是基于Linux環(huán)境下,使用C語言編寫而成,這兩者都需要實驗前進行深入的探索和學習,對我而言,加大了編程和實現(xiàn)的難度。
2、對socket和thread方面的知識可以說一無所知,編程的過程中要不斷地學習才能向前邁一小步。
感受:
1、這次實驗的過程中,我深知自己的知識的匱乏,實驗的過程可以說完全是一個探索的過程,對用到的函數(shù)我必須要在Dev等工具下先進性一定的應用和學習才能真正掌握其用法和內(nèi)涵,實踐的過程中學到了很多的知識,而且在編程的后面階段感覺對這些知識已經(jīng)能夠熟練地應用,最有說服力的就是那些函數(shù)的掌握,可見,軟件這個行業(yè)只有在實踐中才能真正的學到本領。
2、編程的前后,我的代碼被改了很多遍,因為開始編程的時候沒有一個整體的構思,只能走一步算一步,每走一步都是那么的艱難,線程的加入和數(shù)據(jù)結構的改變都使原來的代碼結構發(fā)生天翻地覆的變化,到最后在客戶端加入雙線程時整體的代碼又改了一遍,所以說這次試驗讓我感覺非常的累,而且過程中感覺網(wǎng)絡編程是那么的復雜,現(xiàn)在想想這些東西,還是很簡單的。由此,我明白了開發(fā)軟件前為什么要進行詳細的設計,看著是浪費時間,其實那是在節(jié)省時間和精力,而且是開發(fā)出一個軟件的基礎。
3、通過這次試驗讓我學會了關于網(wǎng)絡的socket和P2P等很多知識,這些知識在短期或者說今生難于忘記,當然還有操作系統(tǒng)方面的thread和process方面的知識。
4、同時我也感受到了IT人士的辛勞,其實我一直都在感受到,只不過這次更加強烈罷了,不過辛勞后,看到自己的成果和感到內(nèi)心的充實,覺得付出還是值得的。
客戶端程序源碼:
//[浠g爜]
tcpclient.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define
SERVER_PORT 8081 ?//
define the defualt connect port
id
#define
BUFFER_SIZE 255 //buf size
int
clifd,length = 0;//socket size
char
buf[BUFFER_SIZE]; //the buffer of socket
pthread_t
thread;//thread discription
char
command[100],uName[20],pwd[20];
void
*listenServer(int);//listen and receive message from the server
int
cli_socket_connect_server(int);//create socket and connect the
server
int
main(int argc)
{
clifd =
cli_socket_connect_server(SERVER_PORT);//return the socket
while(1)
{ char
frontcmd[20],ch1,ch2;
int countcmd
=0,countFcmd=0;
memset(frontcmd,' ',sizeof(frontcmd));
memset(command,' ',sizeof(command));
memset(buf,' ',sizeof(buf));
while(ch1 =
getchar()) //input the command
{
if(ch1 == ' ')
{
while(ch2 =
getchar())
{
if(ch2 == 'n')
break;
else
command[countcmd++]
= ch2;
}
break;
}
else if(ch1 ==
'n')
break;
else
frontcmd[countFcmd++] = ch1;
}//input
over:frontcmd is the really command
if(frontcmd[0] ==
'>')//talk function process
{
strcpy(buf,">>");
send(clifd,buf,BUFFER_SIZE,0);
char *talkto;
talkto =
strtok(frontcmd,">:");
strcpy(buf,talkto);//talk to whom
strcat(buf,":");//:
is used to strtok in the server
strcat(buf,command);//what you send
send(clifd,buf,BUFFER_SIZE,0);
}
if(strcmp(frontcmd,"!logoff") == 0)//logoff command process
{
strcpy(buf,"!logoff");
send(clifd,buf,BUFFER_SIZE, 0);
strcpy(buf,uName);
send(clifd,buf,BUFFER_SIZE, 0 );
close(clifd);
pthread_exit(NULL);
break;
}//end of logoff
if
if(strcmp(frontcmd,"!list") == 0)//list command process
{
strcpy(buf,"!list");
send(clifd,buf,BUFFER_SIZE, 0 );
}//end of list
if
if(strcmp(frontcmd,"!login") == 0)//login command process
{
strcpy(buf,"!login");
send(clifd,buf,BUFFER_SIZE, 0 );
printf("User
name:t");
scanf("%s",uName);
printf("Password:t");
scanf("%s",pwd);
strcpy(buf,uName);
send(clifd,buf,BUFFER_SIZE, 0 );
length =
recv(clifd,buf,BUFFER_SIZE,0);
if
(length < 0)
{
printf(" error comes
when recieve data from server! send filen");
exit( 1 );
}
printf("From
server:nt%sn",buf);
if(pthread_create(&thread, NULL, (void
*)listenServer, (void *)clifd) == 0) //after login success create
the listenServer thread
{
printf("listen
thread create success!n");
}
else
{
printf("listen
thread create failed!n");
exit(1);
}
}//end of login
if
if(strcmp(frontcmd,"!file") == 0)
{
strcpy(buf,frontcmd);//send front command(!file) to the server
send(clifd,buf,BUFFER_SIZE,0);
char
*file,*toUser;
file =
strtok(command," =>");//file
toUser =
strtok(NULL," =>");//destination user
printf("filename:t%sntoUser:t%s",file,toUser);
strcpy(buf,toUser);//send the file to whom
send(clifd,buf,BUFFER_SIZE,0);
}//end of file
if
}//end of while
return 0;
}
void
*listenServer(int file_socket)
{
while(recv(file_socket,buf,BUFFER_SIZE,0)>0)
{
char *flagcmd =
NULL,*printInfo = NULL;
flagcmd =
strtok(buf,"|");
printInfo =
strtok(NULL,"|");
if(strcmp(flagcmd,"!list") == 0)//list the user that logined
now
printf("from
server:n%s",printInfo);
else
if(strcmp(flagcmd,"!logoff") == 0)//logoff
printf("from
server:nt%s",printInfo);
else
if(strcmp(flagcmd,"!file") == 0)//send the file
{
if(strcmp(printInfo,"0") == 0)//destination user does not login
printf("From
server:ntuser does not login,file failed!n");
else
//destination user logined
printf("nConnect
success!file success!n");
}
else
if(strcmp(flagcmd,"!msg") == 0)//receive the file message
{
printf("From
sever:nt%s",printInfo);
}
else
if(strcmp(flagcmd,"!talk") == 0)//talk message
{
if(strcmp(printInfo,"0") == 0)
printf("The user
does not login!n");
else
printf("%sn",printInfo);
}
else
printf("Please
debug!");
}
}
int
cli_socket_connect_server(int serverPort)
{
struct
sockaddr_in cliaddr;
socklen_t socklen
= ?sizeof(cliaddr);
char
sever[20];//store the IP
int
client_socket;
if((client_socket
= ?socket(AF_INET,SOCK_STREAM, 0
)) ?< ?0
)//create socket of the client
{
printf(" create
socket error!n ");
exit( 1 );
}
bzero(
& cliaddr,sizeof(cliaddr));
cliaddr.sin_family
= ?AF_INET;
inet_aton(sever,
& cliaddr.sin_addr);
cliaddr.sin_port
= ?htons(serverPort);//port of
the sever
cliaddr.sin_addr.s_addr ?=
htons(INADDR_ANY);//the same IP with the
sever.Connect auto!
if(connect(client_socket,(struct ?sockaddr * )
& cliaddr, socklen)
< ?0 )//connect
the server
{
printf(" can't
connect!n ");
exit( 1 );
}
return
client_socket; //return the socket
}
服務器端源碼:
//tcpserver.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define
SERVER_PORT 8081 ?//
define the defualt connect port
id
#define
LENGTH_OF_LISTEN_QUEUE 10 ?//
length of listen queue in server
#define
MAX_THREAD 10
#define
BUFFER_SIZE 255
#define
SUCCESS_MESSAGE "Login
Success!"
#define
FAIL_MESSAGE "Login Failed!"
#define
TOTAL_USER_NUM 6
struct
clientInformation
{
int flag;
//record
the struct state: 0 is empty
char loginUsr[20];
//the users that have logined
int socket;
//store the logined uses'
socket
};
struct
clientInformation clientInfo[TOTAL_USER_NUM];//logined users'
array
int
servfd,sever_socket; ?//the socket
int len=0;
//record the length of the socket
int numOfThread = 0;
//record the number of
threads
char users[6][10] =
{"Me","You","Him","Her","user","tmd"};//users that can login
char
buf[BUFFER_SIZE]; ?//the capacity of the
socket
pthread_t
thread[MAX_THREAD]; //thread discription
struct
sockaddr_in servaddr,cliaddr;
void*
docommand(int);//thread function
int main(int
argc)
{
int num;
for(num = 0; num
< TOTAL_USER_NUM; num++)
{
clientInfo[num].flag
= 0;
clientInfo[num].socket = 0;
}
if ?((servfd ?=
socket(AF_INET,SOCK_STREAM, 0 ))
< ?0 )//create
socket in the server
{
printf(" create socket
error!n ");
exit( 1
);
}
bzero( &
servaddr,sizeof(servaddr));
servaddr.sin_family ?=
AF_INET;
servaddr.sin_port ?=
htons(SERVER_PORT);
servaddr.sin_addr.s_addr ?=
htons(INADDR_ANY);
if(bind(servfd,(struct ?sockaddr
* ) & servaddr,sizeof(servaddr)) < 0
)//bind
{
printf(" bind to
port %d failure!n ",SERVER_PORT);
exit( 1
);
}
if(listen(servfd,LENGTH_OF_LISTEN_QUEUE)
< ?0
)//listen
{
printf("
call listen failure!n ");
exit( 1
);
}
while(numOfThread
< MAX_THREAD)
{
socklen_t length
= ?sizeof(cliaddr);
sever_socket ?=
accept(servfd,(struct ?sockaddr
* ) & cliaddr, & length);//accept
the login
if
(sever_socket ?<
0 )
{
printf("
error comes when call accept!n ");
break;
}
else
{
//void* arg[] =
{&sever_socket};
if(pthread_create(&thread[numOfThread++], NULL,
(void *)docommand, (void *)sever_socket) != 0)
printf("綰跨▼%d鍒涘緩澶辮觸!n",numOfThread); //create
the new thread to the login client
else
printf("綰跨▼%d琚垱寤簄",numOfThread);
}
}
close(servfd);
return ?0
;
}
void *docommand(int
sever_socket)
{
while
( 1 )
{// server loop will nerver exit unless any body kill the process
memset(buf,' ',sizeof(buf));
len
= ?recv(sever_socket, buf,
BUFFER_SIZE, 0); ?//recv the
command
if
(len < 0)
{
printf(" error comes when
recieve data from client ! " );
exit( 1 );
}
else
{
if(strcmp(buf,">>") == 0) //talk
process
{
len
= ?recv(sever_socket, buf,
BUFFER_SIZE, 0);//recv the user name that the file will be sent
to
if
(len < 0)
{
printf("error comes when
recieve data from client ! " );
exit( 1 );
}
char
temp[100],*talkto;
strcpy(temp,buf);//temp store what will be sent
talkto =
strtok(buf,":");
int j;
for(j = 0;j
< TOTAL_USER_NUM;j++)
{
if(clientInfo[j].flag == 1)
{
if(strcmp(clientInfo[j].loginUsr,talkto) == 0)
{
strcpy(buf,"!talk|");
strcat(buf,temp);//move temp(the information) to buf
send(clientInfo[j].socket,buf,BUFFER_SIZE,0);//send the talking
message to whom what you talk
break;
}
}
}
if(j ==
TOTAL_USER_NUM)
{
strcpy(buf,"!talk");
strcat(buf,"|0");//"0" == user does not login
send(sever_socket,buf,BUFFER_SIZE, 0 ); //send message(user does
not login)
}
}
else
if(strcmp(buf,"!file") == 0) ?//file process
{
len
= ?recv(sever_socket, buf,
BUFFER_SIZE, 0);//recv the user name that the file will be sent
to
if
(len < 0)
{
printf("error comes when
recieve data from client ! " );
exit( 1 );
}
int i;
for(i = 0;i
< TOTAL_USER_NUM;i++)
{
if(clientInfo[i].flag == 1)
{
if(strcmp(clientInfo[i].loginUsr,buf) == 0)
{
strcpy(buf,"!file|");
strcat(buf,"1");//"1" == user logined
send(sever_socket,buf,BUFFER_SIZE, 0 ); //send to the client who
send file(receiver)
strcpy(buf,"!msg|nreceived the file!n");
send(clientInfo[i].socket,buf,BUFFER_SIZE,0);//tell the client to
recv the file
break;
}
}
}
if(i ==
TOTAL_USER_NUM)
{
strcpy(buf,"!file");
strcat(buf,"|0");//"0" == user does not login
send(sever_socket,buf,BUFFER_SIZE, 0 ); //send message(user does
not login)
}
}//end of if
else
if(strcmp(buf,"!list") == 0) ?//list the logined users process
{
strcat(buf,"|");
int k;
for(k = 0; k
< TOTAL_USER_NUM; k++)
{
if(clientInfo[k].flag == 1)
{
strcat(buf,clientInfo[k].loginUsr);
strcat(buf,"n");
}
}
send(sever_socket,buf,BUFFER_SIZE,0); //send all logined users to
client
}//end of else if
1
else
if(strcmp(buf,"!logoff") == 0) ?// logoff process
{
while(1)
{
strcat(buf,"|logoff
success!n");
send(sever_socket,buf,BUFFER_SIZE,0); //send logoff success
message
memset(buf,' ',sizeof(buf));
len
= ?recv(sever_socket, buf,
BUFFER_SIZE, 0); ?//recv the login users name
int k;
for(k = 0; k
< TOTAL_USER_NUM; k++)//move it out from the
clientinformation struct
{
if(strcmp(buf,clientInfo[k].loginUsr) == 0)
{
strcpy(clientInfo[k].loginUsr," ");
clientInfo[k].flag =
0;
clientInfo[k].socket
= 0;
break;
}
}
close(sever_socket);
numOfThread--;
pthread_exit(NULL);
break;
}
}//end of else if
2
else
if(strcmp(buf,"!login") == 0)//login process
{
len
= ?recv(sever_socket, buf,
BUFFER_SIZE, 0); //recv the login user's name
if
(len < 0)
{
printf("error comes when
recieve data from client ! login" );
exit( 1 );
}
printf("from
client:%stIP:%stPort:%dn ",buf,
inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
int i;
for(i = 0;i
< TOTAL_USER_NUM;i++)//judge whether have this
user
{
if(strcmp(buf,users[i]) == 0)
{
for(i = 0;i
< TOTAL_USER_NUM;i++)
{
if(clientInfo[i].flag == 0)
{
strcpy(clientInfo[i].loginUsr,buf);
clientInfo[i].socket
= sever_socket;
clientInfo[i].flag =
1;
break;
}
}
strcpy(buf,SUCCESS_MESSAGE);
send(sever_socket,buf,BUFFER_SIZE, 0 ); //send login success
message
break;
}
}
if(i ==
TOTAL_USER_NUM)
{
printf("No user
called %s! Login failed!n",buf);
memset(buf,' ',sizeof(buf));
strcpy(buf,FAIL_MESSAGE);
send(sever_socket,buf,BUFFER_SIZE, 0 ); //send login failed
message
}
}//end of else if
3
} //end of else
}// end of while
}
又不懂得地方,歡迎留言詢問,我必盡力解答!
總結
以上是生活随笔為你收集整理的linux 多线程客户端服务端通信,[转载]多线程实现服务器和客户端、客户端和客户端通信;需要代码,留言...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql群集配置_CentOS7 -
- 下一篇: python处理字典的方法_python