lv6 网络编程(8)-网络编程扩展上

目录

前言

1.网络信息检索函数

2.网络属性配置

3.网络超时

3.1 网络超时检测1

3.2 网络超时检测2

3.3 网络超时检测3

总结



前言


1.网络信息检索函数

gethostname() 获得主机名
getpeername() 获得与套接口相连的远程协议地址
getsockname() 获得本地套接口协议地址
gethostbyname() 根据主机名取得主机信息 -> endhostent()

gethostbyaddr() 根据主机地址取得主机信息
getprotobyname() 根据协议名取得主机协议信息
getprotobynumber() 根据协议号取得主机协议信息
getservbyname() 根据服务名取得相关服务信息
getservbyport() 根据端口号取得相关服务信息

代码演示域名解析:

#include "net.h"
int main(int argc, const char *argv[])
{

	int fd;
	struct sockaddr_in cin;
	//定义hostent结构体
	struct hostent *hs = NULL;
	//使用DNS域名解析
	if((hs = gethostbyname(SERV_IP_ADDR)) == NULL){
	perror("gethostbyname");
	exit(0);
	}
	//1.创建socket fd

	fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0){
		perror("socket");
		exit(0);
	}
	//2.connect函数
	bzero(&cin,sizeof(cin));
	cin.sin_family = AF_INET;
	cin.sin_port = htons(SERV_PORT);
#if 0
	cin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);//ipv4专用
#else
    //h_addr = h_addr_list[0]
	//cin.sin_addr.s_addr = *(uint32_t*)hs->h_addr;
    cin.sin_addr.s_addr = *(uint32_t*)*hs->h_addr_list;
	endhostent();//释放结构体
	hs = NULL;
#endif
	//连接
	if(	connect(fd,(struct sockaddr*)&cin,sizeof(cin)) < 0){
		perror("connect");
		exit(0);	
	}	

	//3.读写
	int ret;
	char buf[BUFSIZ];//512

	while(1){
		bzero(buf,sizeof(buf));
		if(fgets(buf,sizeof(buf)-1,stdin) == NULL){
		continue;
		}
		do{	
			ret = write(fd,buf,BUFSIZ-1);
		}while(ret < 0 && EINTR == errno);

		if(ret < 0){
			perror("read");
			exit(0);
		}

		if(!strncasecmp(buf,QUIT,strlen(QUIT))){
			printf("通话结束\n");
			break;
		}


	}

	close(fd);
	return 0;
}

2.网络属性配置

getsockoptsetsockopt

int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen)
int setsockopt(int
sockfd,int level,int optname,const void *optval,socklen_t *optlen)

前面的代码案例中,服务端允许地址快速重用时,使用了setsocket

level指定控制套接字的层次.可以取三种值:

1) SOL_SOCKET:通用套接字选项. (应用层)

2) IPPROTO_TCP:TCP选项. 传输层)

3) IPPROTO_IP:IP选项.(网络层)

optname指定控制的方式(选项的名称),我们下面详细解释
optval获得或者是设置套接字选项.根据选项名称的数据类型进行转换

3.网络超时

在网络通信中,很多操作会使得进程阻塞

TCP套接字中的recv/accept/connect

UDP套接字中的recvfrom

超时检测的必要性:
避免进程在没有数据时无限制地阻塞
当设定的时间到时,进程从原操作返回继续运行

3.1 网络超时检测1

设置socket的属性 SO_RCVTIMEO
参考代码如下:

    struct timeval  tv;

     tv.tv_sec = 5;   //  设置5秒时间
     tv.tv_usec = 0;

     setsockopt(sockfd, SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));//设置接收超时
     recv() / recvfrom()//从socket读取数据

3.2 网络超时检测2

用select检测socket是否’ready’
参考代码如下:

    struct fd_set rdfs;
    struct timeval  tv = {5 , 0};   // 设置5秒时间

    FD_ZERO(&rdfs);
    FD_SET(sockfd, &rdfs);

    if (select(sockfd+1, &rdfs, NULL, NULL, &tv) > 0)   // socket就绪
    {
          recv() /  recvfrom()    //  从socket读取数据
    }

3.3 网络超时检测3

设置定时器(timer), 捕捉SIGALRM信号
参考代码如下:

    void  handler(int signo)     {   return;  }

      struct sigaction  act;
      sigaction(SIGALRM, NULL, &act);
      act.sa_handler = handler;
      act.sa_flags &= ~SA_RESTART;
      sigaction(SIGALRM, &act, NULL);
      alarm(5);
      if (recv(,,,) < 0) ……

 代码案例:

1.server端


#include "net.h"



int main(int argc, const char* argv[])
{

	int fd;
	struct sockaddr_in sin;
	//signal(SIGCHLD,handler);
	//1.创建socket fd

	fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd < 0) {
		perror("socket");
		exit(0);
	}
	/*允许绑定地址快速重用 */
	int b_reuse = 10;
	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));

	/*允许阻塞 */
	struct timeval tout;
	tout.tv_sec = 10;
	tout.tv_usec = 0;
	setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&tout, sizeof(tout));//接收阻塞



	//2.bind函数
	bzero(&sin, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SERV_PORT);
	//优化1
#if 0
	sin.sin_addr.s_addr = htonl(INADDY_ANY);
#else
	if (inet_pton(AF_INET, SERV_IP_ADDR, &sin.sin_addr) < 0) {
		perror("inet_pton");
		exit(0);
	}
#endif
	if (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
		perror("bind");
		exit(0);
	}
	//3.listen()函数
	listen(fd, 5);

	//4.1 accept
	int newfd = -1;
	struct sockaddr_in cin;
	socklen_t socklen = sizeof(cin);


	//5.

	int ret, readwrite;
	char buf[BUFSIZ];
	char buf02[BUFSIZ + 10];


		//阻塞等待客户端连接
		newfd = accept(fd, (struct sockaddr*)&cin, &socklen);
		if (newfd < 0) {
			perror("accept");
			exit(0);
		}

		char ipv4_addr[16];
		if (!inet_ntop(AF_INET, &cin.sin_addr, ipv4_addr, socklen)) {
			perror("inet_ntop");
			exit(0);
		}
		printf("client(%s,%d) is connect\n", ipv4_addr, ntohs(cin.sin_port));

	while (1) {


		bzero(buf, sizeof(buf));
		bzero(buf02, sizeof(buf02));

		readwrite = read(newfd, buf, BUFSIZ - 1);

		if (errno == EAGAIN) {//没有数据可读
			printf("读超时10si,提除客户端\n");
			break;
		}

		if (readwrite < 0) {
			perror("read");
			break;
		}

		if (!readwrite) {
			continue;
		}

		printf("result=%s\n", buf);

		//返回消息给发送端
		strncpy(buf02, BUFADD, strlen(BUFADD));
		strcat(buf02, buf);
		do {
			readwrite = write(newfd, buf02, strlen(buf02));
		} while (readwrite < 0 && EINTR == errno);


		if (!strncasecmp(buf, QUIT, strlen(QUIT))) {
			printf("通话结束fd = %d\n", newfd);
		}
		/*over...*/
	}
	//ret--;
	close(fd);
	return 0;
}

2.client端

#include "net.h"

void checkParam(char*s){
	printf ("\n%s serv_ip serv_port", s);
	printf ("\n\t serv_ip: serv_iperver ip address");
	printf ("\n\t serv_port: server port(>5000)\n\n");
}

int main(int argc, char **argv)
{

	int fd;
	int port = -1;
	struct sockaddr_in cin;

	if(argc != 3){
		checkParam(argv[0]);
		exit(0);
	}

	//1.创建socket fd
	fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0){
		perror("socket");
		exit(0);
	}

	struct timeval ti;
	ti.tv_sec = 10;//允许超时10s
	ti.tv_usec = 0;
	//setsockopt (fd, SOL_SOCKET, SO_SNDTIMEO, &ti, sizeof (ti));
	setsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&ti, sizeof (ti));

	//2.connect函数
	bzero(&cin,sizeof(cin));
	cin.sin_family = AF_INET;

	if((port = atoi(argv[2])) < 5000){
		checkParam(argv[2]);
		exit(0);
	}
	cin.sin_port = htons(port);

	if(inet_pton(AF_INET,argv[1],&cin.sin_addr) < 0){
		perror("inet_pton\n");
		exit(0);
	}

	if(	connect(fd,(struct sockaddr*)&cin,sizeof(cin)) < 0){
		perror("connect");
		exit(0);	
	}	

	//3.读写--使用select函数
	int ret;
	char buf[BUFSIZ];//512

	while(1){
		printf("abc\n");
		bzero(buf,sizeof(buf));
		
		if(fgets(buf,sizeof(buf)-1,stdin) == NULL){
		continue;
		}
		
		do{
			ret = send(fd,buf,BUFSIZ-1,0);
		}while(ret < 0 && EINTR == errno);

		if(ret < 0){
			perror("read");
			exit(0);
		}

		if(!strncasecmp(buf,QUIT,strlen(QUIT))){
			printf("通话结束\n");
			break;
		}
		//读取服务端返回的消息,若是超时没有读取到数据,则退出客户端
			bzero(buf,strlen(buf));//清零
			ret = recv(fd,buf,BUFSIZ-1,0);
			if(ret < 0){
			perror("read");
			exit(0);
			}

			if(!ret){
			printf("超时没收到消息,服务器踢掉客户端\n");
			break;
			}
			printf("来自服务端的数据:%s\n",buf);

			if(!strncasecmp(buf+strlen(BUFADD),QUIT,strlen(QUIT))){
				printf("send client 断开连接\n");
				break;
			}
	}

	close(fd);
	return 0;
}

思考:
  试总结如何在linux中动态检查到是否有网络以及网络中途的掉线/连接的情况检查?
    1.应用层
        心跳检测

       方法 1:数据交互双方隔一段时间,一方发送一点数据到对方,对方给出特定的应答。如果超过设定次数大小的时间内还是没有应答,这时候认为异常

        方法2:改变套接字的属性

函数定义:
void setKeepAlive (int sockfd, int attr_ on, socklen_ .t idle_ time,socklen_ .t interval, socklen_ .t cnt)

{
setsockopt (sockfd, SOL_ SOCKET, SO_ KEEPALIVE, (const char *) &attr. _on, sizeof (attr_ _on));
setsockopt (sockfd, SOL_ _TCP, TCP_ _KEEPIDLE, (const char *) &idle_ _time, sizeof (idle_ time));
setsockopt (sockfd, SOL_ _TCP, TCP_ _KEEPINTVL, (const char *) &interval, sizeof (interval));
setsockopt (sockfd, SOL_ TCP,TCP_ _KEEPCNT,(const char *) &cnt, sizeof (cnt));
}
使用:
int keepAlive = 1;
//设定KeepAlive
int keepIdle = 5;
//开始首次KeepAlive探测前的TCP空闭时间
int keepInterval = 5;
//两次KeepAlive探测间的时间间隔
int keepCount = 3;
//判定断开前的KeepAlive探测次数
setKeepAlive (newfd, keepAlive, keepIdle, keepInterval, keepCount) ;

    2.内核中

 网卡驱动中 2.6内核里面,使能1s的周期性检查定时器
 网卡硬件或者我们通过GPIO,插拔网线时候产生中断,处理相应中断 //立即检测到


总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值