【linux网络编程学习笔记】第二节:创建TCP通信(双向)(socket、bind、listen、accept、connect、recv、send、shutdown、server\client)

Work won't kill but worry will.

劳动无害,忧愁伤身。

上一篇章中创建了TCP的客户端的服务器,但是只能单向发送,本章节主要讲解如何进行双向互发消息,实现的过程很简单,看过上一阶段的章节就不难发现可以使用线程可,进行一遍发送一遍接收。

     废话不多说直接上例程

TCP相关API说明点击跳转

线程相关说明点击跳转

tcp_server.c 服务端

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


void *send_thread(void *arg)
{
	int client_fd = *(int *)arg;
	char buffer[1024];
	ssize_t send_size;

	while(1)
	{
		bzero(buffer, sizeof(buffer));
		scanf("%s",buffer);
		/*
			接受来自与客户端的数据,这个接受具备阻塞特性
			跟read函数差不多,都是指定从client_fd里面读取sizeof(buffer)长的数据放到buffer当中,0则代表按照默认操作读取数据
		*/
		send_size = send(client_fd, buffer, strlen(buffer), 0);
		if(send_size == -1)
		{
			perror("发送数据异常");
			break;
		}
	}


	return NULL;
}

int main(void)
{
	char buffer[1024];
	int skt_fd;
	int client_fd;
	int retval;
	ssize_t recv_size;
	/*
		获取程序通信的套接字(接口)
		AF_INET:IPV4的协议
		SOCK_STREAM:指定TCP协议
		0:代表不变化协议内部(ip手册中指定的参数)
	*/
	skt_fd = socket( AF_INET, SOCK_STREAM, 0);
	if(skt_fd == -1)
	{
		perror("申请套接字失败");
		return -1;
	}

	//绑定自己的地址信息
	struct sockaddr_in native_addr;
	//指定引用IPV4的协议
	native_addr.sin_family = AF_INET;
	//指定端口号,转化为网络字节序(大端序)
	native_addr.sin_port = htons(6666);
	//将所有的IP地址转化为二进制的网络字节序的数据进行绑定
	native_addr.sin_addr.s_addr = htonl(INADDR_ANY);


	/*
		将指定的地址信息及本程序的套接字绑定在一起
		skt_fd:套接字的文件描述符
		&native_addr:需要绑定的地址信息结构体(每个协议的地址信息结构体是不一样的,
		我们用IPV4的协议便需要引用struct sockaddr_in)
		sizeof(native_addr):传入的结构体长度
	*/
	retval = bind( skt_fd, (struct sockaddr *)&native_addr, sizeof(native_addr));
	if(retval == -1)
	{
		perror("绑定套接字地址失败");
		close(skt_fd);
		return -1;
	}


	/*
		设置套接字的同时通信最大连接数为50,并且将这个套接字的属性设置为可监听属性
	*/
	retval = listen(skt_fd, 50);
	if(retval == -1)
	{
		perror("设置最大连接数失败");
		close(skt_fd);
		return -1;
	}

	//用于接收客户端的地址信息
	struct sockaddr_in client_addr;
	socklen_t sklen = sizeof(client_addr);

	/*
		等待客户端链接,链接成功后返回一个代表客户端通信的文件描述符,具备阻塞特性
		skt_fd:代表套接字接口
		client_addr:链接成功后客户端的地址信息会存放到这里面
		sklen:代表结构体的长度
	*/
	client_fd = accept(skt_fd, (struct sockaddr *)&client_addr, &sklen);
	if(client_fd == -1)
	{
		perror("客户端链接失败");
		close(skt_fd);
		return -1;
	}

	printf("服务器:客户端连接成功\n");
	printf("客户端信息:\n客户端IP为%s,端口号为%hu\n",
	 	inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

	//创建发送的子线程
	pthread_t tid;
	pthread_create(&tid, NULL, send_thread, &client_fd);

	while(1)
	{
		bzero(buffer,sizeof(buffer));
		/*
			接受来自与客户端的数据,这个接受具备阻塞特性
			跟read函数差不多,都是指定从client_fd里面读取sizeof(buffer)长的数据放到buffer当中,0则代表按照默认操作读取数据
		*/
		recv_size = recv(client_fd,buffer,sizeof(buffer),0);
		if(recv_size == -1)
		{
			perror("接受数据异常");
			close(skt_fd);
			close(client_fd);
			return -1;
		}else if(recv_size == 0)//代表客户端断开连接
			break;
			
		printf("接收到来自与客户端%ld个字节的数据:%s\n", recv_size, buffer);
	}


	close(client_fd);//关闭客户端通信
	close(skt_fd);//关闭服务器的socket资源

	return 0;
}

tcp_client.c 客户端

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


void *recv_data(void *arg)
{
	int skt_fd = *(int *)arg;
	char buffer[1024];
	ssize_t recv_size;

	while(1)
	{
		bzero(buffer, sizeof(buffer));

		/*
			接受来自与客户端的数据,这个接受具备阻塞特性
			跟read函数差不多,都是指定从client_fd里面读取sizeof(buffer)长的数据放到buffer当中,0则代表按照默认操作读取数据
		*/
		recv_size = recv(skt_fd, buffer, sizeof(buffer), 0);
		if(recv_size == -1)
		{
			perror("接受数据异常");
			break;
		}
		else if(recv_size == 0)//代表服务器断开连接
		{
			close(skt_fd);
			return 0;
		}
		printf("接收到来自与客户端%ld个字节的数据:%s\n", recv_size, buffer);
	
	}

	return NULL;

}


//./client 192.168.33.3
int main(int argc, const char *argv[])
{
	char buffer[1024];
	int skt_fd;
	int retval;
	ssize_t send_size;

	/*
		获取程序通信的套接字(接口)
		AF_INET:IPV4的协议
		SOCK_STREAM:指定TCP协议
		0:代表不变化协议内部(ip手册中指定的参数)
	*/
	skt_fd = socket( AF_INET, SOCK_STREAM, 0);
	if(skt_fd == -1)
	{
		perror("申请套接字失败");
		return -1;
	}

	//服务器的地址信息
	struct sockaddr_in srv_addr;
	//指定引用IPV4的协议
	srv_addr.sin_family = AF_INET;
	//指定端口号,转化为网络字节序(大端序)
	srv_addr.sin_port = htons(6666);
	//将所有的IP地址转化为二进制的网络字节序的数据进行绑定	
	srv_addr.sin_addr.s_addr = inet_addr("192.168.32.68");

	retval = connect(skt_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
	if(retval == -1)
	{
		perror("客户端连接到服务器失败\n");
		close(skt_fd);
		return -1;
	}

	printf("客户端:连接服务器成功\n");

	//创建接收的子线程
	pthread_t tid;
	pthread_create(&tid, NULL, recv_data, &skt_fd);

	while(1)
	{
		scanf("%s", buffer);

		send_size = send( skt_fd, buffer, strlen(buffer), 0);
		if(send_size == -1)
			break;
	}


	close(skt_fd);

	return 0;
}

 


版权声明:本文为qq_44796935原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。