【网络】TCP服务器的实现

本文介绍了socket编程的基本概念,包括网络字节序、socket地址数据类型及相关函数的使用方法,并提供了TCP服务器的三种不同版本(普通版、多进程版、多线程版)的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

socket编程基本概念

在TCP/IP协议中,IP地址+端口号标识个唯一的一个进程,“IP地址+端口号”就是socket

在TCP协议中,建立连接需要两个进程各自有一个socket标识符,这两个socket组成的socket pair就标识着唯一的连接

相关概念介绍

网络字节序

首先呢,我们都知道内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件的偏移也有大端和小端之分。

同理,网络数据流其实也有大端和小端之分。


网络数据流的地址这样规定:先发出的数据是低地址,后发出的数据是高地址。

TCP/IP协议规定:网络数据流应采用大端字节序,低地址高字节

socket地址数据类型


用到的相关函数

socket

作用

创建socket套接字

头文件

#include<sys/types.h>

#include<sys/socket.h>

函数原型

int socket(int domain,int type, int protocol);

参数

domain我们一般选择AF_INET,表示IPv4

type表示传输数据的类型,有字节流类型和SOCK_STREAM数据报类型SOCK_DGRAM

protocol表示创建的类型方式,我们此时设置为0

返回值

成功返回对应的文件描述符

错误返回-1

bind

作用

进行服务器的IP地址和端口号的绑定

头文件

#include<sys/socket.h>

#include<netinet/in.h>

#include<arpa/inet.h>

函数原型

int bind(int sockfd,const struct sockaddr *addr, socklen_t addrlen);

参数

sockfd是服务器的套接字,就是socket函数的返回值

addr是socket服务器的地址内容


addrlen是传入的协议地址的大小

返回值

成功返回0

错误返回-1

listen

作用

设置套接字为监听的状态

头文件

#include<sys/types.h>

#include<sys/socket.h>

函数原型

int listen(int sockfd,int backlog);

参数

sockfd是要设置的套接字

backlog是服务器的最大等待队列的个数

返回值

成功返回0

错误返回-1

connect

作用

使客户端连接到服务器

头文件

#include<sys/types.h>

#include<sys/socket.h>

函数原型

int connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen);

参数

sockfd表示需要连接到服务器的客户端套接字

addr表示服务器的地址和端口号

addrlen表示的是addr的大小

返回值

成功返回0

错误返回-1

accpet

作用

服务器用来接受连接

头文件

#include<sys/types.h>

#include<sys/socket.h>

函数原型

int accept(int sockfd, struct sockaddr * addr,  socklen_t *addrlen);

参数

sockfd表示需要连接的客户端的套接字

addr是传出参数,用来传出客户端的IP地址和端口号

addrlen标示传出参数的长度

返回值

成功返回0

错误返回-1

TCP服务器的代码实现

普通版本

server.c

#include<stdio.h>
#include<sys/socket.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<error.h>

#define SERV_PORT 9999
#define _BACKLOG_ 10

void Usage()
{
	printf("Usage: [ipaddr]\n");
}

int main(int argc,char* argv[])
{
	if(argc != 2)
	{
		Usage();
		return -1;
	}

	//1、调用socket来打开一个网络通信端口
	int sock = socket(AF_INET,SOCK_STREAM,0);
	if(sock < 0)
	{
		perror("socket");
		exit(1);
	}

	//2、服务器来绑定一个固定的IP地址和端口号
	struct sockaddr_in serverSocket;//定义服务端的套接字
	struct sockaddr_in clientSocket;//定义客户端的套接字
	
	bzero(&serverSocket,sizeof(serverSocket));//归零初始化

	serverSocket.sin_family = AF_INET;//设置地址的类型
	serverSocket.sin_addr.s_addr = inet_addr(argv[1]);

	serverSocket.sin_port = htons(SERV_PORT);//设置端口号,SERV_PORT为自定义的宏,值设置为9999

	if(bind(sock,(struct sockaddr*)&serverSocket,sizeof(struct sockaddr_in))< 0)//进行绑定,并进行判断
	{
		perror("bind");
		close(sock);
		exit(2);
	}

	//3、进行监听
	if(listen(sock,_BACKLOG_)< 0)
	{
		perror("listen");
		close(sock);
		exit(3);
	}

	printf("绑定和监听成功...请等待连接...\n");

	while(1)
	{
		//4、接受连接
		socklen_t len = 0;//定义长度len
		int clientSock = accept(sock, (struct sockaddr*)&clientSocket,&len);//接受连接
		if(clientSock < 0)
		{
			perror("accpet");
			exit(4);
		}

		char buf[INET_ADDRSTRLEN];//定义缓冲区,用来存放IP字符串
		memset(buf,0,sizeof(buf));//初始化为0
	
		while(1)
		{
			char tmpBuf[1024];//定义空间来存储收到与发送的消息
			ssize_t ret = read(clientSock,tmpBuf,sizeof(tmpBuf));//从客户端套接字中读入数据
			if(ret < 0)
			{
				perror("read");
				exit(5);
			}

			tmpBuf[ret] = '\0';

			printf("#client :# %s\n",tmpBuf);//打印从客户端读入的数据
			printf("#server :# ");
			memset(tmpBuf,'\0',sizeof(tmpBuf));//将buf空间置零
			fgets(tmpBuf,sizeof(tmpBuf),stdin);//读入需要发送的数据
			write(clientSock,tmpBuf,strlen(tmpBuf));
			printf("请等待...\n");
		}
	}

	close(sock);
	return 0;
}

client.c

#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<unistd.h>
#include<sys/types.h>
#include<netinet/in.h>

#define SERVER_PORT 9999

void Usage()
{
	printf("Usage: [client_ip] [client_port]\n");
}

int main(int argc, char* argv[])
{
	if(argc != 3)
	{
		Usage();
		return 1;
	}

	//调用socket来打开一个网络端口
	char buf[1024];
	char* client_ip = argv[1];
	memset(buf,'\0',sizeof(buf));
	int sock = socket(AF_INET,SOCK_STREAM,0);

	//定义sockaddr_in结构体,并进行初始化
	struct sockaddr_in serverSocket;
	serverSocket.sin_family = AF_INET;
	serverSocket.sin_addr.s_addr = inet_addr(argv[1]);
	serverSocket.sin_port = htons(SERVER_PORT);

	//进行服务器的链接
	int ret = connect(sock,(struct sockaddr*)&serverSocket,sizeof(serverSocket));
	if(ret < 0)
	{
		printf("连接失败...\n");
		return 2;
	}
	
	printf("连接成功...\n");

	while(1)
	{
		printf("#client :# ");
		fflush(stdin);
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf)-1] = '\0';

		if(strcmp(buf,"quit")==0)
			break;

		write(sock,buf,sizeof(buf));
	
		printf("请等待服务器响应...\n");
		read(sock,buf,sizeof(buf));
		printf("#server :# %s",buf);
	}

	close(sock);
	return 0;
}

多进程版本

server.c

#include<stdio.h>
#include<sys/types.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>

void Usage()
{
	printf("Usage: [ipaddr] [port]\n");
	exit(1);
}

int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		Usage();
	}

	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0)
	{
		perror("sock");
		exit(2);
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	addr.sin_addr.s_addr = inet_addr(argv[1]);

	
	if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("bind");
		exit(3);
	}

	if(listen(sockfd,10) < 0) 
	{
		perror("listen");
		exit(4);
	}

	printf("wait connect...\n");

	while(1)
	{
		struct sockaddr_in saddr;
	        socklen_t straddr = sizeof(saddr);
		int fd = accept(sockfd,(struct sockaddr *)&saddr,&straddr);

		printf("accept connect...\n");

		if(fd < 0)
		{
			continue;
		}

		pid_t pid = fork();
	
		if(pid < 0)
		{
			perror("fork");
			exit(6);
		}
		else if(pid > 0)
		{
			printf("find a new client... ip : %s ... port : %d\n",inet_ntoa(saddr.sin_addr),ntohs(saddr.sin_port));
			char buf[1024];
			while(1)
			{
				int s = read(fd, buf,sizeof(buf)-1);
				if(s < 0)
				{
					perror("read");
					exit(7);
				}
				else if(s == 0)
				{
					printf("connect quit... ip : %s ... port : %d\n",inet_ntoa(saddr.sin_addr),ntohs(saddr.sin_port));
					close(fd);
					break;
				}
				else
				{
					buf[s] = '\0';
					printf("#client: %s\n",buf);
					write(fd,buf,strlen(buf));
				}
			}
			close(fd);
			wait(pid,NULL,0);
		}
		else
		{
			continue;
		}
	}
	close(sockfd);
	return 0;
}

client.c

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

void Usage()
{
	printf("Usage: [ipaddr] [port]\n");
	exit(1);
}

int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		Usage();
	}

	//打开一个网络端口
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	
	if(sockfd < 0)
	{
		perror("socket");
		exit(2);
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	addr.sin_addr.s_addr = inet_addr(argv[1]);

	if(connect(sockfd,(struct sockaddr*)&addr, sizeof(addr))< 0)
	{
		perror("connect");
		exit(3);
	}

	printf("连接成功...\n");
	char buf[1024];

	while(1)
	{
		printf("#client: ");
		fflush(stdout);
		int s = read(0,buf,sizeof(buf)-1);
		if(s <= 0)
		{
			perror("read");
			exit(4);
		}
		else
		{
			buf[s-1] = 0;
			write(sockfd,buf,strlen(buf));
		}

		int ret = read(sockfd,buf,sizeof(buf)-1);
		if(ret < 0)
		{
			perror("read");
			exit(5);
		}
		else if(ret == 0)
		{
			printf("连接已断开...\n");
			break;
		}
		else
		{
			buf[s] = '\0';
			printf("#server: %s\n",buf);
		}
	}
	close(sockfd);

	return 0;
} 

多线程版本

server.c

#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<unistd.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<pthread.h>
#include<string.h>

void Usage()
{
	printf("Usage: [addrip] [port]\n");
	exit(1);
}

int StartUp(char* argv[])
{
	//创建接口
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0)
	{
		perror("socket");
		exit(2);
	}

	//进行绑定
	
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	addr.sin_addr.s_addr = inet_addr(argv[1]);

	if(bind(sockfd,(struct sockaddr *)&addr,sizeof(addr)) < 0)
	{
		perror("bind");
		exit(3);
	}
	
	//进行监听
	if(listen(sockfd,10) < 0)
	{
		perror("listen");
		exit(4);
	}

	return sockfd;
}

void* Myhandler(void* arg)
{
	int fd = (int)arg;
	char buf[1024];

	while(1)
	{
		ssize_t s = read(fd,buf,sizeof(buf));
		if(s < 0)//error
		{
			perror("read");
			exit(6);
		}
		else if(s == 0)//quit
		{
			printf("client quit...\n");
			close(fd);
			break;
		}
		else//write
		{
			buf[s] = 0;
			printf("#client : %s\n",buf);
			write(fd,buf,strlen(buf));
		}
	}

}

int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		Usage();
	}

	int sockfd = StartUp(argv);	

	printf("初始化成功...等待连接...\n");

	while(1)
	{
		struct sockaddr_in addr;
		socklen_t addrlen = sizeof(addr);
		int fd = accept(sockfd,(struct sockaddr*)&addr,&addrlen);
		if(fd < 0)
		{
			continue;
		}
		printf("连接成功... ip : %s , port: %d\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
		
		pthread_t td;
		pthread_create(&td,NULL,Myhandler,(void*)fd);
		pthread_detach(td);
	}

	close(sockfd);
	return 0;
}

client.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<error.h>
#include<pthread.h>
#include<string.h>
static void Usage()
{
	printf("Usage : [ipaddr] [port]\n");
	exit(1);
}

int StartUp(char* argv[])
{
	//打开接口
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	addr.sin_addr.s_addr = inet_addr(argv[1]);

	if(connect(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("connect");
		exit(2);
	}
	return sockfd;
}

int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		Usage();
	}

	int sockfd = StartUp(argv);

	printf("连接成功...\n");

	while(1)
	{
		printf("#client : ");
		fflush(stdout);
		char buf[1024];
		ssize_t s = read(0,buf,sizeof(buf)-1);
		if(s <= 0)
		{
			perror("read");
			exit(2);
		}
		else
		{
			buf[s-1] = '\0';
			write(sockfd,buf,strlen(buf));
		}

		s = read(sockfd,buf,sizeof(buf)-1);
		if(s == 0)
		{
			printf("server quit...\n");
			break;
		}
		else if(s < 0)
		{
			perror("read");
			exit(3);
		}
		else
		{
			buf[s] = '\0';
			printf("#server : %s\n",buf);
		}
	}
	close(sockfd);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值