linux 下简单的模拟QQ 聊天过程 UDP 通讯

论坛 期权论坛 脚本     
匿名技术用户   2021-1-7 05:40   698   0


一直好奇QQ的通讯过程是怎么实现的,刚学了点linux下的socket编程,所以也想试着模仿下。QQ登陆起初我是想用tcp有连接的方式i,但是发现QQ登陆时并不是一直和服务器连接这的,要不然太耗费服务器资源,应该是服务器每隔多长时间发送一个心跳包,来检测用户是否在线。写的比较简陋,有时间继续完善。

服务器截图:

客户端1:格式 <ip> <port> <frm> <to>

客户端2:格式 <ip> <port> <frm> <to>

下面是实现的源代码

封装的消息包:

struct message 
{
 int from;
 int to;
 char time[30];
 char content[256];
};

struct people
{
 int id;
 bool is_online;
 char IP[32];
 int port;
 char name[10];
};

//get the current time
void get_time(char *buf)
{
 time_t timep;
 struct tm *p;
 time(&timep);
 p = gmtime(&timep);
 sprintf(buf, "%d : %d : %d", p->tm_hour, p->tm_min, p->tm_sec);
}

服务器的源代码:

#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>   //fork()
#include <pthread.h>
#include "message.h"
#include <algorithm>
#include <vector>
#include <queue>

using namespace std;



struct sockaddr_in s_addr, c_addr;
char* s_addr_ip = (char*)"0.0.0.0";
int s_port = 8888;
int sock;
int len;
socklen_t addr_len;

char buf[124];
struct message ms;
struct people peo;
vector<struct people> peo_set;
queue<struct message> ms_set;

pthread_t thread_id;

int find(vector<struct people> peo_set, int id)
{
// vector<struct people>::iteartor it = peo_set.begin();
 for(int i=0; i< peo_set.size(); ++i)
  if( peo_set[i].id == id )
   return i;
 return -1;
}

void send_ms()
{

   memset(buf,0, sizeof(buf));
   struct sockaddr_in rs;
   int socket;
   int str;
   int id;
   while( !ms_set.empty())
   {
    ms = ms_set.front();
//printf("from %id to %d time %s message %s", ms.from, ms.to, ms.time, ms.content);
    if( (id = find(peo_set, ms.to)) == -1 )
    {
     printf("the man is notonline");
    }
    else
    {

   printf("rsend: begin");
//printf("the message to %d ip %s %d", id, peo_set[id].IP, peo_set[id].port);
                
                    memset(&rs, 0, sizeof(rs));
                 rs.sin_family = AF_INET;
     rs.sin_addr.s_addr = inet_addr(peo_set[id].IP);
     rs.sin_port = htons(peo_set[id].port);
     len = sendto(sock, &ms, sizeof(ms), 0, (struct sockaddr*) &rs, addr_len);
     
     if(len < 0)
     {
      printf("rsent error");
     }
    }

    ms_set.pop();
   }
}
int main(int argc, char* argv[])
{
 int pid;

 if(argc == 2)
 {
  s_addr_ip = argv[1];
 }
 if(argc == 3)
 {
  s_addr_ip = argv[1];
  s_port = atoi(argv[2]);
 }
 sock = socket(AF_INET, SOCK_DGRAM, 0);
 if(sock == -1)
 {
  perror("socket");
  return -1;
 }

 memset(buf, 0, sizeof(buf));
 memset(&s_addr, 0, sizeof(s_addr));

 s_addr.sin_family = AF_INET;
 s_addr.sin_addr.s_addr =  inet_addr(s_addr_ip);
 s_addr.sin_port = htons(s_port);

 if(bind(sock, (struct sockaddr*)&s_addr, sizeof(s_addr)) == -1)
 {
  perror("bind");
  return -2;
 }
 addr_len = sizeof(c_addr);

 len = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr*) &c_addr, &addr_len);
 printf("the new id %s", buf);
 peo.id = atoi(buf);
 strcpy(peo.IP, inet_ntoa(c_addr.sin_addr));
 peo.port = ntohs(c_addr.sin_port);
 peo.is_online = true;
 peo_set.push_back(peo);


 len = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr*) &c_addr, &addr_len);
 printf("the new id %s", buf);
 peo.id = atoi(buf);
 strcpy(peo.IP, inet_ntoa(c_addr.sin_addr));
 peo.port = ntohs(c_addr.sin_port);
 peo.is_online = true;
 peo_set.push_back(peo);


// pthread_create(&thread_id, NULL, send_ms, NULL);
  while(1)
  {

  
   len = recvfrom(sock, &ms, sizeof(ms), 0, (struct sockaddr*) &c_addr, &addr_len);
   if(len < 0)
   {
    perror("recvfrom");
   }
   buf[len] = '\0';
   if(len == 0 )
   {
    printf("the other one close quit\n");
   }
   else
   {
    printf("recvim come form %s:%d \n", inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port));
    printf("from %d to %d time %s message %s", ms.from, ms.to, ms.time, ms.content);
   }

   ms_set.push(ms);
   send_ms();
  }
 return 0;
}

客户端:

#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <resolv.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "message.h"
using namespace std;

int main(int argc, char* argv[])
{
 struct sockaddr_in s_addr;
 socklen_t socklen;
 int sock;
 int len; 
 char buf[125];
 char* ser_addr = (char*)"127.0.0.1";
 int ser_port = 8888;
 

 pid_t pid;
 struct message ms;
 struct people peo;
 int from, to;

 if(argc == 2)
 {
  ser_addr = argv[1];
 }
 if(argc == 3)
 {
  ser_addr = argv[1];
  ser_port = atoi(argv[2]);
 }
 if(argc == 4)
 {
  
  ser_addr = argv[1];
  ser_port = atoi(argv[2]);
  from = atoi(argv[3]);
 }
 if(argc == 5)
 {
  
  ser_addr = argv[1];
  ser_port = atoi(argv[2]);
  from = atoi(argv[3]);
  to = atoi(argv[4]);
  ms.from = from;
  ms.to = to;
 }
 if( (sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
 {
  perror("-1");
//  exit(errno);
  return -1;
 }
 else
 {
  printf("create socket\n");
 }
 
 s_addr.sin_family = AF_INET;
 s_addr.sin_addr.s_addr = inet_addr(ser_addr);
 s_addr.sin_port = htons(ser_port);
 
 memset(buf, 0, sizeof(buf));
 printf("input you id");
 fgets(buf, sizeof(buf), stdin);
 len = sendto(sock, &buf, strlen(buf), 0,  (struct sockaddr*) &s_addr, sizeof(struct sockaddr_in));
 if(len < 0 )
 {
  printf("you id is not know");
  return -1;
 }


 if( -1 == (pid = fork()))
 {
  perror("fork");
  exit(EXIT_FAILURE);
 }
 else if( pid > 0)
 {
  while(1)
  {
   printf("pls send message to send:\n");
   memset(buf, 0, sizeof(buf)); 
   fgets(buf, sizeof(buf), stdin);
   strcpy(ms.content, buf);
   get_time(ms.time);
   if(!strncasecmp(buf, "quit", 4))
   {
    printf("i will quit\n");
    break;
   }
   len = sendto(sock, &ms, sizeof(ms), 0,  (struct sockaddr*) &s_addr, sizeof(struct sockaddr_in));
   if(len < 0)
   {
    printf("rsend error");
    return 3;
   }
  }
 }
 else if(pid == 0)
 {
  while(1)
  {
   
   len = recvfrom(sock, &ms, sizeof(ms), 0, (struct sockaddr*) &s_addr, &socklen);
   buf[len] = '\0';
   printf("receive from service %s:%d message:\n", inet_ntoa(s_addr.sin_addr), ntohs(s_addr.sin_port));
   printf("from %d to %d \n time %s message %s\n", ms.from, ms.to, ms.time, ms.content);
  }
 }

 return 0;
}

用到的函数

1. Inet_addr()

inet_addr()的功能是将一个点分十进制的IP转换成一个长整数型数(u_long类型)

原型in_addr_t inet_addr(const char *cp);

参数:字符串,一个点分十进制的IP地址

返回值

如果正确执行将返回一个无符号长整数型数。如果传入的字符串不是一个合法的IP地址,将返回INADDR_NONE。

头文件:Winsock2.h.

arpa/inet.h(Linux)


2. Inet_ntoa()

功能:

将一个IP转换成一个互联网标准点分格式的字符串。

原型:

char FAR * inet_ntoa( struct in_addr in);

头文件:

arpa/inet.h

Winsock2.h

参数:

一个网络上的IP地址

返回值:

如果正确,返回一个字符指针,指向一块存储着点分格式IP地址的静态缓冲区(同一线程内共享此内存);错误,返回NULL。


3 htons()

将主机的无符号短整形数转换成网络字节顺序

#include <winsock.h>

u_short PASCAL FAR htons( u_short hostshort);

hostshort:主机字节顺序表达的16位数。

注释:

本函数将一个16位数从主机字节顺序转换成网络字节顺序。

返回值:

htons()返回一个网络字节顺序的值。


4 ntohs()

将一个无符号短整形数从网络字节顺序转换为主机字节顺序。

#include <netinet/in.h>

uint16_t ntohs(uint16_t netshort);

netshort:一个以网络字节顺序表达的16位数。


5 recvfrom函数(经socket接收数据):

函数原型:ssize_t recvfrom(int sockfd,void *buf,intlen,unsigned int flags, struct sockaddr *from,socket_t *fromlen);

ssize_t 相当于 int,socket_t 相当于int,这里用这个名字为的是提高代码的自说明性。

6 int sendto(int sockfd, const void*msg,int len unsigned int flags, const structsockaddr *to, int tolen);
该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。

Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。


分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP