APUE笔记---网络IPC:socket套接字使用+聊天程序
来源:互联网 发布:淘宝企业店铺客服电话 编辑:程序博客网 时间:2024/05/18 03:37
APUE笔记—网络IPC:socket套接字使用+聊天程序
1. 套接字描述符的创建与销毁
套接字是通信端点的抽象,套接字描述符是一种文件描述符。
1.1创建套接字描述符
#include <sys/types.h> #include <sys/socket.h>int socket(int domain, int type, int protocol);//返回值:若成功,返回文件(套接字)描述符,若出错,返回-1
- 参数domain(域)确定通信特性,包括地址格式。下图列出部分:
|域|描述|
|:—:|:—:|
|AF_INET|IPv4因特网域|
|AF_INET6|IPv6因特网域|
|AF_UNIX,AF_LOCAL|UNIX域|
- 参数type 确定套接字的类型,进一步确定通信特征。
|类型|描述|协议|
|:—:|:—:|:—:|
|SOCK_STREAM|有序、可靠、双向、面向连接的字节流|TCP|
|SOCK_DGRAM|固定长度、无连接、不可靠的报文传递|UDP|
|SOCK_SEQPACKET|固定长度、有序、可靠、面向连接的报文传递|SCTP|
|SOCK_RAW|原始套接字|自行构造协议头部|
- 参数protocol通常为0,表示给定的域和套接字类型选择默认协议。
1.2销毁套接字
套接字通信是双向的,可以采用shutdown函数禁止一个套接字的IO
#include <sys/socket.h>int shutdown(int sockfd, int how);//返回值:若成功,返回0;若出错,返回-1.
- 参数how有三种选项
|how|描述|
|:—:|:—:|
|SHUT_RD|关闭读端|
|SHUT_WR|关闭写端|
|SHUT_RDWR|关闭读写|
shutdown函数和close函数的区别
- close函数用于关闭一个文件描述符,只是将文件描述符的引用计数减1,当引用计数减为0,则释放该文件描述符,否则引用该文件描述符的进程正常使用。
- shutdown函数用于禁止一个套接字的IO,无论它的文件描述符是否为0,都会设定套接字处于设定状态,并且引用该文件描述符的进程受影响。
2. 寻址
网络进程标示由两部分组成,并且唯一确定
- 计算机的网络地址,也就是IP地址。
- 计算机的端口号。
2.1字节序
在不同的处理器的存放方式主要有两种,以内存中0x0A0B0C0D的存放方式为例,分别有以下几种方式:
大端序
数据以8bit为单位:
地址增长方向 →
| … | 0x0A | 0x0B | 0x0C | 0x0D| …|
示例中,最高位字节是0x0A 存储在最低的内存地址处。下一个字节0x0B存在后面的地址处。
小端序
数据以8bit为单位:
地址增长方向 →
| … | 0x0D | 0x0C | 0x0B | 0x0A | … |
最低位字节是0x0D 存储在最低的内存地址处。后面字节依次存在后面的地址处。
TCP/IP协议栈使用大端序,所以在网络传输之前要进行字节序转换
有四个用来在处理器字节序和网络字节序之间实施转换的函数
#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong);//返回值:以网络字节序标示的32位整数uint16_t htons(uint16_t hostshort);//返回值:以网络字节序标示的16位整数uint32_t ntohl(uint32_t netlong);//返回值:以主机字节序标示的32位整数uint16_t ntohs(uint16_t netshort);//返回值:以主机字节序标示的16位整数
- h表示“主机”字节序, n表示“网络”字节序。
- l表示“长”整形,4字节,32位;s表示“短”整形,16位。
2.2地址格式
一个地址标识一个特定通信域的套接字端点,地址格与这个特定的通信相关,为使不同的地址格式传入套接字函数,地址会被强行转换成一个通用的地址结构sockaddr;
struct sockaddr { sa_family_t sa_family; /* address family, AF_xxx */ char sa_data[14]; /* 14 bytes of protocol address */};//在IPv4因特网域(AF_INET)中,套接字地址用结构sockaddr_in表示struct sockaddr_in { __kernel_sa_family_t sin_family; /* Address family */ __be16 sin_port; /* Port number */ struct in_addr sin_addr; /* Internet address */ /* Pad to size of `struct sockaddr'. */ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)];};/* Internet address */struct in_addr { __be32 s_addr;};//在IPv6因特网域(AF_INET6)中,套接字地址用结构sockaddr_in6表示struct sockaddr_in6 { unsigned short int sin6_family; /* AF_INET6 */ __be16 sin6_port; /* Transport layer port # */ __be32 sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ __u32 sin6_scope_id; /* scope id (new in RFC2553)*/};struct in6_addr { union { __u8 u6_addr8[16];#if __UAPI_DEF_IN6_ADDR_ALT __be16 u6_addr16[8]; __be32 u6_addr32[4];#endif } in6_u; //这个联合体大小是128字节,但是通过不同的分类,当读取ip地址时,会有不同的读取方式。#define s6_addr in6_u.u6_addr8#if __UAPI_DEF_IN6_ADDR_ALT#define s6_addr16 in6_u.u6_addr16#define s6_addr32 in6_u.u6_addr32#endif};
二进制地址格式与点分式十进制字符表示(a.b.c.d)之间相互转换,函数inet_ntop和inet_pton同时适用于IPv4和IPv6地址。
#include <arpa/inet.h>const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);//返回值:若成功,返回地址字符串指针,若出错,返回NULLint inet_pton(int af, const char *src, void *dst);//返回值:若成功,返回1,若格式无效,返回0,若出错,返回-1
- 参数af可以是AF_INET,也可以是AF_INET6,若以不被支持的地址族作为af参数,则两个函数返回错误,并且设置errno为EAFNOSUPPORT
- inet_pton函数将文本字符串格式转换成网络字节序的二进制地址。尝试转换src指向的字符串,并通过dst指针保存二进制结果。
- inet_ntop函数将网络字节序的二进制地址转换为文本字符串格式。从数值格式(stc)转换成表达式格式(dst)。size是目标存储单元的大小,以免缓冲区溢出。
2.3 将套接字与地址关联
使用bind函数关联地址和套接字。
#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);//返回值:若成功,返回0;若出错,返回-1
- bind将addr所指向的socket地址分配给未命名的sockfd文件描述符,addrlen参数指出socket地址的长度。
- 在进程正在运行的计算机上,指定的地址必须有效,不能指定一个其他机器的地址
- 地址必须和创建套接字时的地址组所支持的格式相匹配。
- 地址中国的端口号必须不小于1024,除非该进程有相应的特权。
- 一般只能将一个套接字端点绑定到一个给定的地址上。
对于因特网域,如果IP地址为INADDR_ANY,套接字端点可以被绑定到所有系统网络接口上。这意味可以接收这个系统所安装的任何一个网卡的数据包。
3. 建立连接
3.1 使用connect函数来建立连接
#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);//返回值:若成功,返回0;若出错,返回-1
在connect中指定的地址是要通信的目标地址。如果sockfd没有绑定地址,connect会给调用者一个默认的地址。
3.2 服务器调用listen函数来接收连接请求
#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int listen(int sockfd, int backlog);//返回值:若成功,返回0;若出错,返回-1
- 参数backlog提供一个提示,提示系统该进程要入队的未完成连接请求的数量。默认值为128,由
3.3 accept函数获得连接请求并建立连接
#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//返回值:若成功,返回文件(套接字)描述符,若出错,返回-1
- 函数accept返回的文件描述符是套接字描述符,该描述符连接到调用connect的客户端。该套接字描述符和原始套接字具有相同的套接字类型和地址族。
- 若不关心客户端标识,可以将参数addr和len设为NULL。否则在调用accept之前,将addr参数设为足够大的缓冲区来存放地址,并且将len指向的整数设为这个缓冲区的字节大小。返回时,accept会在缓冲区填充客户端的地址,并且更新指向len的整数来反应该地址的大小。
- 如果没有连接请求,accept会处于阻塞状态等待请求到来
4. 数据传输
对文件的读写操作read和write同样适用于socket。但是socket编程接口提供了几个专门用于socket数据读写的系统调用,增加了对数据读写的控制,用于TCP流数据读写的系统调用:
#include <sys/types.h>#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags);//返回值:返回数据的字节长度,若无可用数据或对等方已经按序结束,返回0,如出错,返回-1。ssize_t send(int sockfd, const void *buf, size_t len, int flags);//返回值:若成功,返回发送的字节数,如出错,返回-1
5. 带外数据
- 带外数据(out-of-band-data)是一些通信协议所支持的可选功能,与普通数据相比,它允许更高优先级的数据传输,带外数据先行传输即使传输队列已经有数据。TCP支持带外数据,但UDP不支持。
- TCP将带外数据称为紧急数据(urgent data)。TCP仅仅支持一个字节的紧急数据,,但是允许紧急数据在普通数据传递机制数据流之外传输。
- 用过send函数的的flags参数指定一个MSG_OOB标志可以产生紧急数据,如果带外数据超过一个字节,则视最后一个字节为紧急数据。
6.客户端和服务器端相互发送数据的简单实现
6.1服务器端程序代码
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/socket.h>#include <sys/types.h>#include <string.h>#include <arpa/inet.h>int main(int argc, char *argv[]){ struct sockaddr_in clientaddr, serveraddr; int sockfd; int connfd; int addrlen; //1.创建socket文件描述符用于处理监听 sockfd = socket(AF_INET, SOCK_STREAM, 0); //2.初始化服务器端的地址族,端口,和IP地址 bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(atoi(argv[2])); inet_pton(AF_INET, argv[1], &serveraddr.sin_addr); //3.将sockfd文件描述符和服务器地址绑定 bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); //4.监听,系统默认队列为128 listen(sockfd, 128); while(1){ //5.接受客户端连接,返回一个connfd文件描述符用与处理连接 addrlen = sizeof(clientaddr); connfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen); int ret; do{ //6.相应请求,先接受客户发送信息并答应,然后发送信息 char buf[1024]; memset(buf, '\0', sizeof(buf)); ret = recv(connfd, buf, sizeof(buf), 0); write(STDOUT_FILENO, buf, strlen(buf)); char dst[128]; printf("from ip:%s\tport:%d\n", inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, dst, sizeof(dst)), ntohs(clientaddr.sin_port)); printf("*-------------------------------------*\n"); memset(buf, '\0', sizeof(buf)); read(STDOUT_FILENO, buf, sizeof(buf)); send(connfd, buf, strlen(buf), 0); printf("msg is sent\n"); printf("*-------------------------------------*\n"); }while(ret); //7.关闭该用于连接的文件描述符 close(connfd); } //8.关闭用于监听的文件描述符 close(sockfd); return 0;}
6.2客户端程序代码
#include <stdio.h>#include <sys/socket.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <arpa/inet.h>int main(int argc, char *argv[]){ struct sockaddr_in serveraddr; int sockfd; //1.创建用于连接服务器的sockfd sockfd = socket(AF_INET, SOCK_STREAM, 0); //2.初始化服务器端的地址信息,主机序转换为网络序 bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(atoi(argv[2])); inet_pton(AF_INET, argv[1], &serveraddr.sin_addr); //3.连接服务器 connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); while(1){ //4.先发送,等待服务器端接受然后打印接受信息 char buf[1024] = { 0 }; read(STDOUT_FILENO, buf, sizeof(buf)); send(sockfd, buf, strlen(buf), 0); printf("msg is sent\n"); printf("*-------------------------------------*\n"); //char buf[1024]; memset(buf, '\0', sizeof(buf)); recv(sockfd, buf, sizeof(buf), 0); write(STDOUT_FILENO, buf, strlen(buf)); char dst[128]; printf("from ip:%s\tport:%d\n", inet_ntop(AF_INET, &serveraddr.sin_addr.s_addr, dst, sizeof(dst)), ntohs(serveraddr.sin_port)); printf("*-------------------------------------*\n"); } //5.释放连接的文件描述符 close(sockfd); return 0;}
6.3运行结果
- APUE笔记---网络IPC:socket套接字使用+聊天程序
- APUE函数笔记十四: 网络IPC:套接字
- apue学习笔记(第十六章 网络IPC:套接字)
- APUE学习笔记——第十六章 网络IPC:套接字
- APUE笔记 网络IPC
- APUE读书笔记-第16章-网络IPC: 套接字
- APUE读书笔记------16章 网络IPC:套接字
- 《APUE》读书笔记-第十六章网络IPC:套接字
- 网络IPC:套接字
- 网络IPC:套接字
- 套接字聊天程序
- 网络编程笔记一、Socket套接字
- 网络IPC之套接字
- 网络套接字学习以及聊天程序开发实例
- socket,网络套接字
- 网络套接字socket
- UNIX环境高级编程学习之第十六章网络IPC:套接字 - 简单TCP Socket 通信
- UNIX环境高级编程学习之第十六章网络IPC:套接字 - 简单UDP Socket 通信
- java 知识点(中)
- 什么是原语?
- Rxjava Subject分析
- 【备注】【C24】《Android 3D游戏开发技术详解与典型案例》PDF 下载
- C#语法小知识(二十二)集合Collection
- APUE笔记---网络IPC:socket套接字使用+聊天程序
- 90%的程序员写不出一个没有BUgd的二分查找的程序
- 递归函数
- C语言中的rand函数和srand函数
- 【直播】西山居2014校园招聘策划类笔试试题
- 在IOS与Android实现possword与number
- 栈的初使用
- Ubuntu
- IOS学习(七)UITabBarViewController标签视图控制器