LINUX网络编程(TCP)(1 )

实现一个网络编程:

使用传输协议: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  都可以从服务器哪里接受到信息了。。

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

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


你可能感兴趣的:(linux,tcp)