TCP笔记-分别用线程和进程进行传输-华清远见

 TCP服务器建立流程:

1.建立socket套接字。

2.绑定IP地址与端口号。

3.建立监听队列,让套接字进入到被动监听状态。

4.接受连接,产生新的套接字。

5.设计数据的收发。

父进程:负责建立链接

子进程:负责与客户端的数据交互。

ps:图片来自华清远见

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

#define BACKLOG 10

void sig_handler(int signum)
{
	pid_t pid;
	pid=wait(NULL);

	printf("child process is recycled successful,pid=%d\n",pid);
}

void recv_send(int cfd)
{

	char buf[128] = {0};
	ssize_t recv_bytes, send_bytes;
	while(1)
	{
		memset(buf,0,sizeof(buf));
		//接收客户端的信息储存到buf中
		recv_bytes = recv(cfd, buf, sizeof(buf),0);
		if(recv_bytes == -1)
		{
			perror("recv");
			break;
		//客户端退出返回0,服务器这里也退出循环
		}else if(recv_bytes == 0)
		{
			printf("client shutdown\n");
			break;
		}
		printf("buf: %s\n",buf);
		//将收到的数据原样返回给发送的客户端
		send_bytes = send(cfd, buf, recv_bytes, 0);
		if(send_bytes == -1)
		{
			perror("send");
			break;
		}

	}
	//用完了就关掉
	colse(cfd);
	exit(EXIT_SUCCESS);
}
int main(int argc, char **argv)
{
	int ret;
	pid_t pid;
	int sfd,cfd;
	struct sockaddr_in src,cli;
	socklen_t len = sizeof(src);
	socklen_t addrlen = sizeof(cli);

	if(signal(SIGCHLD,sig_handler)==SIG_ERR)
	{
		perror("");
		return -1;
	}

	//创建套接字,第二个参数为流式套接字类型
	sfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sfd == -1)
	{
		perror("socket fail");
		return -1;
	}
	
	//服务器地址结构体填充,地址族,IP PORT
	src.sin_family = AF_INET;
	src.sin_port = htons(atoi(argv[2]));
	src.sin_addr.s_addr = inet_addr(argv[1]);
	//绑定套接字,服务器ip和port口号
	ret = bind(sfd, (const struct sockaddr *)&src, len );
	if(ret == -1)
	{
		perror("bind");
		return -1;
	}
	//将套接字设定为被动监听状态,监听客户端的连接请求,10为未决队列长度
	ret = listen(sfd, BACKLOG);
	if(ret == -1)
	{
		perror("listen");
		return -1;
	}

	while(1)
	{
		//接收客户端连接请求,产生连接套接字,用于和客户端通信,通过cli保存客户端ip地址和port口号
		cfd = accept(sfd, (struct sockaddr *)&cli,&addrlen);
		if(cfd == -1)
		{
			perror("accept");
			return -1;
		}
		printf("client IP: %s  PORT: %d\n", inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));//打印客户端IP 和 PORT ,需要将网络字节序转换为主机字节序
		pid = fork();
		if(pid < 0)
		{
			perror("fork");
			return -1;
		}else if(pid == 0)
		{//子进程不需要用到监听套接字,可以关掉
			close(sfd);
			recv_send(cfd);

		}else if(pid>0)
		{
			//父进程不需要连接套接字,可以关掉
			close(cfd);			
		}
	}
	return 0;
}

主线程:负责建立连接。
子线程:负责与客户端的数据交互。

ps:图片来自华清远见

 

 

#include <stdio.h>
#include <sys/types.h>	       /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BACKLOG 10

void *recv_send(void *arg)
{
	int cfd=*(int *)arg;
	char buf[128] = {0};
	ssize_t recv_bytes, send_bytes;
	while(1)
	{
		memset(buf,0,sizeof(buf));
		//接收客户端的信息储存到buf中
		recv_bytes = recv(cfd, buf, sizeof(buf),0);
		if(recv_bytes == -1)
		{
			perror("recv");
			break;
		//客户端退出返回0,服务器这里也退出循环
		}else if(recv_bytes == 0)
		{
			printf("client shutdown\n");
			break;
		}
		printf("buf: %s\n",buf);
		//将收到的数据原样返回给发送的客户端
		send_bytes = send(cfd, buf, recv_bytes, 0);
		if(send_bytes == -1)
		{
			perror("send");
			break;
		}

	}
	//用完了就关掉
	pthread_exit(NULL);
}
int main(int argc, char **argv)
{
	pthread_t tid;
	int ret;
	int sfd,cfd;
	struct sockaddr_in src,cli;
	socklen_t len = sizeof(src);
	socklen_t addrlen = sizeof(cli);

	//创建套接字,第二个参数为流式套接字类型
	sfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sfd == -1)
	{
		perror("socket fail");
		return -1;
	}
	
	//服务器地址结构体填充,地址族,IP PORT
	src.sin_family = AF_INET;
	src.sin_port = htons(atoi(argv[2]));
	src.sin_addr.s_addr = inet_addr(argv[1]);
	//绑定套接字,服务器ip和port口号
	ret = bind(sfd, (const struct sockaddr *)&src, len );
	if(ret == -1)
	{
		perror("bind");
		return -1;
	}
	//将套接字设定为被动监听状态,监听客户端的连接请求,10为未决队列长度
	ret = listen(sfd, BACKLOG);
	if(ret == -1)
	{
		perror("listen");
		return -1;
	}

	while(1)
	{
		//接收客户端连接请求,产生连接套接字,用于和客户端通信,通过cli保存客户端ip地址和port口号
		cfd = accept(sfd, (struct sockaddr *)&cli,&addrlen);
		if(cfd == -1)
		{
			perror("accept");
			return -1;
		}
		printf("client IP: %s  PORT: %d\n", inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));//打印客户端IP 和 PORT ,需要将网络字节序转换为主机字节序

		//创建子线程,负责和客户端交互
		ret=pthread_create(&tid,NULL,recv_send,(void *)&cfd);
		if(ret!=0)
		{
			errno=ret;
			perror("tid wu");
			return -1;
		}
		pthread_detach(tid);//将线程标记分离
	}
	return 0;
}

TCP客户端代码

TCP客户端设计流程:

1.建立socket套接字。

2.连接服务器,发出连接请求。

3.数据读写。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc, const char *argv[])
{
	int ret;
	int sfd;
	ssize_t recv_bytes,send_bytes;
	char buf[128]={0};
	struct sockaddr_in src;
	socklen_t len=sizeof(src);

	if(argc<3)
	{
		perror("argc wu");
		return -1;
	}

	sfd=socket(AF_INET,SOCK_STREAM,0);//流式套接字
	if(sfd==-1)
	{
		perror("socket wu");
		return -1;
	}

	src.sin_family=AF_INET;
	src.sin_port=htons(atoi(argv[2]));
	src.sin_addr.s_addr = inet_addr(argv[1]);
	//连接服务器发出连接请求
	ret = connect(sfd,(const struct sockaddr*)&src,len);
	if(ret==-1)
	{
		perror("connect wu");
		return -1;
	}
		
	while(1)
	{
		memset(buf,0,sizeof(buf));
		fgets(buf,sizeof(buf),stdin);
		//客户端获取屏幕终端的数据,发送数据给服务器
		send_bytes = send(sfd,buf,strlen(buf),0);
		if(send_bytes == -1)
		{
			perror("send wu");
			return -1;
		}
	}
	return 0;
}

以上是今天在华清远见学习到的关于TCP多客户端与服务器连接的代码。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值