Socket

socket

1、一个socket里有一个相关描述(协议,本地地址,本地端口,远程地址,远程端口)。

2、(1)创建socket(相当于一个结构体,里面有很多成员,它创建在内核种):#include<sys/socket.h>
		int socket(int domain,int type,int protocol);//若创建成功则返回一个文件描述符,出错返回-1
	(2)socket的参数
	domain:AF_INET IPv4因特网域
			AF_INET6 IPv6因特网域
			AF_UNIX unix域
			AF_UNSPEC 未指定
	protocol:
			通常为0,默认的协议
	type:
			SOCK_STREAM:表tcp使用tcp协议进行传输
			SOCK_DGRAM:表udp
			SOCK_RAW:原始套接字允许对底层协议进行直接访问
			SOCK_SEQPACKET:
3、(1)字节序:表示数据的多个字节的高字节和低字节在前还是在后的问题。大断字节:高字节在前,低字节在后,相反的叫小端字节序。
	(2)网络协议(即在网络种传输时)使用的是大端字节序。
	(3)字节序的转换函数:
			htonl():将32位的主机字节序转换为网络字节序
			htons():将16位的主机字节序转换为网络字节序
			ntohl():将32位的网络字节序转换为主机字节序
			ntohl():将32位的网络字节序转换为主机字节序
4、(1)通用地址结构(一般不用)
	struct sockaddr{
			unsigned short sa_family;//即domain AF_xxx
			char sa_data[14];//包含远程地址、端口和套接字的数目等
	
				}
	(2)因特网地址结构
		struct in_addr{
			in_addr_t s_addr;//ipv4地址
			}
		
		struct sockaddr_in{
						short int sin_family;//AF_xxx(主机字节序)
						unsigned short int sin_port;//16位的端口号(网络字节序)
						struct in_addr sin_addr;//ipv4地址(网络字节序)
						unsigned char sin_zero[8];//格式对齐补充位

							}(此结构体更常用)
5、IPV4网络字节序与点分十进制的转换函数
	#include<arp/inet.h>
	const char *inet_ntop(int domain,const void *restrict addr,char *restrict str,socklen_t size);//将网络字节序转换成点分十进制。成功返回地址字符串指针,出错返回NULL
	int inet_pton(int domain,const char *restrict str,void *restrict addr);//点分十进制转换为网络字节序,成功返回1,无效格式返回0,出错返回-1

	参数:
		domain:AF_INET
		addr:32为ipv4网络字节序
		str:十进制的地址
		size:地址字符串大小
6、例
	struct sockaddr_in sin;//定义一个sockaddr_in结构体
	char buf[16];
	//填充定义的结构体
	memset(&sin,0,sizeof(sin));//将定义的结构体清零
	sin.sin_family=AF_INET;//填充sin_family字段
	sin.sin_port=htons(3001);//填充端口号,并将其变为网络字节序
	if(inet_pton(AF_INET,"192.168.2.1",&sin.sin_addr.s_addr)<=0)//填充地址字段,并把10进制的地址转换为网络字节序
	{
		//错误的
	}
	printf("%s\n",inet_ntop(AF_INET,&sin.sin_addr.s_addr,buf,sizeof(buf)));//又把填充的网络地址转换为10进制输出出来
	7、服务器端和客户端的流程
(1)服务器端:调用socket函数创建套接字,调用bind绑定本地地址和端口,调用listen启动监听,调用accept从已连接队列中提取客户连接,调用I/O函数(read/write)与客户端通讯,调用close关闭套接字
(2)调用socket函数创建套接字,调用connect连接服务器端,调用I/O函数(read/write)与服务器端通信,调用close关闭套接字

8、一个服务器端的程序
	#include<netdb.h>
	#include<sys/socket.h>
	#include<unistd.h>
	#include<string.h>
	#include<stdio.h>
	#include<stdlib.h>
	#include<memory.h>
	#include<signal.h>
	#include<time.h>

	//用于捕获ctr+c信号,若捕获到则终止程序
	void sig_handler(int signo)
	{
		if(signo=SIGINT)//SIGINT关联ctr+c
		{
			printf("server close\n");
			close(sockfd);//关闭服务器的socket
			exit(1);
		}
	}

	//输出客户端的相关信息
	void out_addr(struct sockaddr_in *clientaddr)
	{
		int port=ntohs(clientaddr->sin_port);//将网络字节序转变为主机字节序
		char ip[16];
		memset(ip,0,sizeof(ip));
		inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip));//将ip地址从网络字节序转换成点分十进制
		printf("client:%s(%d) connected\n",ip,port);
			
	}		
	
	void do_service(int fd)
	{
		long t =time(0);//获得系统时间
		char *s=ctime(&t);//转换成字符串的形式
		size_t size=strlen(s)*sizeof(char);
		if(write(fd,s,size)!=size)//将获得的时间写回客户端中,fd针对某个客户端
		{
			perror("write error");//失败
		}

	}


	int sockfd;
	int main(int argc,char* argv[])	
	{
		if(argc<2)
		{
			printf("usage:%s #port\n",argv[0]);
			exit(1);
		}
			
		if(signal(SIGINT,sig_handler)==SIG_ERR)//捕获ctr+c信号
		{
			perror("signal sigint error");
			exit(1);
		}

		/*
		
		*/
		sockfd=socket(AF_INET,SOCK_STREAM,0);//传教一个套接字,选择IPV4,tcp协议,相当于创建了一个在内核中的结构体
		//定义地址结构并填充相关信息
		struct sockaddr_in serveraddr;
		memset(&serveraddr,0,sizeof(serveraddr));
		serveraddr.sin_family=AF_INET;
		serveraddr.sin_port=htons(atoi(argv[1]));//atoi()将输入的字符串型转换为整型,并将其转换为网络字节序,端口号由命令行提供
		serveraddr.sin_addr.s_addr=INADDR_ANY;//服务器端的地址,不设置固定的IP地址,所有网卡的IP地址都可以(即可以从所有网络获得数据),这里是主机字节序,要将其转换为网络字节序


		if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0)//将socket和地址(ip,端口)进行绑定.第二个参数将因特网地址转换为通用地址,bind要求的是通用地址,所有要进行转换	
		{
			perror("bind error");
			exit(1);
		}

		if(listen(sockfd,10)<0)
		{
			perror("listen error");
			exit(1);
		}//启动监听(指定port监听)通知系统去接受来自客户端的连接请求,将接受到的客户端的连接请求放置到对应的队列中,第二个参数就是队列的大小即可以放10个来自客户端的请求。
		

		struct sockaddr_in clientaddr;//用来存客户端的地址
		socklen_t clientaddr_len=sizeof(clientaddr);
		while(1)
		{

			//从上面的队列中选择一个请求进行连接,若队列中没有客户端连接他会阻塞在accept处,一直获得连接
			int fd=accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);//出错返回0,第二个参数可写为NULL,这是获得客户端的地址即长度,返回一个新的socket描述符。(与客户端相关,利用此描述符可以与客户端进行通信,即只要对此描述进行操作就可和客户端以全双工的方式进行通信,相当于I/O流)。
			if(fd<0)
			{
				perror("accept error");
				continue;//停止此次,继续从客户端获取
			}
			
			out_addr(&clientaddr);//显示客户端地址
			do_service(fd);//给客户端提供服务
			close(fd);//关闭某个客户端的socket


		}


		return 0;
	}


	9、客户端例子
	#include<netdb.h>
	#include<sys/socket.h>
	#include<unistd.h>
	#include<string.h>
	#include<stdio.h>
	#include<stdlib.h>
	#include<memory.h>
	#include<signal.h>
	#include<time.h>
	
	int main(int argc,char* arg[])
	{
		if(argc<3)
		{
			printf("arg error");
			exit(1);
			
		}
	
	
	int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建一个套接字
	if(sockfd<0)
	{
		perror("sockfd error");
		exit(1);
	}
	
	//创建一个地址并填充
	struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_port=htons(atoi(argv[2]));
	inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);

	if(connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0)//将套接字与地址结构相关联,并请求和服务器相连接
	{
		perror("connect error");
		exit(1);
	}

	char buffer[1024];
	memset(buffer,0,sizeof(buffer));
	size_t size;
	if((size=read(sockfd,buffer,sizeof(buffer)))<0)//从套接字中读取数据
	{
		perror("read error");
		exit(1);
	}
		
	if(write(STDOUT_FILENO,buffer,size)!=size)//将读取的数据打印在屏幕中
	{
		perror("write error");
		exit(1);
	}
		return 0;
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值