一直好奇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。 


Logo

更多推荐