码迷,mamicode.com
首页 > 编程语言 > 详细

C语言 socket函数的简单运用 29

时间:2018-12-01 23:32:29      阅读:277      评论:0      收藏:0      [点我收藏+]

标签: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指定的连接类型:

  • AF_INET:IPv4协议
  • AF_INET4:IPv6协议
  • AF_LOCAL:UNIX域协议
  • AF_LINK:链路地址协议
  • AF_KEY:秘钥套接字 

 

大端字节序与小端字节序

大端字节序在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_pton

    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--出错
inet_ntop

 

结构体,我们这里说的结构体是指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 };
sockaddr结构
技术分享图片
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结构

我们通常用到的是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的模型说几个要注意的点:

  •   我们在连接的时候sever会执行到accept这个函数。那么就会处于阻塞状态等待client的连接,只有连接了才会往下面走。
  •   server端的代码到accept前面的属于连接所需要的,connect前面也是连接所需要的。
  •   client端可以不指定自己的IP和端口,客户端是主动连接,而服务器是等待连接。
  •   客户端:socket函数的返回值进过连接的操作后,这个的返回值就是服务端的句柄
  •        服务端:socket函数的返回值进过连接的操作后,这个的返回值就是本身的句柄。而accept的返回值就是客户端的句柄
  •        最后记得借宿的时候要close,客户端的close掉socket的返回值。服务端close掉socket和accept的返回值

  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
版权声明:本文为博主原创文章,转载请附上博文链接!

C语言 socket函数的简单运用 29

标签:strlen   接受   lis   字节序   现在   描述   不用   ima   har   

原文地址:https://www.cnblogs.com/luotianyi520/p/10048513.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!