标签:strlen 接受 lis 字节序 现在 描述 不用 ima har
下面所讲的是在Linux系统上用gcc编写的,Windows好像不能用。
socket是套接字,是用来在网络之间进行数据传递的。
在网络中,我们通过一个IP来确定一台主机,再通过一个port来确定一个进行,而socket就是一个进行。因此:socket = IP + port
网络传输的2种模式
C/S:server端与client端进行相互通信,好处是可以自行的定义。而且在首次登录后都不会浪费过多的流量等等。而其的坏处也是显而易见的,在安装在电脑上的客户端会对系统造成安全问题,开发量大等等。
而还有一种模型是:B/S,与C/S的缺点正好互补。在这里就只是简单的说下。
在讲到正题前,我们先来补充下几个知识点。我们先不用想是干什么用的,先记住就行了用处在下面会讲到。
socket传输类型:
1 .流式socket(SOCK_STREAM)
流式套接字使用的是TCP协议,由于TCP协议建立在三次握手的基础上,所以这种类型能够提供可靠的、面向连接的通信流,能够保证数据传输的正确性和顺序性。
2.数据报socket(SOCK_DGRAM)
数据报套接字使用的是UDP协议,由于UDP将数据扔出去之后就不管的桀骜特性,所以该类型定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。
3.原始socket
原始套接字允许对底层协议如IP或ICMP(在网络层,而TCP和UDP都在传输层)进行直接访问,功能比较强大但是使用不便,主要用于一些协议的开发。
socket指定的连接类型:
大端字节序与小端字节序
大端字节序在Linux诞生之初就已经沿用,因此在网络中传输的时候用的是大端字节序。而小端字节序是微软后来发明的,我们计算机内部的就是小端字节序,这里指的是Windows和Linux。因此我们在进行网络传输的时候要把IP和port 从小端字节序转换成大端字节序。什么是大小端:大端:就是高位的地址储存地位的值(或低位的地址储存高位的值),小端:高位的地址存储高位的值,低位的地址存储低位的值。
因此我们需要一些函数来进行大小端的转换。
#include <arpa/inet.h> //这是下面的端口转换的函数的头文件
数字转换,建立在传入的参数必须是数字的:
端口:
htons(要转换的端口(也可以是存放端口的变量)); //这个是把主机的端口转换成网络的,host to network short
ntohs(要转换的端口(也可以是存放端口的变量)); //这个是把网络的端口转换成主机的,network to host short
ip地址:
htonl(要转换的ip(也可以是存放ip的变量)); //这个是把主机的ip转换成是网络的ip
ntohl(要转换的ip(也可以是存放ip的变量)); //这个是把网络的ip转换成主机的ip
字符串转换:
ip地址:
inet_pton(socket连接类型,源字符串,输出结果); //这个是把主机的IP转换成网络的IP,ip to network
1 int inet_pton(int family,const char * strptr,void * addrptr); 2 返回:1--成功, 0--输入不是有效的表达格式 , -1--出错
inet_ntop(socket连接类型,结构体的IP地址的指针,接收的数组(char类型),接收数组的长度); //这个是把网络的ip转换成主机ip,network to ip
1 const char * inet_ntop(int family,const void * addrptr,char * strptr,size_t len); 2 其中len =sizeof(* strptr) 3 返回: 指向结果的指针--成功 , NULL--出错
结构体,我们这里说的结构体是指socket内置的结构体,用于存储IP的类型,IP和端口号的内置结构体。
socket的结构体有四种:sockaddr,sockaddr_in,sockaddr_un,socket_in6
sockaddr:最早的一种结构体,现在已经被淘汰掉了。但是在调用一些函数的时候还是需要这个类型的。
sockaddr_in:ipv4的一种结构体,也是我们要学习的重点。
sockaddr_un:unix的一种,不是用于网络传输的。
sockaddr_in6:和上面的sockaddr_in一样,但是这个是支持ipv6的。暂时不怎么需要用到。
在下面我只讲下:sockaddr 和 sockaddr_in 这两种类型的结构,剩下的两种我们暂时不需要。
1 struct sockaddr { 2 unsigned short sa_family; /* address family, AF_xxx */ 3 char sa_data[14]; /* 14 bytes of protocol address */ 4 };
1 struct sockaddr_in { 2 short int sin_family; /* Address family */ 3 unsigned short int sin_port; /* Port number */ 4 struct in_addr sin_addr; /* Internet address */ 5 unsigned char sin_zero[8]; /* Same size as struct sockaddr */ 6 }; 7 struct in_addr { 8 unsigned long s_addr; 9 };
我们通常用到的是sockaddr_in,要记住的是ip的下面还包含了一个结构体,因此要指到这个结构体里面。下面的前三项就是我们要做的。
sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序)
sin_addr存储IP地址,使用in_addr这个数据结构
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
先看一下使用TCP和UDP的流程图:
TCP:
UDP:
学艺不精,没有弄过UDP的。因此就用TCP的模型说几个要注意的点:
socket:
头文件:#include < sys/socket.h>
函数原型:int socket(int family,int type,int protocol)
参数含义:
family:对应的就是AF_INET、AF_INET6等。
type:套接字类型:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW。
protocol:0
返回值:
成功:非负套接字描述符。
出错:-1
bind:
头文件:#include< sys/socket.h>
函数原型:int bind(int sockfd,struct sockaddr *my_addr,int addrlen)
参数含义:
sockfd:套接字描述符。
my_addr:本地地址。
addrlen:地址长度:
返回值:
成功:0
出错:-1
listen:
头文件:#include < sys/socket.h>
函数原型:int listen(int sockfd,int backlog)
参数含义:
sockfd:套接字描述符
backlog:请求队列中允许的最大请求数,大多数系统默认为20
返回值:
成功:0
出错:-1
accept:
头文件:#include < sys/socket.h>
函数原型:int accept(int sockfd,struct sockaddr * addr,socklen_t* addrlen)
参数含义:
sockfd:套接字描述符
addr:客户端地址
addrlen:地址长度
返回值:
成功:返回非负数,这个类似于socket的返回值。是客户端的句柄。
出错:-1
connect:
头文件:#include < sys/socket.h>
函数原型:int connect(int sockfd,struct sockaddr* serv_addr,int addrlen)
参数含义:
sockfd:套接字描述符
serv_addr:服务器端地址
addrlen:地址长度
返回值:
成功:0
出错:-1
send
头文件:#include < sys/socket.h>
函数原型:int send(int sockfd,const void* msg,int len,int flags)
参数含义:
sockfd:套接字描述符
msg:指向要发送数据的指针
len:数据长度
flags:一般为0
返回值:
成功:发送的字节数
出错:-1
recv
头文件:#include < sys/socket.h>
函数原型:int recv(int sockfd,void* buf,int len,unsigned int flags)
参数含义:
sockfd:套接字描述符
buf:存放接受数据的缓冲区
len:数据长度
flags:一般为0
返回值:
成功:接受的字节数
出错:-1
read
头文件:#include <unistd.h>
函数原型:ssize_t read(int fd, void *buf, size_t count);
参数含义:
fd:被读的对象
buf:存放接受数据的缓冲区
size_t:这里是要读出的字节数
返回值:
成功:读出的字节数
出错:-1
write
头文件:#include <unistd.h>
函数原型:ssize_t write(int fd, const void *buf, size_t count);
参数含义:
fd:被写入对象
buf:存放接受数据的缓冲区
size_t:这里是要写入的字节数
返回值:
成功:写入的字节数
出错:-1
例子:
1 //创建server 2 #include<sys/socket.h> 3 #include<unistd.h> 4 #include<arpa/inet.h> 5 #include<stdio.h> 6 #include<string.h> 7 /*第一个的头文件是:socket,bind,listen,accept,connect这几个函数的头文件 8 *第二个头文件是:read,write函数的头文件* 9 *第三个的头文件是:inet_pton,inet_ntop,ntohs,htons这几个函数的,还有其他的这里就不一一列出来了 10 * */ 11 12 #define server_ip "127.0.0.1" //服务器的ip,注意我们这里用的是字符串 13 #define server_sport 7776 //服务器端口号 14 15 int main(){ 16 int sock,cli; 17 //创建套接字 18 sock = socket(AF_INET,SOCK_STREAM,0); //第一个参数表示我们用的是IPV4,第二个参数表示我们用的是TCP协议,第三个参数表示我们用的是一般形式 19 //创建连接所需要的结构体 20 struct sockaddr_in server_addr,client_addr; //这里我们引用的是socket_in的结构体,这个结构体是ipv4的 21 22 server_addr.sin_family = AF_INET; //我们在这个结构体中要设置三个参数:1,我们输入的ip的类型AF_INET2,我们的端口,这里要把“大端” 23 server_addr.sin_port = htons(server_sport); //转换成“小端”,我们用htops函数 3,这个参数是IP有两个要注意的:第一这是一个字符串所以要用 24 inet_pton(AF_INET,server_ip,&server_addr.sin_addr.s_addr);//inet_pton这个函数。第二:要填的,在结构体里的sin_addr,所指的另外一个结构体里面的in_addr。 25 26 //连接 27 bind(sock,(struct sockaddr*)&server_addr,sizeof(server_addr)); //一个前面socket的返回的套接字,一个是上面定义的结构体,要记得转换类型成: 28 //struct sockaddr*,还有结构体的长度 29 //设置允许连接数量 30 listen(sock,20); //左边的是套接字,右边的是连接数量 31 //设置用户访问的句柄 32 socklen_t cli_len = sizeof(client_addr); 33 cli = accept(sock,(struct sockaddr*)&client_addr,&cli_len); //三个参数:第一个参数是sock套接字。第二个参数是客户端的结构体名字,要记得转换。第三i 是用户端的套 34 接字长度,要记得转换成socklen_t*类型。我们可以通过这个的返回值来接收客户端的信息。 35 //上面的就是创建服务端的链接所需要的。 36 char buf[BUFSIZ]; //操作系统的宏,大概是8k大小 37 int n,i; 38 while(1){ 39 n = read(cli,buf,sizeof(buf)); //返回值:读到的字节数 40 for(i = 0;i<=n;i++){ 41 buf[i] = toupper(buf[i]); 42 } 43 write(cli,buf,n); 44 //printf("%s",buf); 45 } 46 close(sock); 47 close(cli); 48 return 0; 49 }
1 //创建client 2 #include<sys/socket.h> 3 #include<arpa/inet.h> 4 #include<unistd.h> 5 #include<stdio.h> 6 #include<string.h> 7 8 #define server_ip "127.0.0.1" 9 #define server_port 7776 10 11 //客户端可以不指定自己的ip,因为系统会自动分配端口和ip,因此我们不需要bind函数 12 int main(){ 13 int cli; 14 cli = socket(AF_INET,SOCK_STREAM,0); 15 16 struct sockaddr_in server_addr; 17 memset(&server_addr,0,sizeof(server_addr)); 18 19 server_addr.sin_family = AF_INET; 20 server_addr.sin_port = htons(server_port); 21 inet_pton(AF_INET,server_ip,&server_addr.sin_addr.s_addr); 22 23 connect(cli,(struct sockaddr*)&server_addr,sizeof(server_addr)); 24 25 char buf [BUFSIZ]; 26 int ret,i; 27 while(1){ 28 fgets(buf,sizeof(buf),stdin); 29 write(cli,buf,strlen(buf)); //读取文件操作 30 ret = read(cli,buf,sizeof(buf)); 31 write(STDOUT_FILENO,buf,strlen(buf)); 32 } 33 close(cli); 34 return 0; 35 }
因为下面的博主写的太好了,借鉴了大部分。剩下的都是自己学的总结。
---------------------
作者:繁城落叶
来源:CSDN
原文:https://blog.csdn.net/Leafage_M/article/details/78459799
版权声明:本文为博主原创文章,转载请附上博文链接!
标签:strlen 接受 lis 字节序 现在 描述 不用 ima har
原文地址:https://www.cnblogs.com/luotianyi520/p/10048513.html