c语言聊天室Tcp通信出现的问题

本文总结了多客户机通信的基本原理和技术细节,包括服务器和客户机的建立过程、线程管理、TCP三次握手和四次挥手的过程优化、结构体解析等关键技术点。

多客户机通信的小结:

/***********************************/

服务器需要调用的函数:socket,bind,listen,accept

bind把服务器和sockfd捆绑,accept是listen之后有客户机请求连接,返回客户的信息fd,服务器就是对fd进行send和recv

客户机需要调用的函数:socket,connect

先connect 客户机去连接服务器,连接之后是利用客户机socket函数的返回值sockfd进行通信


/***********************************/

connectt和bind之前要

 memset(&server_addr, 0, sizeof(server_addr));
 server_addr.sin_family = PF_INET;
 server_addr.sin_port = htons(PORT);


/*****************************/

Tcp为了数据的准确性会进行多次握手和挥手这期间需要时间,在socket之后,有了下面的函数,不会出现下一次运行程序要等待的情况

 int opt = 1;
 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

/*************************/

注意创建线程的时候传参的问题


/*********************/

 /*
  struct sockaddr_in{
   shaor sin_family;                 //地址族
   u_short sin_port;     //端口
   struct in_addr sin addr;   //IP地址
   char sin_size[8];                   //用于补充,使得结构体和struct sockaddr大小相同
  };
  
  struct in_addr{
   in_addr_t s_addr;
  
  };
 */


/*******************************/

创建多个客户机的时候在

在listen函数之前加while(1)死循环,但是但是由于pthread_join函数阻塞,

只有与第一个客户机通信线程运行结束的时候才能进入下一次循环,创建新的客户机,这样就不符合常理,不能实现多个客户机的通信


调用函数pthread_detach(pthread_self());可以让线程运行结束的时候自行回收资源,去掉pthread_join函数


/*************************************/

服务器先接受客户机发送的结构体,之后解析结构体info.fd知道是要发送到哪一个客户机,之后只要把相应的text发送过去就行


客户机先录入要发送给谁,发送什么内容,把整个结构体发送过去

客户机接收的时候,只需要一个buf用来接收,不需要对结构体进行操作,因为发送的客户机经过服务器已经把结构体解析了,服务器发送过来的就是text




1.

客户机和服务器聊天出现的问题

void *client_send(void *arg)函数内忘了scanf("%s", buf);程序没有输入的语句;

memset(buf, 0, sizeof(buf));对buf进行了&操作;

 if((!strncmp(buf,"exit",4)))忘了!程序编译的时候好像就出错了
server_addr.sin_family = PF_INET;把server_addr写成sockaddr_in编译报错没有sockaddr_in,找错好长时间

printf("waiting...\n");忘了\n运行时发送有问题

/**********sever.c****************/

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

#define PORT  2222

pthread_t {
	int ret;
	char buf[100] = {0};

	while (1)
	{
		memset(buf, 0, sizeof(buf));
		scanftid1, tid2;

void *client_send(void *arg)
("%s", buf);
		ret = send((int)arg, buf, sizeof(buf), 0);
		if (ret < 0)
		{
			perror("send");
			exit(1);
		}
		if (!strncmp(buf, "exit", 4))
		{
			pthread_cancel(tid2);
			return (void *)0;
		}
	}

	return (void *)0;
}

void *client_recv(void *arg)
{
	char buf[100] = {0};
	int ret;

	while (1)
	{
		ret = recv((int)arg, buf, sizeof(buf), 0);	
		if (ret < 0)
		{
			perror("recv");
			exit(1);
		}
		if (!strncmp(buf, "exit", 4))
		{
			pthread_cancel(tid1);
			return (void *)0;
		}
		printf("\t\t\t%s\n", buf);
	}

	return (void *)0;
}

int main()
{
	int sockfd, ret, addr_len, fd;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	void *thread_ret;

	printf("Start server!\n");

	sockfd = socket(PF_INET, SOCK_STREAM, 0); 
	if (-1 == sockfd)
	{
		perror("socket");
		exit(1);
	}

	int opt = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = PF_INET;
	server_addr.sin_port = htons(PORT);
	//server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	server_addr.sin_addr.s_addr = INADDR_ANY;
	ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if (-1 == ret)
	{
		perror("bind");
		exit(1);
	}

	printf("Waiting...\n");
	while (1)
	{
		ret = listen(sockfd, 5);
		if (-1 == ret)
		{
			perror("listen");
			exit(1);
		}

		memset(&client_addr, 0, sizeof(client_addr));
		addr_len = sizeof(client_addr);
		fd = accept(sockfd, (struct sockaddr *)&client_addr, &addr_len);
		if (-1 == ret)
		{
			perror("accept");
			exit(1);
		}

		printf("accept success!\n");
		pthread_create(&tid1, NULL, client_send, (void *)fd);
		pthread_create(&tid2, NULL, client_recv, (void *)fd);
		/*pthread_join(tid1, &thread_ret);
		pthread_join(tid2, &thread_ret);*/
	}

	close(fd);
	close(sockfd);
	
	return 0;
}



/************client.c************/


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

#define PORT 2222

pthread_t tid1, tid2;

void *client_send(void *arg)
{
    int ret;
	char buf[100] = {0};

	while (1)
	{
		memset(buf, 0, sizeof(buf));
		scanf("%s", buf);
		ret = send((int)arg, buf, sizeof(buf), 0);
		if (ret < 0)
		{
			perror("send");
			exit(1);
		}
		if (!strncmp(buf, "exit", 4))
		{
			pthread_cancel(tid2);
			return (void *)0;
		}
	}

	return (void *)0;
}

void *client_recv(void *arg)
{
    char buf[100] = {0};
	int ret;

	while (1)
	{
		ret = recv((int)arg, buf, sizeof(buf), 0);
		if (ret < 0)
		{
			perror("recv");
			exit(1);
		}
		if (!strncmp(buf, "exit", 4))
		{
			pthread_cancel(tid1);
			return (void *)0;
		}

		printf("\t\t\t%s\n", buf);
	}

	return (void *)0;
}

int main(int argc, char *argv[])
{
	int ret, sockfd;
	struct sockaddr_in server_addr;
	void *thread_ret;

	sockfd = socket(PF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("socket");
		exit(1);
	}

	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = PF_INET;
	server_addr.sin_port = htons(PORT);
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if (-1 == ret)
	{
		perror("connect");
		exit(1);
	}
	
	pthread_create(&tid1, NULL, client_send, (void *)sockfd);
	pthread_create(&tid2, NULL, client_recv, (void *)sockfd);
	pthread_join(tid1, &thread_ret);
	pthread_join(tid2, &thread_ret);

	return 0;
}



















2/********************************/

多个客户机和服务器通信,但是服务器发的消息只能到达第一个客户机,所有的客户机发送的消息服务器都能收到


server.c


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

#define PORT 2222
pthread_t tid1,tid2;
void *client_send(void * arg)
{
	char buf[100] = {0};
	int ret;
	pthread_detach(pthread_self());                 //使用之后自动回收内存
	while(1)
	{
		memset(buf,0,sizeof(buf));
		scanf("%s",buf);
		/*send参数1指定发送端套接字描述符	2.应用程序要发送的数据的缓冲区
					3.实际要发送的数据字节数	4一般是0*/
		ret = send((int)arg,buf,sizeof(buf),0);       //从客户端发送数据到buf
		if(-1 == ret)
		{
			perror("send");
			exit(1);
		}
		/*****************************/
		if(!strncmp(buf,"exit",4))
		{
			pthread_cancel(tid2);
			return (void*)0;
		}		
	}
}

void *client_recv(void *arg)
{
	char buf[100] = {0};
	int ret;
	pthread_detach(pthread_self());
	while(1)
	{
		/*不管服务器还是客户都要recv函数从tcp链接的另一端接收数据
		参数 	1.指定接收端套接字描述符	2。指明缓冲区,用来存放recv接收到的数据
				3.指明buf的长度				4.一般设置0
		*/
		ret = recv((int)arg,buf,sizeof(buf),0);         //服务器端接收数据
		if(-1 == ret)
		{
			perror("recv");
			exit(1);
		}
				/*****************************/
		if(!strncmp(buf,"exit",4))
		{
			pthread_cancel(tid1);
			return (void*)0;
		}
		printf("\t\t\t%s\n",buf);
	}
}

int main()
{
	int ret,addr_len;
	int fd,sockfd;
	struct sockaddr_in server_addr;               //结构体
	struct sockaddr_in client_addr;                 //结构体
	//pthread_t tid1,tid2;
	void * pthread_ret;
	/*socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符,
	应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1
	*/
	sockfd = socket(PF_INET,SOCK_STREAM,0);			//sockfd网络文件描述符
	if(-1 == sockfd)						
	{
		perror("socket");
		exit(1);
	}
	
	/*bind()的作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符
	监听myaddr所描述的地址和端口号
	将将服务器的ip地址,端口等信息和sockfd绑定
	*/
	memset(&server_addr,0,sizeof(server_addr));
	server_addr.sin_family = PF_INET;
	server_addr.sin_port = htons(PORT);
	//server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	server_addr.sin_addr.s_addr = INADDR_ANY;
	
	ret = bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr));
	if(-1 == ret)
	{
		perror("bind");
		exit(1);
	}
	
	printf("waiting...\n");
	while(1)
	{
		ret = listen(sockfd,5);
		if(-1 == ret)
		{
			perror("listen");
			exit(1);
		}
	
	
		/* 三方插手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,
			就阻塞等待直到有客户端连接上来,cliaddr是一个传出参数,accept()返回时传出客户端的地址和端口号*/
		memset(&client_addr,0,sizeof(client_addr));
		addr_len = sizeof(client_addr);

		fd = accept(sockfd,(struct sockaddr*)&client_addr,&addr_len);   //fd客户端的地址和端口号
		if(-1 == fd)
		{
			perror("accept");
			exit(1);
		}
		printf("accept!\n");

		pthread_create(&tid1,NULL,client_send,(void*)fd);                        //last canshu NULL ?
		pthread_create(&tid2,NULL,client_recv,(void*)fd);
	}
	//pthread_join(tid1,&pthread_ret);
	//pthread_join(tid2,&pthread_ret);


	return 0;
}



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


#define PORT 2222
int fd,sockfd;
pthread_t tid1,tid2;
void *client_send(void * arg)
{
	char buf[100] = {0};
	int ret;
	while(1)
	{
		memset(buf,0,sizeof(buf));
		scanf("%s",buf);
		ret = send((int)arg,buf,sizeof(buf),0);       //first canshu
		if(-1 == ret)
		{
			perror("send");
			exit(1);
		}
		/*****************************/
		if(!strncmp(buf,"exit",4))
		{
			pthread_cancel(tid2);
			return (void*)0;
		}			
	}
}

void *client_recv(void *arg)
{
	char buf[100] = {0};
	int ret;
	while(1)
	{
		ret = recv((int)arg,buf,sizeof(buf),0);         //first canshu
		if(-1 == ret)
		{
			perror("recv");
			exit(1);
		}
						/*****************************/
		if(!strncmp(buf,"exit",4))
		{
			pthread_cancel(tid1);
			return (void*)0;
		}
		printf("\t\t\t%s\n",buf);
	}
}

int main()
{
	int ret,addr_len;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
//	pthread_t tid1,tid2;
	void * pthread_ret;
	sockfd = socket(PF_INET,SOCK_STREAM,0);
	if(-1 == sockfd)
	{
		perror("socket");
		exit(1);
	}
	
	memset(&server_addr,0,sizeof(server_addr));
	server_addr.sin_family = PF_INET;
	server_addr.sin_port = htons(PORT);
	server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	/*客户端需要调用connect()连接服务器,connect的参数是对方的地址.
		参数	1.客户端的套接字(表明即将发起连接请求) 2.服务器的套接字所在的地方(自我定义的专有名词)
				3.长度
		*/
				
	ret = connect(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr));//成功返回0
	if(-1 == ret)
	{
		perror("connect");
		exit(1);
	}
	
	/*memset(&client_addr,0,sizeof(client_addr));
	addr_len = sizeof(client_addr);

	fd = accept(sockfd,(struct sockaddr*)&client_addr,&addr_len);   //qiangzhuan _kenneg shaole
	if(-1 == fd)
	{
		perror("accept");
		exit(1);
	}*/

	//pthread_create(&tid1,NULL,client_send,(void*)fd);                        //last canshu NULL ?
	//pthread_create(&tid2,NULL,client_recv,(void*)fd);
	pthread_create(&tid1, NULL, client_send, (void *)sockfd);
	pthread_create(&tid2, NULL, client_recv, (void *)sockfd);
	pthread_join(tid1,&pthread_ret);
	pthread_join(tid2,&pthread_ret);


	return 0;
}








3

客户机和客户机的通信

 pthread_detach(pthread_self());把detach写成cancel

注意void *client(void *arg)函数内什么时候发送text什么发送整个结构体

!!!!!!!!!!!!!!!!!!!!i++;写到pthread_create(&tid1, NULL, client, (void *)fd[i]);前面导致客户机程序无限死循环,就连scanf都不执行

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

之前不要忘了                  memset(&server_addr, 0, sizeof(server_addr));
 server_addr.sin_family = PF_INET;
 server_addr.sin_port = htons(PORT);
 server_addr.sin_addr.s_addr = inet_addr(argv[1]);


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

#define PORT  2222
#define MAX_CLIENT 100

struct information {
	int fd;
	char text[100];
};

pthread_t tid1, tid2;

void *client(void *arg)
{
	int ret;
	int to;
	struct information info;

	pthread_detach(pthread_self());

	while (1)
	{
		memset(&info, 0, sizeof(info));
		ret = recv((int)arg, (void *)&info, sizeof(info), 0);
		if (ret < 0)
		{
			perror("send");
			exit(1);
		}
		
		to = info.fd;

		ret = send(to, (void *)info.text, sizeof(info.text), 0);
		if (ret < 0)
		{
			perror("send");
			exit(1);
		}
		/*if (!strncmp(buf, "exit", 4))
		{
			pthread_cancel(tid2);
			return (void *)0;
		}*/
	}

	return (void *)0;
}

/*void *client_recv(void *arg)
{
	char buf[100] = {0};
	int ret;

	pthread_detach(pthread_self());

	while (1)
	{
		ret = recv((int)arg, buf, sizeof(buf), 0);	
		if (ret < 0)
		{
			perror("recv");
			exit(1);
		}
		if (!strncmp(buf, "exit", 4))
		{
			pthread_cancel(tid1);
			return (void *)0;
		}
		printf("\t\t\t%s\n", buf);
	}

	return (void *)0;
}*/

int main()
{
	int sockfd, ret, addr_len;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	void *thread_ret;
	int i = 0;
	int fd[MAX_CLIENT] = {0};

	printf("Start server!\n");

	sockfd = socket(PF_INET, SOCK_STREAM, 0); 
	if (-1 == sockfd)
	{
		perror("socket");
		exit(1);
	}

	int opt = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = PF_INET;
	server_addr.sin_port = htons(PORT);
	//server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	server_addr.sin_addr.s_addr = INADDR_ANY;
	ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if (-1 == ret)
	{
		perror("bind");
		exit(1);
	}

	printf("Waiting...\n");
	while (1)
	{
		ret = listen(sockfd, 5);
		if (-1 == ret)
		{
			perror("listen");
			exit(1);
		}

		memset(&client_addr, 0, sizeof(client_addr));
		addr_len = sizeof(client_addr);
		fd[i] = accept(sockfd, (struct sockaddr *)&client_addr, &addr_len);
		if (-1 == ret)
		{
			perror("accept");
			exit(1);
		}
		printf("accept success fd = %d!\n", fd[i]);
		pthread_create(&tid1, NULL, client, (void *)fd[i]);
		i++;
	}

	close(fd);
	close(sockfd);
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值