《网络编程》C语言 创建TCP服务器(三种)

2023-05-16

一、循环服务器

伪代码:

sfd = socket ();
bind ();
listen ();
while ( 1 )
{
newfd = accept ();
while ( 1 )
{
recv ();
send ();
}
close ( newfd );
}
close ( sfd );

代码实现:

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>

//打印错误信息
#define ERR_MSG(msg) do{\
	fprintf(stderr,"__%d__",__LINE__); \
	perror(msg);}while(0)

#define PORT 8888     //1024——49151
#define IP "192.168.31.73"   //本机IP
int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("create socket success\n");

	//允许端口快速重用
	int reuse = 1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	
	//填充地址信息结构体,真实的地址信息结构体与协议族相关
	//AF_INET
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);    //网络字节序的端口号
	sin.sin_addr.s_addr = inet_addr(IP);      //网络字节序的IP地址 

	//将地址信息结构体绑定到套接字上
	bind(sfd,(struct sockaddr *)&sin,sizeof(sin));
	if(bind < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success\n");

	//将套接字设置为被动监听状态,让内核去监听是否有客户端链接
	if(listen(sfd,10) < 0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success\n");
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);
while(1)
{
	//从已经完成链接的队列头中,取出一个客户端的信息,创建生成一套新的套接字文件描述符
	//该文件描述符才是与客户端通信的文件描述符
	int newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen);
	if(newfd < 0)
	{
		ERR_MSG("accept");
		return -1;
	}
	//网络字节序的IP--->点分十进制,网络字节序的poet---->本机字节序
	printf("[%s : %d] newfd = %d\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
	
	char buf[128] = "";
	ssize_t res = 0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		//循环接收
		res = recv(newfd,buf,sizeof(buf),0);
		if(res < 0)
		{
			ERR_MSG("recv");
			return -1;
		}else if(res == 0)
		{
			printf("newfd = %d 客户端退出\n",newfd);
			break;
		}
	printf("[%s : %d] newfd = %d : %s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd,buf);
		
		//发送数据
		strcat(buf,"*_*");
		if(send(newfd,buf,sizeof(buf),0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
		printf("send message success\n");
	}
	close(newfd);
}
	close(sfd);
	return 0;
}

二、多进程并发服务器

伪代码:

void handler ( int sig )
{
// 回收僵尸进程
// 回收成功则再回收一次,直到回收失败或者没有回收到为止
while ( waitpid ( - 1 , NULL , WNOHANG ) > 0 );
}
// 捕获 17 号信号 SIGCHLD
sighandler_t s = signal ( 17 , handler );
sfd = socket ();
bind ();
listen ();
while ( 1 )
{
newfd = accept ();
pid = fork ();
if ( pid > 0 )
{
close ( newfd );
}
else if ( 0 == pid )
{
close ( sfd );
while ( 1 )
{ ii. 代码示例
recv ();
send ();
}
close ( newfd );
exit ( 0 );
}
}
close ( sfd );

 

功能代码:

#include<signal.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>

//打印错误信息
#define ERR_MSG(msg) do{\
	fprintf(stderr,"__%d__",__LINE__); \
	perror(msg);}while(0)

#define PORT 8888     //1024——49151
#define IP "192.168.31.73"   //本机IP

typedef void(*sighandler_t)(int);

int cli_msg(int newfd,struct sockaddr_in cin);

void handler(int sig)
{
	while(waitpid(-1,NULL,WNOHANG) > 0);
}

int main(int argc, const char *argv[])
{
	//捕获17号信号
	sighandler_t s = signal(17,handler);
	if(SIG_ERR == s)
	{
		ERR_MSG("signal");
		return -1;
	}

	//创建套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}

	//允许端口快速重用
	int reuse = 1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}

	//绑定客户端的地址信息结构体(非必须绑定)
	//填充要连接的服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);

	//将地址信息结构体绑定到套接字上
	bind(sfd,(struct sockaddr *)&sin,sizeof(sin));
	if(bind < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success\n");

	//将套接字设置为被动监听状态,让内核去监听是否有客户端链接
	if(listen(sfd,10) < 0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success\n");

	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);

	int newfd = 0;
	pid_t pid = 0;

	while(1)
	{
		newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen);
		if(newfd < 0)
		{
			ERR_MSG("newfd");
			return -1;
		}

		printf("[%s : %d]newfd = %d\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);

		pid = fork();
		if(pid > 0)
		{
			close(newfd);
		}
		else if(pid == 0)
		{
			cli_msg(newfd,cin);

			close(newfd);
			exit(0);
		}
		else
		{
			ERR_MSG("fork");
			return -1;
		}
	}


	close(sfd);
	return 0;
}


int cli_msg(int newfd,struct sockaddr_in cin)
{
char buf[128] = "";
	ssize_t res = 0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		//循环接收
		res = recv(newfd,buf,sizeof(buf),0);
		if(res < 0)
		{
			ERR_MSG("recv");
			return -1;
		}else if(res == 0)
		{
			printf("newfd = %d 客户端退出\n",newfd);
			break;
		}
	printf("[%s : %d] newfd = %d : %s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd,buf);
		
		//发送数据
		strcat(buf,"*_*");
		if(send(newfd,buf,sizeof(buf),0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
		printf("send message success\n");
	}
}

三、多线程并发服务器

伪代码:

sfd = socket ();
bind ();
listen ();
struct msg cliInfo ;
while ( 1 )
{
newfd = accept ();
cliInfo . newfd = newfd ;
pthread_create ( & tid , NULL , callback , & cliInfo );
}
void* callBack ( void* arg )
{
pthread_detach ( pthread_self ());
while ( 1 )
{
recv ();
send ();
}
close ( newfd );
pthread_exit ()
}

 

代码实现:

#include<signal.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>
#include<pthread.h>
#include<fcntl.h>
//打印错误信息
#define ERR_MSG(msg) do{\
	fprintf(stderr,"__%d__",__LINE__); \
	perror(msg);}while(0)

#define PORT 8888     //1024——49151
#define IP "192.168.31.73"   //本机IP


struct msg
{
	int newfd;
	struct sockaddr_in cin;
};

void *msg_cli(void *arg)
{
	pthread_detach(pthread_self());

	int newfd = ((struct msg *)arg)->newfd;
	struct sockaddr_in cin = ((struct msg *)arg)->cin;

	char buf[128] = "";
	ssize_t res = 0;
	while(1)
	{
bzero(buf,sizeof(buf));
		//循环接收
		res = recv(newfd,buf,sizeof(buf),0);
		if(res < 0)
		{
			ERR_MSG("recv");
		}else if(res == 0)
		{
			printf("newfd = %d 客户端退出\n",newfd);
			break;
		}
	printf("[%s : %d] newfd = %d : %s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd,buf);
		
		//发送数据
		strcat(buf,"*_*");
		if(send(newfd,buf,sizeof(buf),0) < 0)
		{
			ERR_MSG("send");
		}
		printf("send message success\n");
	}
	close(newfd);
	pthread_exit(NULL);
}



int main(int argc, const char *argv[])
{

	//创建套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}

	//允许端口快速重用
	int reuse = 1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}

	//绑定客户端的地址信息结构体(非必须绑定)
	//填充要连接的服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);

	//将地址信息结构体绑定到套接字上
	bind(sfd,(struct sockaddr *)&sin,sizeof(sin));
	if(bind < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success\n");

	//将套接字设置为被动监听状态,让内核去监听是否有客户端链接
	if(listen(sfd,10) < 0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success\n");

	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);
	int newfd = 0;
	struct msg msg;

	pthread_t tid;

	while(1)
	{
		newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen);
		if(newfd < 0)
		{
			ERR_MSG("newfd");
			return -1;
		}
		
		msg.newfd = newfd;
		msg.cin = cin;
		if(pthread_create(&tid,NULL,msg_cli,(void *)&msg) != 0)
		{
			ERR_MSG("pthread_create");
			return -1;
		}


	}


	close(sfd);
	return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《网络编程》C语言 创建TCP服务器(三种) 的相关文章

  • C++ 库函数<string>示例

    最近在学C 43 43 自己整理了一部分C 43 43 库函数 lt string gt 的一些 函数 主要都是C 43 43 的 想到以后可能会用到 所以打算记录一下 方便自己也方便了大家 在啃这些函数的时候有很多借鉴之处 xff0c 在
  • 两台电脑间的串口通信

    目录 一 准备工作 二 实验过程 三 实验结果 一 准备工作 1 两台笔记本电脑 2 2个usb转串口模块 3 杜邦线若干 4 秒表 二 实验过程 两个串口线分别连接两台电脑 连线方式 xff1a 3V3 3V3 xff0c GND GND
  • c++ primer plus学习笔记(1)——基础知识

    本人还有一星期要开始期末考试了 xff0c 复习c 43 43 时顺便挖个坑 xff0c 之后会详细更新 目录 1 初识源代码 1 1 c 43 43 程序的产生 1 2 代码例 1 3 标记 空白 2 简单数据类型 2 1 变量名 2 2
  • C语言:sizeof和strlen计算字符串大小

    大家清楚 sizeof 和 strlen 的区别吗 xff1f sizeof是运算符 xff0c 确定的是字符所占空间大小 xff0c 参数可以有数组 指针 类型 对象 函数等 strlen是C语言的标准库函数 xff0c 确定是字符串的大
  • Python--爬虫--requests进阶,cookie/session模拟登录

    目录 一 原理 二 实际操作 三 结果 四 问题与总结 一 原理 以下内容为使用requests库发送请求 xff0c 使用cookie session模拟登录 xff08 并且登录时只需输入账号与密码 xff09 我们在使用搜索引擎访问网
  • linux系统移植U-boot与kernel的搭载流程(交互模式下)

    Linux系统移植四大部分 xff1a 搭建交叉开发环境 bootloader的选择和移植 本文选用bootloader下的U boot uImage的配置 编译与移植 根文件系统的制作 全部已完成 xff0c 本文只讲解 如何搭载这些东西
  • FreeRTOS任务创建、删除| FreeRTOS三

    目录 一 FreeRTOS任务创建与删除有关函数 1 1 创建 删除任务的API函数 1 1 1 动态创建任务 1 1 2 静态创建任务 1 1 3 删除任务 二 FreeRTOS任务创建与删除 xff08 动态方法 xff09 2 1 实
  • (学习)基于STM32的串口通信打印数据(HAL库+CubeMX)

    当我们在进行开发的时候 xff0c 通常需要将一些参数进行调整来达到效果 xff0c 这个时候就需要将单片机上的信息通过串口通信传送到PC机上来直观显示 一 基本的专有名词和原理介绍 USART xff1a 只能异步通信 USART xff
  • ROS通信——C++实现

    一 普通话题通信 创建功能包 xff1a catkin create pkg package roscpp rospy std msgs 创建发布者 xff1a include 34 ros ros h 34 include 34 std
  • ROS使用Python编写的步骤

    第一步 xff1a 和C 43 43 编写一样 xff0c 配置好工作空间 第二步 xff1a 在功能包下面建立一个scripts文件夹 第三步 xff1a 在scripts文件里面建立一个 py文件 第四步 编写python文件 注意 x
  • window的QT作为TCP客户端,ubuntu的python作为TCP服务端

    客户端 pro文件加入 QT 43 61 network mainwindow h ifndef MAINWINDOW H define MAINWINDOW H include lt QMainWindow gt include lt Q
  • RoboCom机器人大赛使用yolov5抽取20个随机图片进行人群识别

    目录 1 原理 2 思维流程 2 1 进行yolov5的环境搭建 2 1 1 在Linux的ubuntu环境anaconda的安装 2 1 2 Vscode的安装和配置 2 1 3 Github上面yolov5文件的下载 2 1 4 使用A
  • C语言实现字符串逆序、倒置字符串(字符串逆序问题的升级)

    一 字符串逆序 问题描述 xff1a 输入一个字符串str xff0c 将其内容颠倒过来 xff0c 并输出 数据范围0 lt len str lt 10000 输入描述 xff1a 输入一个字符串 xff0c 可以有空格 输出描述 xff
  • C语言实现“井字棋”游戏(三子棋)人机对弈

    井字棋游戏 xff1a 即三子棋 xff0c 英文名叫Tic Tac Tic xff0c 是一种在3 3格子上进行的连珠游戏 xff0c 和五子棋比较类似 xff0c 由于棋盘一般不画边线框 xff0c 格线排成井字故得名 题目分析 xff
  • C语言基础编程练习(精选例题+题解)

    目录 1 求最大公约数和最小公倍数 2 打印图形 3 质数因子 4 数字排序 5 十进制数转换为八进制数 xff08 进制转换 xff09 6 寻找完数 1 求最大公约数和最小公倍数 题目描述 xff1a 输入两个正整数m和n xff0c
  • C语言——实现“扫雷”小游戏(简易版)

    扫雷游戏想必我们都有玩过 xff0c 那么今天就用C语言来简单实现 扫雷 小游戏 xff01 目录 一 游戏规则 二 基本思路介绍 三 各功能代码实现 1 创建用户交互界面 2 初始化棋盘函数 3 埋雷函数 4 显示棋盘函数 5 排雷函数
  • linux驱动开发关于内核模块、设备模型、内存管理、模块参数介绍

    内核功能 多任务管理 内存管理 文件管理 网络协议栈 设备管理 设备驱动 我是分割线 一 内核开发特点 内核代码的运行时机 启动目标硬件应用程序调用系统调用硬件中断调用中断处理Linux内核和驱动间的接口 kABI 不稳定内核升级后 接口可
  • Java实现单链表及其基本操作

    目录 什么是单链表 xff1f 带头结点的单链表 不带头结点的单链表 模拟实现不带头结点的单链表 定义结点类 初始化 头插法创建单链表 尾插法创建单链表 打印单链表 单链表的查找 获取单链表的长度 按位置寻找前驱结点 单链表的插入 修改指定
  • 优化后的快速排序(详解)

    目录 快速排序 三数取中值分割法获得枢纽元 快速排序的主例程 直接插入排序代码 快速排序完整代码 快速排序 快速排序是实践中的一种快速的排序算法 xff0c 它的平均运行时间是O N log N xff0c 该算法之所以特别快 xff0c
  • Java多线程~谈谈自己对于synchronized、volatile关键字的理解,线程的状态转换以及创建线程的方式

    目录 创建线程的方式 继承Thread类 实现Runnable接口 实现Callable接口 线程的状态转换 volatile关键字 synchronized关键字 synchronized关键字的特性 synchronized原理 创建线

随机推荐