网站首页 seo镇江html5
Linux系统编程学习相关博文
- Linux系统编程——文件编程的学习
 - Linux系统编程——进程的学习
 - Linux系统编程——进程间通信的学习
 - Linux系统编程——线程的学习
 
Linux系统编程——网络编程的学习
- 一、概述
 - 1. TCP/UDP
 - 2. 端口号
 - 3. 字节序
 - 4. Sockt服务器和客户端的开发步骤
 - 1. 服务器
 - 2. 客户端
 
- 二、网络编程API
 - 三、API介绍
 - 1. socket函数
 - 2. bind函数
 - 3. listen函数
 - 4. accept函数
 - 5. connect函数
 - 6. inet_aton函数
 - 7. inet_ntoa函数
 - 8. htons函数
 
- 四、API的使用例子
 
一、概述
1. TCP/UDP
- TCP面向连接( 如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
 - TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
 - TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低 (对实时应用很有用,如IP电话,实时视频会议等)
 - 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
 - TCP首部开销20字节;UDP的首部开销小,只有8个字节
 - TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
 
2. 端口号
一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等。这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP 地址与网络服务的关系是一对多的关系。实际上是通过“IP地址+端口号”来区分不同的服务的。端口提供了一种访问通道,服务器一般都是通过知名端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TFTP (简单文件传送协议) 服务器的UDP端口号都是69。
3. 字节序
- 小端字节序 (Little endian):将低序字节存储在起始地址
 - 大端字节序 (Big endian):将高序字节存储在起始地址
 - 网络字节序 = 大端字节序
 
4. Sockt服务器和客户端的开发步骤
1. 服务器
- 创建套接字
 - 为套接字添加信息 (IP地址和端口号)
 - 监听网络连接
 - 监听到有客户端接入,接受一个连接
 - 数据交互
 - 关闭套接字,断开连接
 
2. 客户端
- 创建套接字
 - 连接服务器 (输入服务器IP地址和端口号)
 - 数据交互
 - 关闭套接字,断开连接

 
二、网络编程API
在Linux系统中,操作系统提供了一系列的API,详细看下图
创建套接字	socket()
绑定		bind()
监听		listen()
连接(服务器)		accept()
连接(客户端)		connect()地址转换		inet_aton() / inet_ntoa
字节序转换	htons() / htonl() / ntohs() / ntohl()
 
三、API介绍
1. socket函数
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int socket(int domain, int type, int protocol);1. 函数功能:创建套接字2. 形参说明:
domain:指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族)- AF_INET	IPV4因特网域- AF_INET6	IPv6因特网域 - AF_UNIX 	Unix域 - AF_ROUTE 	路由套接字- AF_KEY 	密钥套接字 - AF_UNSPEC 未指定
type:指定 socket 的类型- SOCK STREAM流式套接字提供可靠的、面向连接的通信流,它使用 TCP 协议,从而保证了数据传输的正确性和顺序性- SOCK DGRAM数据报套接字定义了一种无连接的服,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议 UDP- SOCK RAW允许程序使用底层协议,原始套接字允许对底层协议如 IP 或 ICMP 进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发
protocal:通常赋值为0- 0 选择 type 类型对应的默认协议- IPPROTO_TCP TCP 传输协议- IPPROTO_UDP UDP 传输协议- IPPROTO_SCTP SCTP 传输协议- IPPROTO_TIPC TIPC 传输协议
3. 返回值:成功,返回套接字的文件描述符;失败,返回-1
 
2. bind函数
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);1. 函数功能:用于绑定IP地址和端口号到sockfd
2. 形参说明:
sockfd:调用socket函数返回的套接字描述符
addr:是一个指向包含有本机 IP 地址及端口号等信息的 sockaddr 类型的指针,指向要绑定给 sockfd 的协议地址结构,这个地址结构根据地址创建 socket 时的地址协议族的不同而不同(该结构体原型在下方)
addrlen:addr的大小struct sockaddr结构体原型:struct sockaddr {sa_family_t sa_family;char        sa_data[14];};
可同等替换为:struct sockaddr_in {__kernel_sa_family_t  sin_family; /* 协议族 */__be16  sin_port;   /* 端口号 */struct in_addr  sin_addr;   /* IP地址结构体 *//* 填充到结构体sockaddr的大小,没有实际意义,只是为跟sockaddr结构体在内存中对齐,这样两者才能相互转换 */unsigned char  __pad[__SOCK_SIZE__ - sizeof(short int) -sizeof(unsigned short int) - sizeof(struct in_addr)];};
3. 返回值:成功,返回0;失败,返回-1
 
3. listen函数
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int listen(int sockfd, int backlog);1. 函数功能:监听设置,设置能处理的最大连接数- 设置能处理的最大连接数,listen() 并末开始接受连线,只是设置 sockect 的 listen 模式,listen 函数只用于服务端,服务路进程不知道要与谁连接,因此,它不会主动地要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后响应该连接请求,并对它做出处理,一个服务进程可以同时处理多个客户进程的连接。主要就两个功能:将一个未连接的套接字转换为一个被动套接字(监听),规定内核为相应套接字排队的最大连接数。- 内核为任何一个给定监听套接字维护两个队列:- 未完成连接队列,每个这样的 SYN 报文段对应其中一项:已由某个客户发出并到达服务醒,而服务器正在等待完成相应的 TCP 二次握手过程。这些套接字处于 SYN REVD 状态;- 已完成连接队列,每个已完成 TCP 三次握手过程的客户端对应其中一项。这些套接字处于 ESTABLISHED 状态
2. 形参说明:
sockfd:调用socket函数返回的套接字描述符
backlog:请求队列允许的最大请求数
3. 返回值:成功,返回0;失败,返回-1
 
4. accept函数
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);1. 函数功能:用于从已完成连接队列队头返回下一个已完成的连接。如果已完成连接队列为空,那么进程被投入睡眠
2. 形参说明:
sockfd:调用socket函数返回的套接字描述符
addr:用来返回已连接的对端(客户端)的协议地址
addrlen:客户端地址长度
3. 返回值:该函数的返回值是一个新的套接字描述符,返回值是表示已连接的套接字描述符,而第一个参数是服务器监听套接字描述符。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在,内核为每个由服务器进程接受的客户连接创建一个已连接套接字(表示 TCP 三次挥手已完成),当服务器完成对某个给定客户的服务时,相应的已连接套接字就会被关闭。成功,返回0;失败,返回-1
 
5. connect函数
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);1. 函数功能:绑定之后的client端(客户端),与服务器建立连接
2. 形参说明:
sockfd:目的服务器的套接字描述符
addr:服务器端的IP地址和端口号的结构体指针
addrlen:addr大小
3. 返回值:成功,返回0;失败,返回-1
 
6. inet_aton函数
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int inet_aton(const char *cp, struct in_addr *inp);char *inet_ntoa(struct in_addr in);1. 函数功能:将字符串形式的 IP 地址转为网络能识别的格式
2. 形参说明:
cp:IP地址
inp:存放IP地址的结构体指针(如下所示)struct sockaddr结构体原型:struct sockaddr {sa_family_t sa_family;char        sa_data[14];};
可同等替换为:struct sockaddr_in {__kernel_sa_family_t  sin_family; /* 协议族 */__be16  sin_port;   /* 端口号 */struct in_addr  sin_addr;   /* IP地址结构体 *//* 填充到结构体sockaddr的大小,没有实际意义,只是为跟sockaddr结构体在内存中对齐,这样两者才能相互转换 */unsigned char  __pad[__SOCK_SIZE__ - sizeof(short int) -sizeof(unsigned short int) - sizeof(struct in_addr)];};
 
7. inet_ntoa函数
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>char *inet_ntoa(struct in_addr in);1. 函数功能:将网络格式的IP地址转为字符串格式
2. 形参说明:
in:存放IP地址的结构体(结构体原型见上方)
 
8. htons函数
#include <arpa/inet.h>uint16_t htons(uint16_t hostshort);1. 函数功能:返回网络字节序的值(关于什么是字节序请看概述部分),用来转换端口号
2. 形参说明:
hostshort:转换的端口号。h代表host,n代表net,s代表short(两个字节),l代表long(4个字节),通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取
 
四、API的使用例子
实现功能:服务器可以接收各个客户端的信息,每隔五秒服务器向连接的客户端发送信息(类似心跳包)
 1. 服务端
  1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <sys/types.h>          /* See NOTES */6 #include <sys/socket.h>7 #include <arpa/inet.h>8 #include <netinet/in.h>9 10 //1. int socket(int domain, int type, int protocol);11 //2. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);12 //3. uint16_t htons(uint16_t hostshort);13 //4. int inet_aton(const char *cp, struct in_addr *inp);14 //5. int listen(int sockfd, int backlog);15 //6. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);16 17 int main(int argc, char **argv)18 {19     int mark = 0;20     int c_fd = 0;21     int s_fd = 0;22     int s_bind = 0;23     int c_lent = 0;24     int n_read = 0;25     int s_listen = 0;26 27     char msg[128] = {'\0'};28     char readBuf[128] = {'\0'};29     char sendBuf[128] = {'\0'};30     //char *sendBuf = "Thank you for your message";31 32     struct sockaddr_in s_addr;33     struct sockaddr_in c_addr;34 35     memset(&s_addr, 0, sizeof(struct sockaddr_in));36     memset(&c_addr, 0, sizeof(struct sockaddr_in));37 38     if(argc != 3){39         printf("Please input three params\n");40         exit(-1);41     }42 43     //1. socket44     s_fd = socket(AF_INET, SOCK_STREAM, 0);45     if(s_fd == -1){46         perror("Create socekt failed:");47         exit(-1);48     }49 50     //2. bind51     s_addr.sin_family = AF_INET;52     s_addr.sin_port = htons(atoi(argv[2]));53     inet_aton(argv[1], &s_addr.sin_addr);54 55     s_bind = bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));56     if(s_bind == -1){57         perror("Bind failed:");58         exit(-1);59     }60 61     //3. listen62     s_listen = listen(s_fd, 10);63     if(s_listen == -1){64         perror("Listen failed:");65         exit(-1);66     }67 68     printf("listen...\n");69 70     //4. accept71     while(1){72         c_lent = sizeof(struct sockaddr_in);73         c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &c_lent);74         if(c_fd == -1){75             perror("Accept failed:");76             exit(-1);77         }78 79         printf("Get connect: %s\n", inet_ntoa(c_addr.sin_addr));80 81         mark++;82 83         if(fork() == 0){84             while(1){85                 memset(sendBuf, '\0', sizeof(sendBuf));86 87                 sprintf(sendBuf, "Welcome No.%d client", mark);88                 write(c_fd, sendBuf, sizeof(sendBuf));89                 sleep(5);90 91                 if(fork() == 0){92                     while(1){93                         memset(msg, '\0', sizeof(msg));94                         memset(readBuf, '\0', sizeof(readBuf));95 96                         n_read = read(c_fd, readBuf, sizeof(readBuf));97                         if(n_read == -1){98                             printf("Read failed:");99                         }else{
100                             sprintf(msg, "Get No.%d message: %s", mark, readBuf);
101                             printf("%s\n", msg);
102                         }
103                     }
104                 }
105             }
106         }
107     }
108 
109     return 0;
110 }
 
2. 客户端
  1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <sys/types.h>          /* See NOTES */6 #include <sys/socket.h>7 #include <arpa/inet.h>8 #include <netinet/in.h>9 10 //1. int socket(int domain, int type, int protocol);11 //2. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);12 //3. int inet_aton(const char *cp, struct in_addr *inp);13 //4. char *inet_ntoa(struct in_addr in);14 15 int main(int argc, char **argv)16 {17     int c_fd = 0;18     int n_read = 0;19 20     char readBuf[128] = {'\0'};21     char sendBuf[128] = {'\0'};22     //char *sendBuf = "Message from client";23 24     struct sockaddr_in c_addr;25 26     memset(&c_addr, 0, sizeof(struct sockaddr_in));27 28     if(argc != 3){29         printf("Please input three params\n");30         exit(-1);31     }32 33     //1. socket34     c_fd = socket(AF_INET, SOCK_STREAM, 0);35     if(c_fd == -1){36         perror("Create socekt failed:");37         exit(-1);38     }39 40     //2. connect41     c_addr.sin_family = AF_INET;42     c_addr.sin_port = htons(atoi(argv[2]));43     inet_aton(argv[1], &c_addr.sin_addr);44 45     if(connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1){46         perror("Connect failed:");47         exit(-1);48     }49 50     while(1){51         memset(sendBuf, '\0', sizeof(sendBuf));52 53         printf("input :");54         gets(sendBuf);55         write(c_fd, sendBuf, strlen(sendBuf));56 57         if(fork() == 0){58             while(1){59                 memset(readBuf, '\0', sizeof(readBuf));60 61                 n_read = read(c_fd, readBuf, sizeof(readBuf));62                 if(n_read == -1){63                     printf("Read failed:");64                 }else{65                     printf("Get server message: %s\n", readBuf);66                 }67             }68         }69     }70 71     return 0;72 }
