《TCP/IP网络编程》读书笔记
(1) Windows 下的 socket 程序依赖 Winsock.dll 或 ws2_32.dll,必须提前加载。DLL 有两种加载方式,请查看:动态链接库DLL的加载
(2) Linux 使用“文件描述符”的概念,而 Windows 使用“文件句柄”的概念;Linux 不区分 socket 文件和普通文件,而 Windows 区分;
(3) Linux 下 socket() 函数的返回值为 int 类型,而 Windows 下为 SOCKET 类型,也就是句柄。
(4) Linux 下使用 read() / write() 函数读写,而 Windows 下使用 recv() / send() 函数发送和接收。
(5) 关闭 socket 时,Linux 使用 close() 函数,而 Windows 使用 closesocket() 函数。
文件描述符 对象
0 标准输出
1 标准输出
2 标准错误
最新的DLL是 ws2_32.dll,大小为 69KB,对应的头文件为 winsock2.h。
使用#pragma命令,在编译时加载:
#pragma comment (lib, "ws2_32.lib")
WinSock 编程的第一步就是加载 ws2_32.dll,然后调用 WSAStartup() 函数进行初始化,并指明要使用的版本号
- WSADATA wsaData;
- WSAStartup( MAKEWORD(, ), &wsaData);
4. socket编程
(2) 使用bind()函数将套接字与特定的IP地址和端口绑定起来,只有这样,流经该IP地址和端口的数据才能交给套接字处理
(3) 使用connect()函数来建立连接
可以认为,sockaddr 是一种通用的结构体,可以用来保存多种类型的IP地址和端口号,而 sockaddr_in 是专门用来保存 IPv4 地址的结构体
另外还有 sockaddr_in6,用来保存 IPv6 地址
(7) accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号
(8) listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen() 后面的代码会继续执行,直到遇到accept()
(9) accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。
(10) TCP服务器端函数调用顺序:socket()->bind()->listen()->accept()->read()/write()->close()
(11) TCP客户端函数调用顺序:socket()->connect()->read()/write()->close()
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- #define BUF_SIZE 1024
- void error_handling(char *message);
- int main(int argc, char *argv[])
- {
- int serv_sock, clnt_sock;
- char message[BUF_SIZE];
- int str_len, i;
- struct sockaddr_in serv_adr;
- struct sockaddr_in clnt_adr;
- socklen_t clnt_adr_sz;
- if(argc!=) {
- printf("Usage : %s <port>\n", argv[]);
- exit();
- }
- serv_sock=socket(PF_INET, SOCK_STREAM, );
- if(serv_sock==-)
- error_handling("socket() error");
- memset(&serv_adr, , sizeof(serv_adr));
- serv_adr.sin_family=AF_INET;
- serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
- serv_adr.sin_port=htons(atoi(argv[]));
- if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-)
- error_handling("bind() error");
- if(listen(serv_sock, )==-)
- error_handling("listen() error");
- clnt_adr_sz=sizeof(clnt_adr);
- for(i=; i<; i++)
- {
- clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
- if(clnt_sock==-)
- error_handling("accept() error");
- else
- printf("Connected client %d \n", i+);
- while((str_len=read(clnt_sock, message, BUF_SIZE))!=)
- write(clnt_sock, message, str_len);
- close(clnt_sock);
- }
- close(serv_sock);
- return ;
- }
- void error_handling(char *message)
- {
- fputs(message, stderr);
- fputc('\n', stderr);
- exit();
- }
(2)基于linux环境的tcp回声客户端的实现
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- #define BUF_SIZE 1024
- void error_handling(char *message);
- int main(int argc, char *argv[])
- {
- int sock;
- char message[BUF_SIZE];
- int str_len;
- struct sockaddr_in serv_adr;
- if(argc!=) {
- printf("Usage : %s <IP> <port>\n", argv[]);
- exit();
- }
- sock=socket(PF_INET, SOCK_STREAM, );
- if(sock==-)
- error_handling("socket() error");
- memset(&serv_adr, , sizeof(serv_adr));
- serv_adr.sin_family=AF_INET;
- serv_adr.sin_addr.s_addr=inet_addr(argv[]);
- serv_adr.sin_port=htons(atoi(argv[]));
- if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-)
- error_handling("connect() error!");
- else
- puts("Connected...........");
- while()
- {
- fputs("Input message(Q to quit): ", stdout);
- fgets(message, BUF_SIZE, stdin);
- if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))
- break;
- write(sock, message, strlen(message));
- str_len=read(sock, message, BUF_SIZE-);
- message[str_len]=;
- printf("Message from server: %s", message);
- }
- close(sock);
- return ;
- }
- void error_handling(char *message)
- {
- fputs(message, stderr);
- fputc('\n', stderr);
- exit();
- }
(2) 使用 shutdown() 函数可以只断开一条数据传输通道,而保留另一条
- int shutdown(int sock, int howto); //Linux
- int shutdown(SOCKET s, int howto); //Windows
sock 为需要断开的套接字,howto 为断开方式
howto 在 Linux 下有以下取值:
- SHUT_RD:断开输入流。套接字无法接收数据(即使输入缓冲区收到数据也被抹去),无法调用输入相关函数。
- SHUT_WR:断开输出流。套接字无法发送数据,但如果输出缓冲区中还有未传输的数据,则将传递到目标主机。
- SHUT_RDWR:同时断开 I/O 流。相当于分两次调用 shutdown(),其中一次以 SHUT_RD 为参数,另一次以 SHUT_WR 为参数。
howto 在 Windows 下有以下取值:
- SD_RECEIVE:关闭接收操作,也就是断开输入流。
- SD_SEND:关闭发送操作,也就是断开输出流。
- SD_BOTH:同时关闭接收和发送操作。
shutdown() 用来关闭连接,而不是套接字,不管调用多少次 shutdown(),套接字依然存在,直到调用 close() / closesocket() 将调用 close()/closesocket() 关闭套接字时,或调用 shutdown() 关闭输出流时,都会向对方发送 FIN 包。FIN 包表示数据传输完毕,计算机收到 FIN 包就知道不会再有数据传送过来了
(3) 默认情况下,close()/closesocket() 会立即向网络中发送FIN包,不管输出缓冲区中是否还有数据,而shutdown() 会等输出缓冲区中的数据传输完毕再发送FIN包, 也就意味着,调用 close()/closesocket() 将丢失输出缓冲区中的数据,而调用 shutdown() 不会。
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- #define BUF_SIZE 30
- void error_handling(char *message);
- int main(int argc, char *argv[])
- {
- int serv_sd, clnt_sd;
- FILE * fp;
- char buf[BUF_SIZE];
- int read_cnt;
- struct sockaddr_in serv_adr, clnt_adr;
- socklen_t clnt_adr_sz;
- if(argc!=) {
- printf("Usage: %s <port>\n", argv[]);
- exit();
- }
- fp=fopen("file_server.c", "rb");
- serv_sd=socket(PF_INET, SOCK_STREAM, );
- memset(&serv_adr, , sizeof(serv_adr));
- serv_adr.sin_family=AF_INET;
- serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
- serv_adr.sin_port=htons(atoi(argv[]));
- bind(serv_sd, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
- listen(serv_sd, );
- clnt_adr_sz=sizeof(clnt_adr);
- clnt_sd=accept(serv_sd, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
- while()
- {
- read_cnt=fread((void*)buf, , BUF_SIZE, fp);
- if(read_cnt<BUF_SIZE)
- {
- write(clnt_sd, buf, read_cnt);
- break;
- }
- write(clnt_sd, buf, BUF_SIZE);
- }
- shutdown(clnt_sd, SHUT_WR);
- read(clnt_sd, buf, BUF_SIZE);
- printf("Message from client: %s \n", buf);
- fclose(fp);
- close(clnt_sd); close(serv_sd);
- return ;
- }
- void error_handling(char *message)
- {
- fputs(message, stderr);
- fputc('\n', stderr);
- exit();
- }
(2)file_client.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- #define BUF_SIZE 30
- void error_handling(char *message);
- int main(int argc, char *argv[])
- {
- int sd;
- FILE *fp;
- char buf[BUF_SIZE];
- int read_cnt;
- struct sockaddr_in serv_adr;
- if(argc!=) {
- printf("Usage: %s <IP> <port>\n", argv[]);
- exit();
- }
- fp=fopen("receive.dat", "wb");
- sd=socket(PF_INET, SOCK_STREAM, );
- memset(&serv_adr, , sizeof(serv_adr));
- serv_adr.sin_family=AF_INET;
- serv_adr.sin_addr.s_addr=inet_addr(argv[]);
- serv_adr.sin_port=htons(atoi(argv[]));
- connect(sd, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
- while((read_cnt=read(sd, buf, BUF_SIZE ))!=)
- fwrite((void*)buf, , read_cnt, fp);
- puts("Received file data");
- write(sd, "Thank you", );
- fclose(fp);
- close(sd);
- return ;
- }
- void error_handling(char *message)
- {
- fputs(message, stderr);
- fputc('\n', stderr);
- exit();
- }
不同 CPU 保存和解析数据的方式不同(主流的 Intel 系列 CPU 为小端序),小端序系统和大端序系统通信时会发生数据解析错误。因此在发送数据前,要将数据转换为统一的格式——网络字节序(Network Byte Order)。网络字节序统一为大端序。
ping 域名可以查看域名对应的IP地址
nslookup命令可以查看计算机中注册的默认DNS服务器地址
(1) 下列函数可以通过传递字符串格式的域名获取IP地址
- #include <netdb.h>
- struct hostent *gethostbyname(const char *hostname);
成功时返回hostnet结构体指针,失败时返回NULL指针
例子:gethostbyname.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- void error_handling(char *message);
- int main(int argc, char *argv[])
- {
- int i;
- struct hostent *host;
- if(argc!=) {
- printf("Usage : %s <addr>\n", argv[]);
- exit();
- }
- host=gethostbyname(argv[]);
- if(!host)
- error_handling("gethost... error");
- printf("Official name: %s \n", host->h_name);
- for(i=; host->h_aliases[i]; i++)
- printf("Aliases %d: %s \n", i+, host->h_aliases[i]);
- printf("Address type: %s \n",
- (host->h_addrtype==AF_INET)?"AF_INET":"AF_INET6");
- for(i=; host->h_addr_list[i]; i++)
- printf("IP addr %d: %s \n", i+,
- inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
- return ;
- }
- void error_handling(char *message)
- {
- fputs(message, stderr);
- fputc('\n', stderr);
- exit();
- }
- struct hostnet
- {
- char *h_name;
- char **h_aliases;
- int h_addrtype;
- int h_lenght;
- char **h_addr_list;
- }
(2) gethostbyaddr()函数利用IP地址获取域相关信息
- #include <netdb.h>
- struct hostnet *gethostbyaddr(const char *addr, socklen_t len, int fanily);
成功时返回hostnet结构体变量地址值,失败时返回NULL指针
addr:含有IP地址信息的in_addr结构体指针
len: 向第一个参数传递的地址信息的字节数,IPv4时为4,IPv6时为6
family: 传递地址族信息,IPv4时为AF_INET, IPv6时为AF_INET6
例子:gethostbyaddr.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- void error_handling(char *message);
- int main(int argc, char *argv[])
- {
- int i;
- struct hostent *host;
- struct sockaddr_in addr;
- if(argc!=) {
- printf("Usage : %s <IP>\n", argv[]);
- exit();
- }
- memset(&addr, , sizeof(addr));
- addr.sin_addr.s_addr=inet_addr(argv[]);
- host=gethostbyaddr((char*)&addr.sin_addr, , AF_INET);
- if(!host)
- error_handling("gethost... error");
- printf("Official name: %s \n", host->h_name);
- for(i=; host->h_aliases[i]; i++)
- printf("Aliases %d: %s \n", i+, host->h_aliases[i]);
- printf("Address type: %s \n",
- (host->h_addrtype==AF_INET)?"AF_INET":"AF_INET6");
- for(i=; host->h_addr_list[i]; i++)
- printf("IP addr %d: %s \n", i+,
- inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
- return ;
- }
- void error_handling(char *message)
- {
- fputs(message, stderr);
- fputc('\n', stderr);
- exit();
- }
- unsigned short htons(unsigned short);
- unsigned short ntohs(unsigned short);
- unsigned long htonl(unsigned long);
- unsigned long ntohl(unsigned long);
(4) 将字符串信息转换为网络字节序的整数型
- #include <arpa/inet.h>
- in_addr_t inet_addr(const char *string);
成功时返回32位大端序整数型值,失败时返回INADDR_NONE
(5) inet_aton()也将字符串形式IP地址转换位32位网络字节序整数并返回,只不过该函数利用in_addr结构体,且其使用频率跟高
- #include <arpa/inet.h>
- int inet_aton(const char *string, struct in_addr *addr);
成功时返回1(true),失败时返回0(false)
(6) inet_ntoa()函数可以把网络字节序整数型IP地址转换成字符串形式
- #include <arpa/inet.h>
- char *inet_ntoa(struct in_addr adr);
成功时返回转换的字符串地址值,失败时返回-1;
调用完该函数后应立即将字符串信息复制到其他内存空间,因为,若再次调用inet_ntoa函数,则有可能覆盖之前保存的字符串信息
- struct sockaddr_in addr;
- char *serv_ip = "211.217.168.13";
- char *serv_port "";
- memset(&addr, , sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr(serv_ip);
- addr.sin_port = htons(atoi(serv_port));
利用常数INADDR_ANY分配服务器的IP地址,可以自动获取运行服务器端的计算机IP地址
addr.sin_addr.s_addr = htonl(INADDR_ANY);
- getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
- setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen)
- SO_SNDBUF:输入缓冲大小相关可选项
- SO_RCVBUF:输出缓冲大小相关可选项
- SO_REUSEADDR:该可选项设置为TRUE可将Time_wait状态下的套接字端口号重新分配给新的套接字
TCP_NODELAY设置为1可禁用Nagle算法
- #include <unistd.h>
- pid_t fork(void);
父进程:fork函数返回子进程的ID
子进程:fork函数返回0
销毁僵尸进程1:利用wait函数
- #include <unistd.h>
- pid_t wait(int *statloc);
->成功时返回终止的子进程ID,失败时返回-1
子进程终止时传递的返回值将保存到statloc所指内存空间,需要用下列宏进行分离
- WIFEXITED 子进程正常终止时返回“真”true
- WEXITSTATUS 返回子进程的返回值
销毁僵尸进程2:利用waitpid函数
- #include <sys/wait.h>
- pid_t waitpid(pid_t pid, int *statloc, int options);
->成功时返回终止的子进程ID,失败时返回-1
pid 等待终止的目标子进程ID,若传递-1,则可以等待任意子进程终止
(4) options 传递sys/wait.h中声明的常量WNOHANG,即使没有终止的子进程也不会进入组赛状态,而是返回0并退出函数
(5)信号与signal函数
- #include <signal.h>
- void (*signal(int signal, void (*func)(void)))(int);
->为了在产生信号时调用,返回之前注册的函数指针
(6) 利用sigaction函数进行信号处理
- #include <signal.h>
- int sugacyion(int signo, const struct sigaction *act, struct sigaction *oldact);
->成功时返回0,失败时返回-1
通过fork函数复制套接字文件描述符后,同一端口将对应多个套接字,只有这些套接字描述符都终止,才能销毁套接字
13.进程间通信
创建管道的函数:
- #include <unistd.h>
- int pipe(int filedes[]);
->成功时返回0,失败时返回-1
filedes[0]:通过管道接收数据时使用的文件描述符,即管道出口
filedse[1]:通过管道传输数据时使用的文件描述符,即管道入口
14.I/O复用
(1) 针对fd_set变量的操着的宏:
- FD_ZERO(fd_set *fdset)
- FD_SET(int fd, fd_set *fdset)
- FD_CLR(int fd, fd_set *fdset)
- FD_ISSET(fint fd, d_set *fdset)
(2) select函数:
- #include <sys/select.h>
- #include <sys/time.h>
- int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
15.多种I/O函数
(1) 收到MSG_OOB紧急消息时,操着系统将产生SIGURG消息,并调用注册的信号处理函数
(2) 处理SIGURG信号时必须指定处理信号的进程,而geipid函数返回调用此函数的进程ID
- fcntl(recv_sock, F_SETOWN, getpid());
上述调用的含义是“将文件描述符recv_sock指向的套接字拥有者(F_SETOWN)改为把getpid函数返回值用作ID的进程
(4) 紧急指针指向紧急消息的下一个位置(偏移量+1),紧急消息的意义在于督促消息处理,而非紧急传输形式受限的消息
(5) 调用recv函数的同时传递MSG_PEEK可选项,是为了保证即使不存在待读取的数据也不会进入阻塞状态,设置MSG_PEEK选项并调用recv函数时,即使读取了输入缓冲的数据也不会删除,该选项通常与MSG_DONTWAIT合作,用于调用以非阻塞方式验证待读取数据存在与否的函数
《TCP/IP网络编程》读书笔记的更多相关文章
- csapp读书笔记-并发编程
这是基础,理解不能有偏差 如果线程/进程的逻辑控制流在时间上重叠,那么就是并发的.我们可以将并发看成是一种os内核用来运行多个应用程序的实例,但是并发不仅在内核,在应用程序中的角色也很重要. 在应用级 ...
- CSAPP 读书笔记 - 2.31练习题
根据等式(2-14) 假如w = 4 数值范围在-8 ~ 7之间 2^w = 16 x = 5, y = 4的情况下面 x + y = 9 >=2 ^(w-1) 属于第一种情况 sum = x ...
- CSAPP读书笔记--第八章 异常控制流
第八章 异常控制流 2017-11-14 概述 控制转移序列叫做控制流.目前为止,我们学过两种改变控制流的方式: 1)跳转和分支: 2)调用和返回. 但是上面的方法只能控制程序本身,发生以下系统状态的 ...
- CSAPP 并发编程读书笔记
CSAPP 并发编程笔记 并发和并行 并发:Concurrency,只要时间上重叠就算并发,可以是单处理器交替处理 并行:Parallel,属于并发的一种特殊情况(真子集),多核/多 CPU 同时处理 ...
- 读书笔记汇总 - SQL必知必会(第4版)
本系列记录并分享学习SQL的过程,主要内容为SQL的基础概念及练习过程. 书目信息 中文名:<SQL必知必会(第4版)> 英文名:<Sams Teach Yourself SQL i ...
- 读书笔记--SQL必知必会18--视图
读书笔记--SQL必知必会18--视图 18.1 视图 视图是虚拟的表,只包含使用时动态检索数据的查询. 也就是说作为视图,它不包含任何列和数据,包含的是一个查询. 18.1.1 为什么使用视图 重用 ...
- 《C#本质论》读书笔记(18)多线程处理
.NET Framework 4.0 看(本质论第3版) .NET Framework 4.5 看(本质论第4版) .NET 4.0为多线程引入了两组新API:TPL(Task Parallel Li ...
- C#温故知新:《C#图解教程》读书笔记系列
一.此书到底何方神圣? 本书是广受赞誉C#图解教程的最新版本.作者在本书中创造了一种全新的可视化叙述方式,以图文并茂的形式.朴实简洁的文字,并辅之以大量表格和代码示例,全面.直观地阐述了C#语言的各种 ...
- C#刨根究底:《你必须知道的.NET》读书笔记系列
一.此书到底何方神圣? <你必须知道的.NET>来自于微软MVP—王涛(网名:AnyTao,博客园大牛之一,其博客地址为:http://anytao.cnblogs.com/)的最新技术心 ...
- Web高级征程:《大型网站技术架构》读书笔记系列
一.此书到底何方神圣? <大型网站技术架构:核心原理与案例分析>通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技术架构设计 ...
随机推荐
- [HNOI2013] 游走 - 概率期望,高斯消元,贪心
假如我们知道了每条边经过的期望次数,则变成了一个显然的贪心.现在考虑如何求期望次数. 由于走到每个点后各向等概率,很显然一条边的期望次数可以与它的两个端点的期望次数,转化为求点的期望次数 考虑每个点对 ...
- 10行代码实现简易版的Promise
实现之前,我们先看看Promise的调用 const src = 'https://img-ph-mirror.nosdn.127.net/sLP6rNBbQhy0OXFNYD9XIA==/79910 ...
- centos7 walle2瓦力部署教程
项目部署上线,如果是单服务器,那么有多种方式可以部署,比如直接ftp上传,或者直接git去拉取,人工操作也不会花费精力和时间,但是如果采用了集群模式,有多台服务器,那么依靠一台一台的去上传代码,就显得 ...
- js基础之--变量 作用域和内存问题
基本类型:Undefind Null Boolean Number String 引用类型: 对象 在操作对象时,实际上实在操作对象的引用而不是实际的对象.为此,引用类型的值是按引用访问的. 从一个变 ...
- OpenCV之XML和YAML文件读写
FileStorage类 该类有两个构造函数 FileStorage::FileStorage() FileStorage::FileStorage(const string& source, ...
- 如何删除github中的repository
打开个人界面->点击进入想删除的repository的界面->拉到最下面的danger zone->delete
- 每天进步一点点------Allegro 布线完成后如何修改线宽
一.如果要改变整个一条导线的宽度 1.在find栏里选择Cline; 2.在PCB中选择要改的导线,点击右键,选择Change Width 3.在对话框中输入你想要的线宽 3.如果要改变整个导线 ...
- vue基础api
vue比jq好处 1jq 频繁操作dom 增加了性能消耗 vue 模拟dom 从内存中拿 2jq 数据没有统一管理 vue 统一管理数据 3vue 组件开发可以提取出公共的html或js mv*好 ...
- C#中通过SendARP读取MAC地址
C#中通过SendARP读取MAC地址: using System.Runtime.InteropServices; publicstaticstring GetMacBySendARP(string ...
- poj 1611 :The Suspects经典的并查集题目
Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized ...