像qq這類的軟體怎樣實現通信的呢?

請問qq怎樣實現的通訊呢?大家有沒有誰用c語言寫一個這樣的源代碼呢?如果想了解這些知識的話應該在網上搜索什麼關鍵字或者看哪本書呢?謝謝大家


有意思的問題,讓我想起了十幾年前的日子

最簡單的,你需要了解網路編程,知道怎樣從網路收發數據,然後設計一個簡單的通信協議,定義聊天的內容怎樣在網路上傳遞,這樣就可以開始。如果複雜一些,高性能的網路編程一本書都講不完,協議工程可以開一門課,加密又是另外一門課,存儲離線的消息要用資料庫,等等。如果對性能的要求超出單台電腦的能力,整個系統的複雜性更是幾何級數上升,考驗你的架構能力。

推薦一個知乎用戶, @謝澤帆 ,可以看看他回答過的問題。

推薦使用irc協議,協議很簡單,所以適合練手,協議很老,所以有很多C語言實現可以參考。當然,用xmpp等更好些,但你需要更多的基礎知識,比如xml解析。

本科時正好有次作業是這個,貼上來博大家一笑吧,這是我寫的第一個超過百行的c程序。重新打開當年寫的文檔和代碼,感覺自己當年好可愛,有這麼多匪夷所思的想法。

本聊天程序採用Windows Sockets,使用TCP協議

每個客戶端與伺服器建立兩個TCP連接,其中第一個傳送上行數據,下行數據由第二個連接傳送。

信息格式

無論何種類型的包,長度均為256位元組

低端在左,包的結構如下圖所示

a

b

c

d

e

str1

str2

0

1

2

3

4-7

8-31

32-255

  • a:第0位元組,0表示上行,1表示下行。

  • b:第1位元組,表示包的類型。

  • c,d:第2,3位元組,保留不用。

  • e:第4-7位元組,可保存一32位數值,不用。

  • str1:8-31位元組,可表示一24-1個ASCII碼的字元串

  • str2:32-255位元組,可表示一224-1個ASCII碼構成的字元串。

    每次收發256位元組,如果不是256,認為出錯

協議數據單元格式:

四個位置分別表示上圖中的a,b,str1,str2,——表示忽略

用戶管理:

  • 註冊用戶: 0,0,用戶名,密碼

    • 確認: 1,0,——,——

    • 拒絕: 1,1,——,原因

  • 設定個人信息: 0,1,——,個人信息

  • 用戶登錄: 0,120,用戶名,密碼

    • 確認: 1,120,——,——

    • 拒絕: 1,121,——,原因

  • 註銷: 0,121,——,——

    • 確認: 1,122,——,——

  • 更改密碼 0,122,新密碼,——

    • 確認 1,123,——,——

      組管理

  • 加入組: 0,10,組名,密碼

    • 確認: 1,10,——,——

    • 拒絕: 1,11,——,原因

  • 創建組: 0,11,組名,密碼

    • 確認: 1,12,——,——

    • 拒絕: 1,13,——,原因

  • 離開組: 0,12,組名,——

    • 確認 1,14,——,確認信息

      信息查詢

  • 查詢用戶信息: 0,21,用戶名,——

    • 返回: 1,22,用戶名,信息

  • 列出所有組: 0,15,——,——

  • 列出特定組成員: 0,16,組名,——

  • 列出在線用戶: 0,20,——,——

    • 返回標題: 1,21,——,標題

    • 返回項: 1,20,項目,——

      聊天

  • 發信息: 0,30,目標,信息

    • 確認: 1,30,——,——

    • 否認: 1,32,——,——

  • 信息: 1,31,來源,信息

程序的工作流程

伺服器端

伺服器端在主線程等待客戶端的連接,依次調用socket,bind,listen函數,在指定埠(1982)等待連接,然後進入循環:接受第一個連接,接受第二個連接,啟動聊天處理線程,然後進入下一次循環,直到程序需要退出。

每個聊天處理線程處理和對應的一個客戶端的連接。它首先從主線程獲得與客戶端的兩個連接的socket句柄,然後等待客戶端的註冊和登錄請求,直到登錄成功,然後將客戶的名稱寫入在線用戶列表
,之後處理用戶的各種請求 ,直到用戶退出。中間連接如果發生問題,該線程會關掉與該客戶的連接,將名字從在線用戶列表清除,然後退出。

一個控制進程處理控制台輸入。

客戶端

客戶端的主進程首先創建與伺服器的連接,然後開始處理客戶的登錄和註冊,直到登錄成功,這時啟動接收處理線程。此時,主線程只負責接收控制台的命令並封裝,送至伺服器;接受處理線程接受從伺服器發來的各種信息並顯示。

伺服器代碼

#include &
#include &
#include &
#include &
#include &
#include &
#include &
#define DEFAULT_PORT 1982
#define MAX_USERS 200
#define MAX_ONLINE 100
#define MAX_MENBERS_PER_GROUP 20
#define MAX_GROUPS 20
#define ACC_REC "accounts.bin"
#define DEBUGINFO 0
char Buffer[256];
int havemsg,wantquit,c0,c1;
char tosend[256];
WSADATA wsaData;
SOCKET tempsock,tempsock1,listen_socket;
CRITICAL_SECTION sendallow;
struct user{
char name[24];
char passwd[24];
char intro[224];
} ;
user users[MAX_USERS];
struct group{
char groupname[24];
char passwd[24];
char member[MAX_MENBERS_PER_GROUP][24];
} ;
group groups[MAX_GROUPS];
struct {
char name[24];
SOCKET recvsocket;
SOCKET sendsocket;
} online[MAX_ONLINE];
void dump(void){
_flushall();
FILE *dumpfile;
dumpfile=fopen(ACC_REC,"w");
fwrite(users,sizeof(user),MAX_USERS,dumpfile);
fwrite(groups,sizeof(group),MAX_GROUPS,dumpfile);
fclose(dumpfile);
_flushall();
};
int resume(void){ //load file system from disk
FILE *dumpfile;
_flushall();
dumpfile=fopen(ACC_REC,"r");
if(dumpfile==NULL)
return -1;
else{
fread(users,sizeof(user),MAX_USERS,dumpfile);
fread(groups,sizeof(group),MAX_GROUPS,dumpfile);
fclose(dumpfile);
return 0;
};
_flushall();
};
int user_to_sock(char user_name[24]){
int c;
char tomatch[24];
strcpy(tomatch,user_name);
for(c=0;c&&>0){
bufs[1]=(char)20;
strcpy((bufs+8),online[c].name);
send(bbb,bufs,256,0);
};
};
}

else if((buf[0]==0)(buf[1]==15)){
bufs[1]=(char)21;
strcpy((bufs+32),"groups list:");
send(bbb,bufs,256,0);
for(c=0;c&&>0){
bufs[1]=(char)20;
strcpy((bufs+8),groups[c].groupname);
send(bbb,bufs,256,0);
};
};
}

else if((buf[0]==0)(buf[1]==16)){
bufs[1]=(char)21;
feedback=findgroupnum(buf+8);
if (feedback==-1){
strcpy((bufs+32),"no such group");
send(bbb,bufs,256,0);
}
else {
strcpy((bufs+32),"users of this group:");
send(bbb,bufs,256,0);
bufs[1]=20;
for(c=0;c&&>0){
strcpy((bufs+8),groups[feedback].member[c]);
send(bbb,bufs,256,0);
};
};
};
}
else if((buf[0]==0)(buf[1]==11)){
feedback=addgroup((buf+8),(buf+32));
if (feedback==-1){
strcpy((bufs+32),"rejected");
bufs[1]=13;
}
else {
printf("new group: %s
",(buf+8));
addtogroup(feedback,name,(buf+32));
bufs[1]=12;
}
send(bbb,bufs,256,0);
}

else if ((buf[0]==0)(buf[1]==10)){
feedback=addtogroup(findgroupnum(buf+8),name,(buf+32));
if (feedback==-1){
strcpy((bufs+32),"you have already in this group");
bufs[1]=11;
}
else if (feedback==-2){
strcpy((bufs+32),"wrong password to this group");
bufs[1]=11;
}
else {
bufs[1]=10;
};
send(bbb,bufs,256,0);
}

else if ((buf[0]==0)(buf[1]==12)){
feedback=leavegroup(findgroupnum(buf+8),name);
if (feedback==0)
strcpy((bufs+32),"leave group successfully");
else
strcpy((bufs+32),"you are not in this group");
bufs[1]=14;
send(bbb,bufs,256,0);
}
else if((buf[0]==0)(buf[1]==21)){
feedback=getusernum(buf+8);
if (feedback&>=0){
strcpy((bufs+8),(buf+8));
strcpy((bufs+32),users[feedback].intro);
}
else {
strcpy((bufs+8),"system info");
strcpy((bufs+32),"no such user");
}
bufs[1]=22;
send(bbb,bufs,256,0);
}
else if((buf[0]==0)(buf[1]==30)){ //loop1
feedback=findgroupnum(buf+8);
if (feedback==-1){//feedback=-1
feedback=user_to_sock(buf+8);
if (feedback==-1){
bufs[1]=32;
}
else { //loop0
bufs[1]=31;
strcpy((bufs+32),(buf+32));
strcpy((bufs+8),name);
send(online[feedback].sendsocket,bufs,256,0);
bufs[1]=30;
}; //loop0

send(bbb,bufs,256,0);
}//feedback=-1
else {
for(c=0;c&

客戶端代碼

#include &
#include &
#include &
#include &
#include &
#include &
#define DEFAULT_PORT 1982
SOCKET in,out;
int loggedon=0;
char lastmsg[256],lastgroup[256];
CRITICAL_SECTION wrcon;
void r(void){
char buf[256];
int feedback;
do{
feedback=recv(in,buf,256,0);
if (!(feedback==256)){
Sleep(100);
printf("connection lost
");
exit(0);
};
EnterCriticalSection(wrcon);
if(buf[1]==31){
printf("received from %s:%s
",(buf+8),(buf+32));
}
else if (buf[1]==20){
printf("%s
",(buf+8));
}
else if (buf[1]==21){
printf("%s
",(buf+32));
}
else if (buf[1]==22){
printf("info of %s
%s
",(buf+8),(buf+32));
}
else if (buf[1]==123){
printf("password changed successfully
");
}
else if (buf[1]==10){
printf("join to gruop %s successfully
",(lastgroup+8));
}
else if (buf[1]==11){
printf("join to gruop %s rejected :%s
",(lastgroup+8),(buf+32));
}
else if (buf[1]==12){
printf("create gruop %s successfully
",(lastgroup+8));
}
else if (buf[1]==13){
printf("create gruop %s rejected
",(lastgroup+8));
}
else if (buf[1]==14){
printf("leave gruop %s successfully
",(lastgroup+8));
}
else if (buf[1]==30){
printf("you said to %s : %s
",(lastmsg+8),(lastmsg+32));
}
else if (buf[1]==32){
printf("talk to %s failed
",(lastmsg+8));
}
else if (buf[1]==122){
}

else printf("other info %d
",buf[1]);
LeaveCriticalSection(wrcon);

} while (1);
};
int main(int argc,char *argv[]){
WSADATA wsaData;
char optionchar,optionstr[24],address[20];
char Buffer[256];
int err=WSAStartup(0x202,wsaData);
if(err==SOCKET_ERROR){
cerr&<&<"WSAStartup failed with error "&<&


嗯……大佬們都給出了挺有深度的回答。這裡我就斗膽假設題主(或者某個搜到這個問題的人)和我當時一樣,暫時不知道這麼深,只是學了c語言,好奇如何通信。

關於這個問題,我也有過同樣的疑問。這裡是我查找的時候整理出來的專欄。

簡單的聊天室實現(上):通信-SOCKET

暫時先把我的專欄貼過來,字之後再碼。


根據mpq之前公開的資料來看,pcqq的協議是騰訊自己搞的UDP協議,每個包有一個協議頭,一個類似於CHAP的密鑰,和以xml為主體的消息內容構成,消息內容與微信xml差不多。具體資料(數據包標頭含義)記不清了需要回去看看代碼……

ps.這裡有一個較舊版本pcqq的協議解析:

http://aae.me/index.php?c=threadfid=3


推薦閱讀:

如何看待《使命召喚14》到現在都沒有任何鎖區的現況?
為什麼用搜索引擎搜過的東西,在上別的網站時會顯示類似的廣告?這是互聯網的陰謀嗎?
華為EchoLife HG8120C接無線路由器 如何破解?
如何看待Impact Team於8月21日凌晨公布了從某婚外情網站竊取的數據?
非程序員有哪些電腦技能讓程序員感到神奇?

TAG:軟體 | 計算機網路 |