文档章节

LINUX网络编程(TCP)(2 )

庸俗武士
 庸俗武士
发布于 2014/06/04 22:05
字数 1373
阅读 176
收藏 4
点赞 0
评论 0

在 LINUX网络编程(TCP)(1) 中的 程序。。。。


如果启用2个client连接 Server。 先连上server的哪一个Client只要不执行 命令3.

那么第2个client貌似不管怎么发 命令,都得不到 服务器的响应。这是为什么呢?

简单来说:由于通信是使用TCP协议的,而TCP协议是面向连接的, 只要有连接没有断开,那么 服务器就没有办法响应了。只有等连接断开之后,才能处理其他client的命令。


于是,可以对服务器端的程序 做 如下的 修改:

#1. fork()函数的使用。

服务器端程序 变化为:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>

#define PORT_NUM	(2227)		//定义端口号。
#define BUFFSIZE	(1024)

#define	CMD_ONE			("1")
#define CMD_TWO			("2")
#define CMD_THREE		("3")

#define CMD_CODE_ERR	(0)
#define CMD_CODE_ONE	(1)
#define CMD_CODE_TWO	(2)
#define CMD_CODE_THREE	(3)

#define LISTEN_QUE		(10)

#define LOG_MSG(format, ...)	do{fprintf(stderr, "[MessageInfo]: "format" \n", ##__VA_ARGS__);}while(0)

typedef struct socklnk_st{
	int32_t sd;
	struct socklnk_st *next;
}socklnk_t;


socklnk_t*
create_soclnk(int32_t data)
{
	socklnk_t *soclnk = NULL;
	
	soclnk = (socklnk_t *)malloc(sizeof(socklnk_t));
	if(soclnk != NULL){
		soclnk->next = NULL;
		soclnk->sd = data;
	}
	
	return soclnk;
}

void
free_soclnk(socklnk_t *dst)
{
	socklnk_t *tmp = dst;
	socklnk_t *next;

	while(tmp != NULL){
		next = tmp->next;
		close(tmp->sd);
		free(tmp);
		tmp = next;
	}
}

//定义每个Code返回值。
static int8_t *server_msg[] = {
	[CMD_CODE_ONE] = "Hello",
	[CMD_CODE_TWO] = "Good",
	[CMD_CODE_THREE] = "Bye",
	[CMD_CODE_ERR]	= "Error Command",
};

socklnk_t *
start_serice(uint16_t port)
{
	int8_t serv[NI_MAXSERV];
	int32_t sd;
	int32_t opt = 1;
	struct addrinfo *ai, *res = NULL;
	struct addrinfo hints;
	socklnk_t *head = NULL;
	socklnk_t *cur  = NULL;	
	
	if(snprintf(serv, NI_MAXSERV, "%u", port) < 0){
		goto func_end;
	}
	
	(void)memset(&hints, 0x00, sizeof hints);
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE;
	
	if(getaddrinfo(NULL, serv, &hints, &res) != 0){
		goto func_end;
	}
	
	for(ai = res; ai != NULL; ai = ai->ai_next){
		sd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
		if(sd < 0){
			continue;
		}
		
		if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) < 0){
			close(sd);
			free_soclnk(head);
			goto func_end;
		}
		
		if(bind(sd, ai->ai_addr, ai->ai_addrlen) < 0){
			close(sd);
			continue;
		}
		
		if(listen(sd, LISTEN_QUE) < 0){
			close(sd);
			continue;
		}
		
		if(head == NULL){
			head = create_soclnk(sd);
			if(head == NULL){
				close(sd);
				free_soclnk(head);
				goto func_end;			
			}
			cur = head;
		}else{
			cur->next = create_soclnk(sd);
			if(cur->next == NULL){
				close(sd);
				free_soclnk(head);
				goto func_end;			
			}
			cur = cur->next;
		}
	}
	
func_end:
	if(res != NULL){
		freeaddrinfo(res);
	}

	return head;
}

int
main(int ac, char **av)
{
	int8_t *buf;
	int32_t connfd;
	int32_t maxfd = -1;
	int32_t cmd;
	int32_t sendlen;
	fd_set fds;
	pid_t pid;        //+新追加
	
	socklnk_t *sdlnk = NULL;
	socklnk_t *this;
	
	
	buf = (int8_t*)malloc(BUFFSIZE);
	if(buf == NULL){
		LOG_MSG("Service Start Failed");
		goto func_end;		
	}
	
	sdlnk = start_serice(PORT_NUM);
	if(sdlnk == NULL){
		LOG_MSG("Service Start Failed");
		goto func_end;
	}
	
	//main Loop
	for(;;){
		FD_ZERO(&fds);
		for(this = sdlnk; this != NULL; this = this->next){
			FD_SET(this->sd, &fds);
			if(this->sd > maxfd){
				maxfd = this->sd;
			}
		}
		
		if(select(maxfd + 1, &fds, NULL, NULL, NULL) > 0){
			for(this = sdlnk; this != NULL; this = this->next){
				if(FD_ISSET(this->sd, &fds) != 0){
					if((connfd = accept(this->sd, NULL, NULL)) < 0){
						continue;
					}
					break;
				}
			}
		}else{
			goto func_end;
		}
	
		if(this == NULL){
			goto func_end;
		}
		
		
		//+新追加。。
		pid = fork();
		if(pid < 0){
			close(connfd);
			goto func_end;
		}else if(pid > 0){
			close(connfd);
			waitpid(-1, NULL, WNOHANG);
		}else{
			for(;;){
				(void)memset(buf, 0x00, BUFFSIZE);
			
				if(read(connfd, buf, BUFFSIZE) > 0){
					if(strncmp(buf, CMD_ONE, strlen(CMD_ONE)) == 0){
						cmd = CMD_CODE_ONE;
					}else if(strncmp(buf, CMD_TWO, strlen(CMD_TWO)) == 0){
						cmd = CMD_CODE_TWO;
					}else if(strncmp(buf, CMD_THREE, strlen(CMD_THREE)) == 0){
						cmd = CMD_CODE_THREE;
					}else{
						cmd = CMD_CODE_ERR;
					}
				
					sendlen = strlen(server_msg[cmd]);
				
					if(write(connfd, server_msg[cmd], sendlen) != sendlen){
						close(connfd);
						goto func_end;
					}
				
					if(cmd == CMD_CODE_THREE){
						break;
					}
				}else{
					break;
				}
			}
		
			close(connfd);
			goto func_end;
		
		}	
	}
	
	
func_end:
	if(sdlnk != NULL){
		free_soclnk(sdlnk);
	}
	
	if(buf != NULL){
		free(buf);
	}
	
	return 0;
}

哦,谢天谢地,服务 可以响应多个  客户端啦~

但是 ,又出现了一个新的问题。。 竟然 产生了 Defunct进程。。。

这是怎么回事?。。

理由是 子进程 结束后 会想 父进程 发送一个SIGCHLD的信号。默认对这个信号的处理是 忽略。

而在上面的代码中,waitpid用了WNOHANG参数。也就是不等待 子进程结束。因此子进程 结束了 ,就没有对他做处理了。

下面的 代码 则是改进了 ,已经不会产生 Defunct了。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <signal.h>

#define PORT_NUM	(2227)		//定义端口号。
#define BUFFSIZE	(1024)

#define	CMD_ONE			("1")
#define CMD_TWO			("2")
#define CMD_THREE		("3")

#define CMD_CODE_ERR	(0)
#define CMD_CODE_ONE	(1)
#define CMD_CODE_TWO	(2)
#define CMD_CODE_THREE	(3)

#define LISTEN_QUE		(10)

#define LOG_MSG(format, ...)	do{fprintf(stderr, "[MessageInfo]: "format" \n", ##__VA_ARGS__);}while(0)

typedef struct socklnk_st{
	int32_t sd;
	struct socklnk_st *next;
}socklnk_t;


socklnk_t*
create_soclnk(int32_t data)
{
	socklnk_t *soclnk = NULL;
	
	soclnk = (socklnk_t *)malloc(sizeof(socklnk_t));
	if(soclnk != NULL){
		soclnk->next = NULL;
		soclnk->sd = data;
	}
	
	return soclnk;
}

void
free_soclnk(socklnk_t *dst)
{
	socklnk_t *tmp = dst;
	socklnk_t *next;

	while(tmp != NULL){
		next = tmp->next;
		close(tmp->sd);
		free(tmp);
		tmp = next;
	}
}

//定义每个Code返回值。
static int8_t *server_msg[] = {
	[CMD_CODE_ONE] = "Hello",
	[CMD_CODE_TWO] = "Good",
	[CMD_CODE_THREE] = "Bye",
	[CMD_CODE_ERR]	= "Error Command",
};

socklnk_t *
start_serice(uint16_t port)
{
	int8_t serv[NI_MAXSERV];
	int32_t sd;
	int32_t opt = 1;
	struct addrinfo *ai, *res = NULL;
	struct addrinfo hints;
	socklnk_t *head = NULL;
	socklnk_t *cur  = NULL;	
	
	if(snprintf(serv, NI_MAXSERV, "%u", port) < 0){
		goto func_end;
	}
	
	(void)memset(&hints, 0x00, sizeof hints);
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE;
	
	if(getaddrinfo(NULL, serv, &hints, &res) != 0){
		goto func_end;
	}
	
	for(ai = res; ai != NULL; ai = ai->ai_next){
		sd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
		if(sd < 0){
			continue;
		}
		
		if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) < 0){
			close(sd);
			free_soclnk(head);
			goto func_end;
		}
		
		if(bind(sd, ai->ai_addr, ai->ai_addrlen) < 0){
			close(sd);
			continue;
		}
		
		if(listen(sd, LISTEN_QUE) < 0){
			close(sd);
			continue;
		}
		
		if(head == NULL){
			head = create_soclnk(sd);
			if(head == NULL){
				close(sd);
				free_soclnk(head);
				goto func_end;			
			}
			cur = head;
		}else{
			cur->next = create_soclnk(sd);
			if(cur->next == NULL){
				close(sd);
				free_soclnk(head);
				goto func_end;			
			}
			cur = cur->next;
		}
	}
	
func_end:
	if(res != NULL){
		freeaddrinfo(res);
	}

	return head;
}


void
sig_child(int32_t signo)
{
	for(;;){
		if(waitpid(-1, NULL, WNOHANG) > 0){
			break;
		}
	}
}



int
main(int ac, char **av)
{
	int8_t *buf;
	int32_t connfd;
	int32_t maxfd = -1;
	int32_t cmd;
	int32_t sendlen;
	fd_set fds;
	pid_t pid;
	
	socklnk_t *sdlnk = NULL;
	socklnk_t *this;
	
	
	buf = (int8_t*)malloc(BUFFSIZE);
	if(buf == NULL){
		LOG_MSG("Service Start Failed");
		goto func_end;		
	}
	
	sdlnk = start_serice(PORT_NUM);
	if(sdlnk == NULL){
		LOG_MSG("Service Start Failed");
		goto func_end;
	}
	
	signal(SIGCHLD, sig_child);
	
	//main Loop
	for(;;){
		FD_ZERO(&fds);
		for(this = sdlnk; this != NULL; this = this->next){
			FD_SET(this->sd, &fds);
			if(this->sd > maxfd){
				maxfd = this->sd;
			}
		}
		
		if(select(maxfd + 1, &fds, NULL, NULL, NULL) > 0){
			for(this = sdlnk; this != NULL; this = this->next){
				if(FD_ISSET(this->sd, &fds) != 0){
					if((connfd = accept(this->sd, NULL, NULL)) < 0){
						continue;
					}
					break;
				}
			}
		}else{
			goto func_end;
		}
	
		if(this == NULL){
			goto func_end;
		}
		
		pid = fork();
		if(pid < 0){
			close(connfd);
			goto func_end;
		}else if(pid > 0){
			close(connfd);
			
		}else{
			for(;;){
				(void)memset(buf, 0x00, BUFFSIZE);
			
				if(read(connfd, buf, BUFFSIZE) > 0){
					if(strncmp(buf, CMD_ONE, strlen(CMD_ONE)) == 0){
						cmd = CMD_CODE_ONE;
					}else if(strncmp(buf, CMD_TWO, strlen(CMD_TWO)) == 0){
						cmd = CMD_CODE_TWO;
					}else if(strncmp(buf, CMD_THREE, strlen(CMD_THREE)) == 0){
						cmd = CMD_CODE_THREE;
					}else{
						cmd = CMD_CODE_ERR;
					}
				
					sendlen = strlen(server_msg[cmd]);
				
					if(write(connfd, server_msg[cmd], sendlen) != sendlen){
						close(connfd);
						goto func_end;
					}
				
					if(cmd == CMD_CODE_THREE){
						break;
					}
				}else{
					break;
				}
			}
		
			close(connfd);
			goto func_end;
		
		}	
	}
	
	
func_end:
	if(sdlnk != NULL){
		free_soclnk(sdlnk);
	}
	
	if(buf != NULL){
		free(buf);
	}
	
	return 0;
}


© 著作权归作者所有

共有 人打赏支持
庸俗武士
粉丝 2
博文 15
码字总数 5935
作品 0
杭州
高级程序员
Linux网络编程:什么是Linux下的网络编程?

想知道Linux下的网络编程培训×××?先来了解一下什么是Linux下的网络编程吧!Linux下的网络编程指的是socket套接字编程,入门比较简单。在学校里学过一些皮毛,平时就是自学玩,没有见识过...

长沙千锋 ⋅ 05/23 ⋅ 0

PHP程序员进阶之路好书籍推荐

今天给PHP程序员们推荐几本PHP进阶好书,PHP程序员们不要以为自己现在已经工作就放弃了一个上进的心,现在互联网发展这么快,小心长江后浪推前浪,前浪死在沙滩上哦。。。 · 《UNIX网络编程...

Yomut ⋅ 04/25 ⋅ 0

嵌入式Linux学习基础规划篇

嵌入式的学习是需要日积月累的,是通过一点一滴的积累才能成为大神。下面来介绍一下嵌入式linux学习基础规划,目标是达到适应嵌入式应用软件开发、嵌入式系统开发或嵌入式驱动开发的基本素质...

创客学院 ⋅ 04/10 ⋅ 0

嵌入式软件学习方法 嵌入式软件知识

目前嵌入式软件的学习主要主要面向的几个操作系统是,Linux,WINCE、VxWorks等等。其中Linux是开源免费的,而且其源代码是开放的,更加适合我们学习嵌入式软件。下面我们介绍一下嵌入式软件学...

创客学院 ⋅ 04/12 ⋅ 0

Linux操作系统学习中请绕开这五大坑

导读 开发应用比较困难,只适用网络等等,这些对于Linux的评价只是在一些并不了解Linux的人的看法,一但你真正认识了Linux,都会被Linux的魅力所征服... 误区一:开发应用比较困难 这种说法在...

linuxprobe16 ⋅ 05/18 ⋅ 0

Linux多线程并发服务器编程(线程池,FTP服务器)

分享网盘下载:https://pan.baidu.com/s/1gfNCcXt 密码: irfk 内容简介 本课程从最基础的进程、线程概念讲起逐步深入,通过理论与实践结合的方式,使学员快说掌握linux多线程网络编程技术,并...

人气王子333 ⋅ 2017/11/13 ⋅ 0

Linux网络编程(三)

目录 前言 socket 什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口。socket可以看成两个进程之间通信的抽象。socket是全双工的通信方式。 socket不仅可以用于本机的进程间...

simonforfuture ⋅ 01/20 ⋅ 0

Linux基础知识99问(一)

出处:兄弟连教育交流平台 1.什么是Linux?   Linux是一个诞生于网络、成长于网络且成熟于网络的奇特的操作系统。1991年,芬兰大学生Linus Torvalds萌发了开发一个自由的UNIX操作系统的想法...

疯狂的小黑 ⋅ 2016/09/27 ⋅ 0

Linux网络编程必看书籍推荐

首先要说讲述TCP/IP的书很多,其中有3泰书很全。 分别是《TCP/IP详解》三卷本,《用TCP/IP进行网际互连》三卷本,《TCP/IP指南》+《IPv6》四卷本 其中TCP/IP详解的作者还写了另外2本经典著作...

晨曦之光 ⋅ 2012/03/09 ⋅ 0

Linux网络编程必看书籍推荐

首先要说讲述TCP/IP的书很多,其中有3泰书很全。 分别是《TCP/IP详解》三卷本,《用TCP/IP进行网际互连》三卷本,《TCP/IP指南》+《IPv6》四卷本 其中TCP/IP详解的作者还写了另外2本经典著作...

晨曦之光 ⋅ 2012/03/09 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

如何优雅的编程——C语言界面的一点小建议

我们鼓励在编程时应有清晰的哲学思维,而不是给予硬性规则。我并不希望你们能认可所有的东西,因为它们只是观点,观点会随着时间的变化而变化。可是,如果不是直到现在把它们写在纸上,长久以...

柳猫 ⋅ 36分钟前 ⋅ 0

从零手写 IOC容器

概述 IOC (Inversion of Control) 控制反转。熟悉Spring的应该都知道。那么具体是怎么实现的呢?下面我们通过一个例子说明。 1. Component注解定义 package cn.com.qunar.annotation;impo...

轨迹_ ⋅ 37分钟前 ⋅ 0

系统健康检查利器-Spring Boot-Actuator

前言 实例由于出现故障、部署或自动缩放的情况,会进行持续启动、重新启动或停止操作。它可能导致它们暂时或永久不可用。为避免问题,您的负载均衡器应该从路由中跳过不健康的实例,因为它们...

harries ⋅ 38分钟前 ⋅ 0

手把手教你搭建vue-cli脚手架-详细步骤图文解析[vue入门]

写在前面: 使用 vue-cli 可以快速创建 vue 项目,vue-cli很好用,但是在最初搭建环境安装vue-cli及相关内容的时候,对一些人来说是很头疼的一件事情,本人在搭建vue-cli的项目环境的时候也是...

韦姣敏 ⋅ 48分钟前 ⋅ 0

12c rman中输入sql命令

12c之前版本,要在rman中执行sql语句,必须使用sql "alter system switch logfile"; 而在12c版本中,可以支持大量的sql语句了: 比如: C:\Users\zhengquan>rman target / 恢复管理器: Release 1...

tututu_jiang ⋅ 今天 ⋅ 0

Nginx的https配置记录以及http强制跳转到https的方法梳理

Nginx的https配置记录以及http强制跳转到https的方法梳理 一、Nginx安装(略) 安装的时候需要注意加上 --with-httpsslmodule,因为httpsslmodule不属于Nginx的基本模块。 Nginx安装方法: ...

Yomut ⋅ 今天 ⋅ 0

SpringCloud Feign 传递复杂参数对象需要注意的地方

1.传递复杂参数对象需要用Post,另外需要注意,Feign不支持使用GetMapping 和PostMapping @RequestMapping(value="user/save",method=RequestMethod.POST) 2.在传递的过程中,复杂对象使用...

@林文龙 ⋅ 今天 ⋅ 0

如何显示 word 左侧目录大纲

打开word说明文档,如下图,我们发现左侧根本就没有目录,给我们带来很大的阅读障碍 2 在word文档的头部菜单栏中,切换到”视图“选项卡 3 然后勾选“导航窗格”选项 4 我们会惊奇的发现左侧...

二营长意大利炮 ⋅ 今天 ⋅ 0

智能合约编程语言Solidity之线上开发工具

工具地址:https://ethereum.github.io/browser-solidity/ 实例实验: 1.创建hello.sol文件 2.调试输出结果

硅谷课堂 ⋅ 今天 ⋅ 0

ffmpeg 视频格式转换

转 Mp4 格式 #> ffmpeg -i input.avi -c:v libx264 output.mp4#> ffmpeg -i input.avi -c:v libx264 -strict -2 output.mp4#> ffmpeg -i input.avi -c:v libx264 -strict -2 -s 1......

Contac ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部