文档章节

LINUX网络编程(TCP)(1 )

庸俗武士
 庸俗武士
发布于 2014/06/03 23:04
字数 1231
阅读 209
收藏 3

实现一个网络编程:

使用传输协议:TCP

主要是写一些关于socket函数的使用。

大致实现这么一个功能:

客户端 

发送1      服务器返回 Hello

发送2      服务器返回 GOOD

发送3      服务器返回 Bye  并且关闭连接。


先看客户端的程序:

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

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

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

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

/*
**************************************
@brief	连接到指定的Server
@param(in)	host:	服务器名
@param(in)	port:	服务器端口
@return	Success: socket描述符应该(大于0)
		Failure: -1
**************************************
*/	
int32_t
jet_connect(const int8_t *host, uint16_t port)
{
	int32_t sd = -1;
	int8_t serv[NI_MAXSERV];
	struct addrinfo *ai, *res = NULL;
	struct addrinfo hints;
	
	//异常处理。
	if(host == NULL){
		goto func_end;
	}
	
	//snprintf会在最后加上一个\0,所以这里就不memset serv了。
	if(snprintf(serv, NI_MAXSERV, "%u", port) < 0){
		goto func_end;
	}
	
	(void)memset(&hints, 0x00, sizeof hints);
	//Ipv4 , Ipv6地址都可以		如果只要Ipv4那么这里填写:AF_INET, IPv6:AF_INET6
	hints.ai_family = AF_UNSPEC;	
	//TCP 为SOCK_STREAM, UDP为:SOCK_DGRAM
	hints.ai_socktype = SOCK_STREAM;
	//这里写不写都无所谓。 socket函数会根据family和sockettype自动匹配。
	hints.ai_protocol = IPPROTO_TCP;
	
	//取得服务器的addrinfo
	if(getaddrinfo(host, serv, &hints, &res) != 0){
		LOG_MSG("%s", gai_strerror(errno));
		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(connect(sd, ai->ai_addr, ai->ai_addrlen) < 0){
			//Connect失败了, 所以socket描述符关闭,因为后面不用了。
			close(sd);
			continue;
		}
		
		//成功连接上了。 不用在去循环了.
		break;
	}
	
	//上述循环是否成功 可以通过ai是否为NULL来判断。
	if(ai == NULL){
		sd = -1;
		goto func_end;
	}

func_end:
	if(res != NULL){
		freeaddrinfo(res);
	}

	return sd;
}

/*
**************************************
从stdin这里获取输入,然后发送给Server。
**************************************
*/
void
do_event(int32_t sd)
{
	int8_t *buf = NULL;
	int8_t *sendbuf;
	int8_t sendlen;

	if((buf = (int8_t *)malloc(BUFFSIZE)) == NULL){
		goto func_end;
	}
	(void)memset(buf, 0x00, BUFFSIZE);
	
	while(fgets(buf, BUFFSIZE, stdin) != NULL){
		//Check Command...
		if(strncmp(buf, CMD_ONE, strlen(CMD_ONE)) == 0){
			sendbuf = CMD_ONE;
		}else if(strncmp(buf, CMD_TWO, strlen(CMD_TWO)) == 0){
			sendbuf = CMD_TWO;
		}else if(strncmp(buf, CMD_THREE, strlen(CMD_THREE)) == 0){
			sendbuf = CMD_THREE;
		}else{
			//客户端如果可以的话,还是尽量不要将明显的错误信息送出去。
			LOG_MSG("The Command Err. Please Input 1~3");
			(void)memset(buf, 0x00, BUFFSIZE);
			continue;
		}

		sendlen = strlen(sendbuf);
		
		if(write(sd, sendbuf, sendlen) < sendlen){
			LOG_MSG("message send to server Error");
		}
		
		(void)memset(buf, 0x00, BUFFSIZE);
		if(read(sd, buf, BUFFSIZE) < 0){
			LOG_MSG("message receive from server Error");
		}
		LOG_MSG("%s", buf);
		
		if(sendbuf == (int8_t*)CMD_THREE){
			break;
		}
		
		(void)memset(buf, 0x00, BUFFSIZE);
	}
	
func_end:
	if(buf != NULL){
		free(buf);
	}
	
	return ;
}

int
main(int ac, char **av)
{
	int32_t sd;

	//需要输入Server的地址或名字。
	if(ac != 2){
		LOG_MSG("you should indicate a server");
		goto func_end;
	}

	if((sd = jet_connect(av[1],  PORT_NUM)) < 0){
		LOG_MSG("Connect to Server Failed");
		goto func_end;
	}

	do_event(sd);
	close(sd);
	
func_end:
	return 0;
}


同时给出服务器端 程序:(注释 基本没写..)

#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返回值。
//这个是C99下,数组初始化才可以这么使用
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;
	
	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;
		}
		
		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);	
	}
	
	
	
func_end:
	if(sdlnk != NULL){
		free_soclnk(sdlnk);
	}
	
	if(buf != NULL){
		free(buf);
	}
	
	return 0;
}


现在用客户端输入1,2,3  都可以从服务器哪里接受到信息了。。

但是好像有点什么不足的样子。。

下一篇 对 服务器 端 做一个改进/。


© 著作权归作者所有

共有 人打赏支持
庸俗武士
粉丝 2
博文 15
码字总数 5935
作品 0
杭州
高级程序员
私信 提问
Linux多线程并发服务器编程(线程池,FTP服务器)

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

人气王子333
2018/06/26
0
0
Linux网络编程必看书籍推荐

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

晨曦之光
2012/03/09
300
0
Linux平台下基于TCP/IP协议的C++网络编程初步

Linux平台下基于TCP/IP协议的C++网络编程初步 实验平台:Ubuntu(Linux) 开发工具:Eclipse+CDT 作者:@郑海波 http://blog.csdn.net/nuptboyzhb/ 特点: 1.基于Linux平台的网络编程,最重要的...

长平狐
2012/10/08
1K
1
Linux网络编程:什么是Linux下的网络编程?

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

长沙千锋
2018/05/23
0
0
Linux网络编程必看书籍推荐

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

晨曦之光
2012/03/09
3.8K
0

没有更多内容

加载失败,请刷新页面

加载更多

开始看《Java学习笔记》

虽然书买了很久,但一直没看。这其中也写过一些Java程序,但都是基于IDE的帮助和对C#的理解来写的,感觉不踏实。 林信良的书写得蛮好的,能够帮助打好基础,看得出作者是比较用心的。 第1章概...

max佩恩
昨天
12
0
Redux 三大原则

1.单一数据源 在传统的MVC架构中,我们可以根据需要创建无数个Model,而Model之间可以互相监听、触发事件甚至循环或嵌套触发事件,这些在Redux中都是不被允许的。 因为在Redux的思想里,一个...

wenxingjun
昨天
8
0
跟我学Spring Cloud(Finchley版)-12-微服务容错三板斧

至此,我们已实现服务发现、负载均衡,同时,使用Feign也实现了良好的远程调用——我们的代码是可读、可维护的。理论上,我们现在已经能构建一个不错的分布式应用了,但微服务之间是通过网络...

周立_ITMuch
昨天
4
0
XML

学习目标  能够说出XML的作用  能够编写XML文档声明  能够编写符合语法的XML  能够通过DTD约束编写XML文档  能够通过Schema约束编写XML文档  能够通过Dom4j解析XML文档 第1章 xm...

stars永恒
昨天
2
0
RabbitMQ学习(2)

1. 生产者客户端 void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body) 1. 在生产者客户端发送消息时,首先......

江左煤郎
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部