创建简单的linux服务端

 1.首先贴上源码

// 目的:使用多进程处理客户端发来的请求,将客户端发来的字符串改成大写发回客户端
// 步骤:
// 		1.socket()创建监听文件描述符 lfd
//		2.bind() 将lfd与ip和port绑定
//		3.listen() 设置为被动监听模式
//		4.while(1)
//		{
// 			cfd = accept()
// 			pid = fork()创建子进程
//			父进程,回收子进程
//			子进程,循环读写,循环退出时,注意:子进程不能再循环去创建子进程
// 		}
// 		

// 先不使用自定义头文件实现一下
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <strings.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <arpa/inet.h>


int main()
{
	// --1-- int socket(int domain, int type, int protocol);
	int lfd = socket(AF_INET, SOCK_STREAM, 0);
	if(lfd < 0)
	{
		perror("socket error");
		return -1;
	}

	// --2-- int bind(int sockfd, const struct sockaddr *addr, 
	// socklen_t addrlen);
	struct sockaddr_in serv;
	// void bzero(void *s, size_t n);
	bzero(&serv, sizeof(serv));
	serv.sin_family = AF_INET;
	serv.sin_port = htons(8866);
	serv.sin_addr.s_addr = htonl(INADDR_ANY);
	int ret = bind(lfd, (struct sockaddr *)(&serv), sizeof(serv));
	if(ret < 0)
	{
		perror("bind error");
		return -1;
	}

	// --3-- int listen(int sockfd, int backlog);
	ret = listen(lfd, 10);
	if(ret < 0)
	{
		perror("listen error");
		return -1;
	}

	// --4-- 进入while循环,监听连接请求,建立相应子进程
	int cfd;
	socklen_t len;
	pid_t pid;
	while(1)
	{
		// --接受连接--int accept(int sockfd, struct sockaddr *addr, 
		// socklen_t *addrlen);
		struct sockaddr_in client;
		len = sizeof(client);
		do
		{
			cfd = accept(lfd, (struct sockaddr *)&client, &len);
		} while (cfd < 0 && (errno == ECONNABORTED || (errno == EINTR)));
		if(cfd < 0)
		{
			perror("accept error");
			return -1;
		}
		// 打印连接的客户端的信息
		char IP[16];
		inet_ntop(AF_INET, &client.sin_addr.s_addr, IP, sizeof(IP));
		printf("client ip = [%s], port = [%d]\n", IP, ntohs(client.sin_port));


		// --fork子进程--
		pid = fork();
		if(pid < 0)
		{
			perror("fork error");
			return -1;
		}
		else if(pid > 0)  // 父进程
		{
			//关闭连接文件描述符
			close(cfd);
			printf("hello\n");
			//回收子进程

		}
		else if(pid ==0)  // 子进程
		{
			//关闭通信文件描述符
			close(lfd);
			// 处理业务逻辑 读写
			char buf[128];
			int n = 0;
			int i = 0;
			
			while(1)
			{
				memset(buf, 0x00, sizeof(buf));
				n = read(cfd, buf, sizeof(buf));
				if(n <= 0)
				{
					perror("read error or client closed");
					break;
				}
				printf("%s\n", buf);

				for(i = 0; i < n; i++)
				{
					buf[i] = toupper(buf[i]);
				}

				write(cfd, buf, sizeof(buf));

			}
			// 读错误退出业务逻辑
			close(cfd);
			exit(-1);   // 也可以 break,退出外面的 while(1)循环,接着就退出了整个子进程
			            // exit() 更直观一点,表示到此没有什么要处理的了,直接退出整个进程吧~
		}

	}



	return 0;
}

2.注意点:

        这个案例没有使用自定义的库函数(里面对socket()、bind()、listen()等进行了封装,实现异常处理)。所以这里的异常处理是直接写在了代码里。
但是没有对读写操作时可能被信号打断的情况的异常进行处理。

3.有待改进的地方:

        父进程没有对子进程的退出进行回收,会产生僵尸进程。后面会有一个回收子进程的版本(使用自定义头文件对异常进行处理(头文件下载于网络))

4.实验:

        ①:客户端主动连接服务端:

        ②:服务端接受三个客户端的连接(还可以更多),在服务端打印客户端的信息:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值