TCP 回射服务器

TCP回射 服务端程序

本例为多进程的 TCP 回射程序(服务端)

#include <unp.h>
int main(int argc, char **argv)
{
	int					listenfd, connfd;
	pid_t				childpid;
	socklen_t			clilen;
	struct sockaddr_in	cliaddr, servaddr;
	
	// 指定协议族(AF_INET: ipv4 协议), 
	// 指定套接字类型(TCP 数据流, UDP 数据报或者为原始套接字SOCK_STREAM: 基于 tcp), 
	// 最后一个选项默认即可
	// 成功返回一个非负的套接字描述符(socket descriptor), 此时 listenfd 是一个将要
	// 主动调用 connect 函数的客户端套接字(主动套接字)
	listenfd = Socket(AF_INET, SOCK_STREAM, 0);
	
	// 初始化服务端套接字
	bzero(&servaddr, sizeof(servaddr));
	// 套接字规范中, 仅需要协议族, ip 地址和端口号三个属性
	servaddr.sin_family      = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port        = htons(SERV_PORT);
	
	// 本地协议地址赋予套接字(端口相关, 初步理解为进程赋予众所周知的端口)
	Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

	// 将 listenfd 转换成被动套接字, 指示内核, 此套接字可被调用, 此时, 服务端监听开始!
	// 客户端理所应当的免去这一步
	Listen(listenfd, LISTENQ);

	for ( ; ; ) {
		clilen = sizeof(cliaddr);

		// 返回已建立连接的客户端套接字, 如果当前没有套接字完成连接, 函数阻塞
		// 本函数循环调用, 直到所有建立连接的客户端套接字服务完毕, 此时阻塞,
		// 等待新的连接建立.
		// 客户端的套接字被存放于 cliaddr 中.
		connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);

		// 核心!!!
		// 运行到此, 说明已经成功获得(否则阻塞与 accept 中) connfd, 但是本
		// 主进程并不处理客户端连接, 而是通过 fork() 复制一个完全相同的子进程
		// 服务客户套接字.
		// 服务端调用 fork() 时, 会返回一个非零的子进程 id, 故而, if 中的语句永远
		// 不会被父进程执行, 而被复制而来的子进程运行到这里的时候, fork() 函数不会复制
		// 新的子进程, 而是返回 0, 即, c 中, fork() 函数一次调用, 会有两次返回值.
		if ( (childpid = Fork()) == 0) {	/* 子进程 */
			// 子进程拿到 connfd 连接之后, 
			// 先关闭监听套接字, 子进程无需监听, 全权交由父进程, 而后由父进程分发请求即可
			Close(listenfd);	
			str_echo(connfd);	/* 处理请求 */
			exit(0); // 子进程到此, 已完成使命, 终止退出即可, 此时 connfd 默认 close
		}
		Close(connfd);			/* 父进程断开连接 */
	}
}

void str_echo(int sockfd)
{
	ssize_t		n;
	char		buf[MAXLINE];

again:
	while ( (n = read(sockfd, buf, MAXLINE)) > 0)
		Writen(sockfd, buf, n);

	if (n < 0 && errno == EINTR)
		goto again;
	else if (n < 0)
		err_sys("str_echo: read error");
}
  1. 套接字 sockaddr_in cliaddr, servaddr
    网际套接字地址结构, 包含协议族(ipv 4, ipv 6), 端口号, ip 地址

TCP回射 客户端程序

本例为多进程的 TCP 回射程序(客户端)

#include	"unp.h"

int
main(int argc, char **argv)
{
	int					sockfd;
	struct sockaddr_in	servaddr;

	// 错误退出
	if (argc != 2)
		err_quit("usage: tcpcli <IPaddress>");

	// 创建一个主动套接字, 和服务端协议一致即可
	sockfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(SERV_PORT);
	// ip 格式转换 点分十进制和二进制整数
	Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

	// 本地套接字连接服务端套接字, 此时客户端会自行分配端口
	Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));

	// 连接成功, 执行操作即可
	str_cli(stdin, sockfd);		/* 输入 */

	exit(0);
}

void
str_cli(FILE *fp, int sockfd)
{
	char	sendline[MAXLINE], recvline[MAXLINE];

	while (Fgets(sendline, MAXLINE, fp) != NULL) {

		Writen(sockfd, sendline, strlen(sendline));

		if (Readline(sockfd, recvline, MAXLINE) == 0)
			err_quit("str_cli: server terminated prematurely");

		Fputs(recvline, stdout);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值