03网络编程基本模型及api的讲解

TCP客户/服务器模型

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

socket

socket函数
	包含头文件<sys/socket.h>
	功能:创建一个套接字用于通信
	原型
	int socket(int domain, int type, int protocol);
	参数
	domain :指定通信协议族(protocol family)
	type:指定socket类型,流式套接字SOCK_STREAM,数据报套接字SOCK_DGRAM,原始套接字SOCK_RAW
	protocol :协议类型
	返回值:成功返回非负整数, 它与文件描述符类似,我们把它称为套接口描述字,简称套接字。失败返回-1

bind

bind函数
	包含头文件<sys/socket.h>
	功能:绑定一个本地地址到套接字
	原型
	int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
	参数
	sockfd:socket函数返回的套接字
	addr:要绑定的地址
	addrlen:地址长度
	返回值:成功返回0,失败返回-1

listen

listen函数
	一般来说,listen函数应该在调用socket和bind函数之后,调用函数accept之前调用。
	对于给定的监听套接口,内核要维护两个队列:
	1、已由客户发出并到达服务器,服务器正在等待完成相应的TCP三路握手过程
	2、已完成连接的队列

在这里插入图片描述
在这里插入图片描述

accept

accept函数
	包含头文件<sys/socket.h>
	功能:从已完成连接队列返回第一个连接,如果已完成连接队列为空,则阻塞。
	原型
	int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
	参数
	sockfd:服务器套接字
	addr:将返回对等方的套接字地址
	addrlen:返回对等方的套接字地址长度
	返回值:成功返回非负整数,失败返回-1

connect

connect函数
	包含头文件<sys/socket.h>
	功能:建立一个连接至addr所指定的套接字
	原型
	int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
	参数
	sockfd:未连接套接字
	addr:要连接的套接字地址
	addrlen:第二个参数addr长度
	返回值:成功返回0,失败返回-1

Socket API 中的地址复用

SO_REUSEADDR
	服务器端尽可能使用SO_REUSEADDR
	在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项。
使用SO_REUSEADDR选项可以使得不必等待TIME_WAIT状态消失就可以重启服务器


2client.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>


/*
IPV4国际套接字地址
struct sockaddr_in {
   sa_family_t    sin_family; // address family: AF_INET
   in_port_t      sin_port;   // port in network byte order
   struct in_addr sin_addr;   // internet address 
};

 //Internet address.
struct in_addr {
   uint32_t       s_addr;     // address in network byte order
};

长连接与短连接客户端说的算!
*/

//创建一个TCP协议的客户端
//长连接
#if 0
void test()
{
	int sockfd = 0;
	const char *serverip = "192.168.66.128";
	
	//创建socket
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1)
	{
		perror("socket()");
		exit(0);
	}
	//定义socket结构体 man 7 ip
	
	struct sockaddr_in srvsddr;
	srvsddr.sin_family = AF_INET;
	srvsddr.sin_port = htons(8001);//转化为网络字节序
	//第一种
	#if 0
	srvsddr.sin_addr.s_addr = inet_addr(serverip);
	#endif
	//第二种
	#if 0
	//srvsddr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY 就是0.0.0.0 不存在网络字节序
	//srvaddr.sin_addr.s_addr = inet_addr(INADDR_ANY); //绑定本机的任意一个地址
	#endif
	//第三种
	//建议使用这种
	#if 1
	int ret;
	ret = inet_pton(AF_INET, serverip, &srvsddr.sin_addr);
	if (ret == 0)
	{
		printf("%s is invalid\n", serverip);
		return;
	}	
	#endif
	//进程-》内核
	if( connect(sockfd, (struct sockaddr *)&srvsddr,sizeof(srvsddr)) < 0)
	{
		perror("connect()");
		exit(0);
	}

	char recvbuf[1024] = {0};
	char sendbuf[1024] = {0};	
	//char *fgets(char *s, int size, FILE *stream); 从stream 读取size-1大小的数据存入s,最后加'\0'
	while( fgets(sendbuf, sizeof(sendbuf), stdin) != NULL )
	{
		//向服务器写数据
		//ssize_t write(int fd, const void *buf, size_t count);
		// 从buf中读取count大小的数据存入文件描述符为fd的文件中。
		write(sockfd, sendbuf, strlen(sendbuf));
		//ssize_t read(int fd, void *buf, size_t count);
		//从文件描述符为fd的文件中读取大小为count的数据存入buf中。
		read(sockfd, recvbuf, sizeof(recvbuf));
		fputs(recvbuf, stdout);//从服务器收到数据,打印屏幕
		
		//清空缓冲区
		memset(recvbuf, 0, sizeof(recvbuf));
		memset(sendbuf, 0, sizeof(sendbuf));	
	}
	close(sockfd);
	return;	
}

#endif

//短连接
#if 1
void  test()
{

	int i = 0;
    //创建多个socket发送信息
	for (i = 0; i < 10; i++)
	{
		int sockfd = 0;
		sockfd = socket(PF_INET, SOCK_STREAM, 0);
		if (sockfd == -1)
		{
			perror("fun socket\n");
			exit(0);
		}

		struct sockaddr_in srvaddr;
		srvaddr.sin_family = AF_INET;
		srvaddr.sin_port = htons(8001);
		srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //127.0.0.1
		//srvaddr.sin_addr.s_addr = inet_addr(INADDR_ANY); //绑定本机的任意一个地址 

		if (connect(sockfd, (struct sockaddr*) (&srvaddr), sizeof(srvaddr)) < 0)
		{
			printf("errno:%d \n", errno);
			perror("fun socket\n");
			exit(0);
		}

		char revbuf[1024] = { 0 };
		char sendbuf[1024] = { 0 };
	
		sprintf(sendbuf, "i:%d\n", i);
		//向服务写数据
		write(sockfd, sendbuf, strlen(sendbuf)); //服务器端回发信息
		//从服务器读数据
		read(sockfd, revbuf, sizeof(revbuf));
		fputs(revbuf, stdout); //从服务器收到数据,打印屏幕

		memset(revbuf, 0, sizeof(revbuf));
		memset(sendbuf, 0, sizeof(sendbuf));
		
		close(sockfd);
	}
}
#endif

int main()
{
	test();
	return 0;
}

2server.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>


/*
IPV4国际套接字地址
struct sockaddr_in {
   sa_family_t    sin_family; // address family: AF_INET
   in_port_t      sin_port;   // port in network byte order
   struct in_addr sin_addr;   // internet address 
};

 //Internet address.
struct in_addr {
   uint32_t       s_addr;     // address in network byte order
};


*/

//创建一个TCP协议的服务器
#if 1
void test()
{
	int sockfd = 0;
	const char *serverip = "192.168.66.128";
	
	//创建socket
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1)
	{
		perror("socket()");
		exit(0);
	}
	//定义socket结构体 man 7 ip
	
	struct sockaddr_in srvsddr;
	srvsddr.sin_family = AF_INET;
	srvsddr.sin_port = htons(8001);//转化为网络字节序
	//第一种
	#if 0
	srvsddr.sin_addr.s_addr = inet_addr(serverip);
	#endif
	//第二种
	#if 0
	//srvsddr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY 就是0.0.0.0 不存在网络字节序
	//srvaddr.sin_addr.s_addr = inet_addr(INADDR_ANY); //绑定本机的任意一个地址
	#endif
	//第三种
	//建议使用这种
	#if 1
	int ret;
	ret = inet_pton(AF_INET, serverip, &srvsddr.sin_addr);
	if (ret == 0)
	{
		printf("%s is invalid\n", serverip);
		return;
	}	
	#endif
	
	//设置端口复用
	//使用SO_REUSEADDR选项可以使得不必等待TIME_WAIT状态消失就可以重启服务器
	int optval = 1;
    if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)
	{
		perror("getsockopt()");
		exit(0);
	}
	
	//从进程->内核 将结构首地址和该结构大小都传递给了内核
	//内核知道需要从进程复制多少数据进来
	
	if(bind(sockfd, (struct sockaddr *)&srvsddr,sizeof(srvsddr)) <0 )
	{
		perror("bind()");
		exit(0);
	}
	//一但调用listen函数,这个套接字sockfd将变成被动套接字,只能接受连接,
	//不能主动的发送连接
	//listen 做了两个队列。。。。。。
	// 队列由内核管理,一部分是完成三次握手的,一部分是没有完成三次握手的。
	if(listen(sockfd, SOMAXCONN) < 0)
	{
		perror("listen()");
		exit(0);
	}
	
	/*
		值-结果参数:传入结构体的首地址和结构体的长度,这样内核
		在写该结构不至于越界;当函数返回时,结构大小又是一个结果
		
	*/
	
	struct sockaddr_in peeraddr;
	unsigned int conn = 0;
	socklen_t peerlen = sizeof(peeraddr);//值-结果参数
	
	//内核到进程
	//accept接受已经完成三次握手的链接,没有链接会阻塞直到有链接
	//sockfd监听套接字 conn连接套接字
	conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
	if (-1 == conn) 
	{
		perror("accept()");
		exit(0);
	}

	printf("传入后 peerlen = %d\n", peerlen);
	#if 1
	printf("客户端的ip:%s port:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
	#endif
	
	#if 1
	char ip[16] = {0};
	socklen_t size = sizeof(ip);
	if (ip != NULL)
	{
		//inet_ntop(int af, const void *src,char *dst, socklen_t size)
		inet_ntop(AF_INET, (void *)&peeraddr.sin_addr.s_addr, (char *)ip, size);
	}
	if (errno == ENOSPC)
	{
		perror("分配的内存size不够");
		return;
	}
	printf("客户端的ip:%s port:%d\n", ip, ntohs(peeraddr.sin_port));
	
	#endif
	
	char recvbuf[1024] ={0};
	while(1)
	{
		memset(recvbuf, 0, sizeof(recvbuf));
		int ret = read(conn, recvbuf, sizeof(recvbuf));
		if (ret == 0) 
		{
			printf("对方已经关闭\n");
			exit(0);
		}
		else if (ret < 0)
		{
			perror("读取数据失败\n");
			exit(0);
		}
		fputs(recvbuf, stdout);//服务器收到数据,打印到屏幕
		write(conn, recvbuf, ret);	//将收到的数据再发给客户端
	}
	
	return;	
}

#endif


int main()
{
	test();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值