引言

这是Linux选修课的课设作业,由于此时计网等课程还没有学到相应内容,该课划水导致自身知识储备不足,而课设提交又即将截止,所以对于网络编程的原理与相关函数仅做出大概了解后即动手编写,并未对性能和逻辑进行过多优化。所以,它“能用,但还存在一些不足”。在这里,简单记录下Client端和Server端代码(并未封装)。 涉及Qt图形界面和数据库部分的工作,是组内其他成员分工完成的,我并不了解。有需要者可自行参考,成品在GitHub仓库

客户端代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<netdb.h>
#include<signal.h>
#include<unistd.h>
#include<iostream>
#include<pthread.h>
#include<sys/types.h>
#include<arpa/inet.h>    
#include<sys/socket.h>
#include<netinet/in.h>
using namespace std;
char ip_address[20],mess[2222],msg_rec[2222],l_ip[20]="";
int soc_cli,soc_rec,con_rec,fin,l_len;
struct sockaddr_in  servaddr,cliaddr,localaddr;
pthread_t rcv;

void in_ip_defult()
{
  char ip_defult[]="47.94.1.16";
  stpcpy(ip_address,ip_defult);
  // the defult ip in Aliyun  
  return ;
}

void in_ip(char ip_input[])
{
  stpcpy(ip_address,ip_input);
  /* If you run the server program locally, illegal input other than the IP will still connect local successfully.
     but if run in the server,the illegal input will not successfully connect.
     ( illegal input such as:"hhh" )*/
  return ;
}

int con_ser()
{
  soc_cli=socket(AF_INET,SOCK_STREAM,0);
  // set socket | return socket description | error -1
  // AF_INET:ipv4  SOCK_STREAM:full duplex  0:automatic choose agreement of type(2)
  if(soc_cli==-1)
  {
    printf("--failed to create socket\n");
    return 0;
  }
  // init
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family=AF_INET;
  // choose ipv4 but not ipv6
  servaddr.sin_port=htons(6666);
  // defult ip  port
  if(inet_pton(AF_INET,ip_address,&servaddr.sin_addr)==-1)
  {
    printf("--failed to inet_tion\n");
    return 0;
  }
  // transform the ip address to binary
  if(connect(soc_cli,(struct sockaddr*)&servaddr,sizeof(servaddr))==-1)
  //        (description,server socket address,length of socket address)
  {
    printf("--failed to connection\n");
    return 0;
  }
  //printf("--connection success!\n");
  return 1;
}

int send_info()
{
	while(1)
	{
		fgets(mess,2222,stdin);
		// buffer | stop when '\n' what want to send 
		
		if(send(soc_cli,mess,strlen(mess),0)==-1)
		// (socket description,buffer,length,0)
		{
			printf("--failed to send this  message!\n");
			return 0;
		}
		if(mess[0]=='!'&&mess[1]=='q')
		{
			/*
			close(soc_cli);
			kill(getppid(),SIGUSR1);
			exit(EXIT_SUCCESS);
			return 0;
			*/
			break;
		}
	}
	close(soc_cli);
	kill(getppid(),SIGUSR1);
	exit(EXIT_SUCCESS);
//	善后工作,关闭套接字/对接父进程
	return 1;
}

void con_recv()
{
//	printf("----I'm listening the server program\n");
	while(1)
	{
		bzero(msg_rec,sizeof(msg_rec));
		// The string is zeroed out each time a message is received
		fin=read(soc_cli,msg_rec,sizeof(msg_rec));
		if(!fin)
		{
			printf("--The server program is close\n");
			break;
		}
		printf("--Msg from server:");
		fputs(msg_rec,stdout);
		printf("\n");
	}
}

int main()// 功能实现按数字标号,附注释
{
  /* 1.选择默认ip或者自定义ip,如果指向非本机的客户端,会检测ip输入是否合法*/
  in_ip_defult();
  // 默认ip,阿里云,后期调试/考核看情况去该函数里改
  /*
  scanf("%s",ip_address);
  std::cin.ignore();
  in_ip(ip_address);
  // QT选择自定义ip,传参需要 **字符数组** 形式,用指针会报错
  // 字符读入的时候需要ignore掉\n,否则会当成第一条消息发送
  */
  
  /* 2.与服务器的连接部分,返回1则连接成功,返回0则图形界面自行决定处理逻辑,这里选择退出不进行重连
  printf("--please wait to see if the connection is succeed.\n");
  printf("--you will get message in 2s if success\n");
  */
  if(!con_ser()) return 0;

  //sleep(1); 非必须,仅Shell测试时为获取成功信息时美观设置
  
  /* 3.父进程接收消息*/
  pid_t k=fork();
  if(k>0) con_recv();
  
  /* 4.子进程发送消息*/
  if(!k)
	  send_info();
  close(soc_cli);
  // 关闭套接字,否则服务端陷入接收消息的死循环
  return 0;
}

服务端代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<netdb.h>
#include<signal.h>
#include<unistd.h>
#include<iostream>
#include<pthread.h>
#include<sys/types.h>
#include<arpa/inet.h>    
#include<sys/socket.h>
#include<netinet/in.h>
using namespace std;

int soc_ser,lst,acpt,fin,k,soc_lst,tot=0;
struct sockaddr_in seraddr,cliaddr;
char msg_rec[2222],mess[2222];
socklen_t l_size;
int clients[10],flag=-1;

int init()
{
	memset(clients,-0x3f,sizeof(clients));
	soc_ser=socket(AF_INET, SOCK_STREAM, 0);
	if(soc_ser==-1)
	{
		printf("--failed creating socket description\n");
		return 0;
	}
	l_size=sizeof(struct sockaddr_in);
	memset(&seraddr,0,sizeof(seraddr));
	seraddr.sin_family=AF_INET;
	seraddr.sin_addr.s_addr=htonl(INADDR_ANY);
	seraddr.sin_port=htons(6666);
	if(bind(soc_ser,(struct sockaddr*)&seraddr,sizeof(seraddr))==-1)
	{
		printf("--failed bind\n");
		return 0;
	}
	lst=listen(soc_ser,10);
	if(lst==-1)
	{
		printf("--failed listen\n");
		return 0;
	}
	printf("--Listen success,now begin accept:\n");
	return 1;
}

void *ser_rcv(void *q)
{
	fin=0;
	k=*(int *)q;
	bzero(msg_rec,sizeof(msg_rec));
	while(read(k,msg_rec,sizeof(msg_rec))&&strlen(msg_rec))
	// 如果读入失败,字符数组清零,连'\0'都没有,踩坑好久
	{
		// The string is zeroed out each time a message is received
		if(msg_rec[0]=='!'&&msg_rec[1]=='q')
		{
			tot--;
			break;
		}
		else
		{
			if(msg_rec)
			printf("--messages from clients: ");
			fputs(msg_rec,stdout);
			printf("\n");
			for(int i=0;i<10;i++)
			{
				if(clients[i]!=-0x3f)
				{
					stpcpy(mess,msg_rec);
					send(clients[i], mess, strlen(mess), 0);
				}
			}
			bzero(msg_rec,sizeof(msg_rec));
		}
	}
	for(int i=0;i<10;i++)
	{
		if(clients[i]==k)
		{
			clients[i]=-0x3f;
			break;
		}
	}
    return 0;
}
int main()
{
	if(!init())
	  return 1;
	while(1)
	{
		acpt=accept(soc_ser,(struct sockaddr *)&cliaddr,&l_size);
		if(acpt!=-1)
		{
			tot++;
			if(tot>10)
			{
				tot--;
				break;
			}
			while(1)
			{
				++flag;
				if(flag>9) flag-=10;
				if(clients[flag]!=-0x3f)
				{
					clients[flag]=acpt;
					break;
				}
			}
			pthread_t k;
			pthread_create(&k,NULL,ser_rcv,&acpt);
		}
	}
	return 0;
}
本文作者:睿屿青衫
本文来源:https://www.reashal.com/docs/004
许可协议:CC BY-NC-SA 4.0
本站随笔均为原创,涉及隐私且时效性较强,不允许任何人以任何形式转载。
本站笔记多对原始作品作出修改,允许非商业用途转载,转载时请注明出处。