《TCPIP网络编程》学习笔记

第1章 理解网络编程和套接字

网络编程中接受连接请求的套接字创建过程:

第一步:调用socket函数创建套接字;

第一步:调用bind函数分配IP地址和端口号;

第三步:调用listen函数转为可接收请求状态;

第四步:调用accept函数受理连接请求。

//Linux下的C语言编译器-GCC(GNU Compiler Collection,GNU编译器集合)
gcc hello_server.c -o hserver   //编译hello_server.c文件并生成可执行文件hserver,命令中的-o是用来指定可执行文件名的可选参数 

以_t为后缀的数据类型,元数据类型(primitive),在sys/types.h头文件中一般由typedef声明定义,为了适应系统、时代的变化,如过去16位操作系统时代,int类型是16位的,

文件描述符(File Descriptor):0、1、2分配给标准输入输出及标准错误

WSA(Windows Sockets Asynchronous,Windows异步套接字)

//调用WSAStartup函数,设置程序中用到的Winsock版本,并初始化相应版本的库
int WSAStartup(WORD wVersionRequested, LPWSADATA, lpWSAData);
//wVersionRequested 程序员要用的Winsock版本信息 lpWSAData WSADATA结构体变量的地址值

第2章 套接字类型与协议设置

int socket(int domain, int type, int protocol) //协议族(PF,protocol family),传输方式,协议//Windows返回SOCKET句柄

SOCK_STREAM、SOCK_DGRAM(datagram):存在数据边界意味着接收数据的次数应和传输次数相同

//Linux I/O函数 read/write
ssize_t write(int fd, const void* buf, size_t nbytes);//文件描述符 缓冲地址值 字节数
ssize_t read(int fd, void* buf, size_t nbytes);//成功时返回接收的字节数(但遇到文件末尾则返回0),失败使返回-1。
size_t是通过typedef声明的unsigned int类型,ssize_t前面多加的s代表signed
//Windows严格 区分文件I/O函数和套接字I/O函数 send/recv
int send(SOCKET s, const char* buf, int len, int flags);//p23
int recv(SOCKET s, const char* buf, int len, int flags);

第3章 地址族与数据序列

字节序(Order)与网络字节序

ox 12345678 --2种:大端序(big endian)0x12 0x34 0x56 0x78;小端序(little endian)反之

在通过网络传输数据时约定统一方式,这种约定称为网络字节序(network byte order),统一大端序

字节序转换htons(h、to、n、s)(把short型数据从主机字节序转化为网络字节序)

inet_aton函数和inet_addr函数都将字符串形式(点分...)IP地址转换为32位网络字节序整数并返回,inet_aton会自动把转换后的IP地址信息填入in_addr结构体变量

struct sockaddr_in
{
    sa_family_t     sin_family; //地址族(address family)
    unit16_t        sin_port;   //16位TCP/UDP端口号
    struct in_addr  sin_addr;   //32位IP地址
    char            sin_zero[8];//不使用
};
struct in_addr
{
    in_addr_t       s_addr;     //32位IPV4地址
};
in_addr_t inet_addr(const char* string)   //in_addr_t IP地址 声明为unit32_t

INADDR_ANY 可自动获取运行服务器端的计算机IP地址,若只有一个NIC,则精确匹配,127.0.0.1是回送地址(loopback address),指的是计算机自身IP地址

第4、5章 基于TCP的服务器端/客户端

int listen(int sok, int backlog);   //backlog连接请求队列(Queue)的长度,若为5,则队列长度为5,表示最多使5个连接请求进入队列

客户端的IP地址和端口在调用connect函数时自动分配,无需调用标记的bind函数进行分配。

ACK号 -> SEQ(sequence)号 + 传递的字节数 + 1

第6章 基于UDP的服务器端/客户端

不必调用TCP连接中调用的listen函数和accept函数,UDP中只有创建套接字的过程和数据交换过程。

调用sendto函数时自动分配IP和端口号;在UDP通信过程中使I/O函数调用次数保持一致。

针对UDP套接字调用connect函数并不意味着要与对方UDP套接字连接,这只是向UDP套接字注册目标IP和端口信息。不用sendto每次都填

第7章 优雅地断开套接字连接

int shutdown (int sock, int howto); p124

第8章 域名及网络地址

DNS(Domain Name System)

struct hostent //host entry
{
    char * h_name;              //official name
    char ** h_aliases;          //alias list
    int h_addrtype;             //host address type
    int h_length;               //address length
    char ** h_addr_list;        //address list
}

第9章 套接字的多种可选项

服务器端先断开连接的时候,套接字处在Time-wait过程中,相应端口是正在使用的状态,所以再启动,bind函数调用过程报错,而客户端每次运行程序时都会动态分配端口号

SO_REUSEADDR的默认值为0(假),这就意味着无法将Time_wait状态下的套接字端口号重新分配给新的套接字。改为1用setsockopt函数

第10章 多进程服务器端

CPU核的个数与可同时运行的进程数相同。相反,若进程数超过核数,进程将分时使用CPU资源。

“应该向创建子进程的父进程传递子进程的exit参数值或return语句的返回值”,“父母要负责收回自己生的孩子”;为了销毁子进程,父进程应主动请求获取子进程的返回值。

pid_t wait(int* statloc);//子进程终止时传递的返回值保存到函数参数所指内存空间
//宏分离 WIFEXIED WEXITSTATUS
//调用wait函数时,如果没有已终止的子进程,那么程序将阻塞(Blocking)直到有子进程终止
​
pid_t waitpid(pid_t pid, int * statloc, int options);//P165

信号与signal函数 P168

SIGALRM:已到通过alarm函数注册的时间
SIGINT:输入Ctrl+c
SIGCHLD:子进程终止
​
sigaction(SIGALRM, &act, 0);//注册SIGALRM信号的处理器 //“注册信号”--signal()/sigaction

fork函数复制文件描述符,父进程将2个套接字(一个是服务器端套接字,另一个是与客户端连接的套接字)文件描述符复制给子进程。

I/O程序分割的意义 p180

第11章 进程间通信

进程具有完全独立的内存结构,就连通过fork函数创建的子进程也不会与父进程共享内存空间

数据进入管道后成为无主数据

int pipe(int filedes[2]);//filedes[0]接受 管道出口;filedes[1]传输 管道入口

第12章 I/O复用

能在不创建新进程的同时向多个客户端提供服务 “教师 学生 一对一 一对多举手”

//使用select函数时可以将多个文件描述符集中到一起统一监视,监视项称为“事件”
int select(int maxfd, fd_set* readset, fd_set* writeset, fd_set* exceptset, cosnt struct timeval* timeout);
//调用select函数后,除发生变化的文件描述符对应位外,剩下的所有位初始化为0。结构体timeval的成员tv_sec和tv_usec的值将被替换为超时前剩余时间;因此调用select函数前,每次都需要初始化timeval结构体变量 p203
​
FD_ISSET(int fd, fd_set* fdset):若参数fdset指向的变量中包含文件描述符fd的信息,则返回“真”;fdset一般为select之后的副本,值仍为1的位置上的文件描述符发生了变化

第13章 多种I/O函数

除紧急指针的前面1个字节外,数据接收方将通过调用常用输入函数读取剩余部分。p218

设置MSG_PEEK选项并调用recv函数时,即使读取了输入缓冲的数据也不会删除。

readv & writev 函数

struct iovec
{
    void* iov_base;
    size_t iov_len;
}

"Windows中并不存在Linux那样的信号处理机制“ sigaction / select p225

第14章 多播与广播

struct ip_mreq//p233
{
    struct in_addr imr_multiaddr;
    struct in_addr imr_interface;
}

多播即使在跨越不同网络的情况下,只要加入多播组就能接收数据。相反,广播只能向同一网络中的主机传输数据。

第15、16章 使用标准I/O函数 I/O流分离

使用标准I/O函数时会得到额外的缓冲支持

 

FILE* fdopen(int fildes, const char* mode);//p249 将创建套接字时返回的文件描述符转换为标准I/O函数中使用的FILE结构体指针
int fileno(FILE* stream);//p251  
int dup(int fildes);
int dup2(int fildes, int fildes2);//p261

第17章 优于select的epoll

int epoll_create(int size);//p268
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event); //结构体epoll_event用于保存发生变化的(发生事件)的文件描述符
epoll_ctl(A, EPO_CTL_ADD, B, C);//epoll例程A中注册文件描述符B,主要目的是监视参数C中的事件
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
//成功时返回发生事件的文件描述符数,失败时返回-1。同时在第二个参数指向的缓冲中保存发生事件的文件描述符集合。因此,无需像select那样插入针对所有文件描述符的循环。

条件触发和边缘触发

条件触发方式中,只要输入缓冲中还有数据,就将以事件方式再次注册

EPOLLET:以边缘触发(Edge Trigger)的方式得到事件通知

int fcntl(int filedes, int cmd, ...);//p278 更改或读取文件属性

第18章 多线程服务器端的实现

  • 线程的创建和上下文切换比进程的创建和上下文切换更快

  • 线程间交换数据时无需特殊技术

线程隔开栈区域,共享数据区和堆

//p287
int pthread_create(
pthread_t* restrict thread, const pthread_attr_t* restrict arr, void* (* start_routine)(void*), void* restrict arg
);
thread--保存新创建线程ID的变量地址值。线程与进程相同,也需要用于区分不同线程的ID。
attr--用于传递线程属性的参数,传递NULL时,创建默认属性的线程。
start_routine--相当于线程main函数的、在单独执行流中执行的函数地址值(函数指针)。
arg--通过第三个参数传递调用函数时包含传递参数信息的变量地址值。
    
int pthread_join(pthread_t thread, void** status);//p290
thread--该参数值ID的线程终止后才会从该函数返回。
status--保存线程的main函数返回值的指针变量地址值。
​
root@my_linux:/tcpip# gcc -D_REENTRANT mythread.c -o mthread -lpthread   p292    
    
int pthread_detach(pthread_t thread);//p307    

线程访问变量num时应该阻止其他线程访问 同步Synchronization

临界区:“函数内同时运行多个线程时引起问题的多条语句构成的代码块。”

互斥量(Mutex,Mutual Exclusion)

pthread_mutex_init() & pthread_mutex_destroy(); //p300

pthread_mutex_lock() & pthread_mutex_unlock();

sem_init() & sem_destroy(); sem_post() & sem_wait(); //p304

第19章 Windows平台下线程的使用

“Windows线程在首次调用的线程main函数返回时销毁(销毁时间点和销毁方法与Linux不同)。还有其他方法可以终止线程,但最好的办法就是让线程main函数终止(返回),故省略其他说明。”

HANDLE CreateThread();//p318
uintptr_t _beginthreadex( //p319 创建“使用线程安全标准C函数”的线程
void* security,
unsigned stack_size,
unsigned (* start_address)(void*),    //传递线程的main函数信息
void* arglist,
unsigned initflag,
unsigned* thrdaddr                    //用于保存线程ID的变量地址值
);                                    //成功时返回线程句柄,失败时返回0

所以终止状态又称“signaled状态”(boolean TRUE),未终止状态称为“non-signaled状态”(FALSE)。

DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);//p322 针对单个内核对象验证signaled状态
DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliseconds);//p323 

第20章 Windows中的线程同步

 

典型的内核模式同步方法有基于事件(Event)、信号量、互斥量等内核对象的同步

void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);//p329
//销毁CRITICAL_SECTION对象使用过的(CRITICAL_SECTION对象相关的)资源
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
//获取和释放“钥匙”
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
​
HANDLE CreateMutex(
    LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName);//p331
BOOL CloseHandle(HANDLE hObject);
WaitForSingleObject(hMutex, INFINITE);//互斥量在WaitForSingleObject函数返回时自动进入non-signaled状态,“auto-reset”
//临界区的开始
//......
//临界区的结束
ReleaseMutex(hMutex);//p332
​
HANDLE CreateSemaphore(
    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, 
        LONG lMaximumCount, LPCTSTR lpName);
BOOL ReleaseSemaphore()//p334 释放意味着信号量值的增加
信号量对象的值大于0时成为signaled状态,为0时成为non-signaled状态。  
WaitForSingleObject(hSemaphore, INFINITE);//返回的时候同时将信号量值减一
//临界区的开始
//...
//临界区的结束
ReleaseSemaphore(hSemaphore, 1, NULL);//释放意味着信号量的增加
​
事件对象的主要特点是可以创建manual-reset模式的对象
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, 
        BOOL bInitialState, LPCTSTR lpName);//p337
BOOL ResetEvent(HANDLE hEvent);//to the non-signaled 
BOOL SetEvent(HANDLE hEvent);//to the signaled

第21章 异步通知I/O模型

异步I/O是指I/O函数的返回时刻与数据收发的完成时刻不一致

“异步方式能够比同步方式更有效地使用CPU” "指定监视对象后可以离开执行其他任务,最后再回来验证状态变化"

 

int WSAEventSelect();//p347 该函数用于指定某一套接字为事件监视对象
WSAEVENT WSACreateEvent(void);//p348 便捷创建manual-reset模式non-signaled状态的事件对象
BOOL WSACloseEvent(WSAEVENT hEvent);
DWORD WSAWaitForMultipleEvents();//p349   发生了事件
int WSAEnumNetworkEvents();//p350-351 !!  区分事件类型  

 

第22章 重叠I/O模型

SOCKET WSASocket();//p358 可以创建适用于重叠I/O的套接字
WSASocket(PF_INET, SOCK_STREAM, 0, NULL, WSA_FLAG_OVERLAPPED);//创建出可以进行重叠I/O的非阻塞模式的套接字
int WSASend();//p359 执行重叠I/O的WSASend函数
BOOL WSAGetOverlappedResult();//p361
int WSARecv();
​
int WSAGetLastError(void);//p364 WSA_IO_PENDING

 

Gather/Scatter I/O是指,将多个缓冲中的数据累积到一定程度后一次性传输(Gather输出),将接收的数据分批保存(Scatter输入)。

重叠I/O中有2种方法确认I/O的完成并获取结果。

  • 利用WSASend、WSARecv函数的第六个参数,基于事件对象。

    • 完成I/O时,WSAOVERLAPPED结构体变量引用的事件对象将变为signaled状态。

    • 为了验证I/O的完成和完成结果,需要调用WSAGetOverlappedResult函数。

  • 利用WSASend、WSARecv函数的第七个参数,基于Completion Routine。//p367 

void CALLBACK CompletionROUTINE();//p370 返回值类型void后插入的CALLBACK关键字与main函数中声明的关键字WINAPI相同,都是用于声明函数的调用规范,所以定义Completion Routine时必须添加。

第23章 IOCP

IOCP(Input Output Completion Port,输入输出完成端口)

完成端口对象(Completion Port,简称CP对象)

 

实现非阻塞模式的套接字

 

HANDLE CrateIoCompletionPort();//p379-380 创建CP对象、连接完成端口对象和套接字
​
BOOL GetQueuedCompletionStatus();//p381 确认完成端口已完成的I/O和线程的I/O处理

 

第一章 网络编程入门

网络编程Tcp/Ip协议_哔哩哔哩_bilibili

1.1 软件结构

C/S B/S

1.2 网络通信协议

TCP/IP

 

1.3 协议分类

UDP

TCP

 

1.4 网络编程三要素

协议

IP地址

ipconfig(configuration--配置)

ping 空格 IP地址

端口号

 

第二章 TCP通信程序

2.1 概述

 

2.2 Socket类

套接字:包含了IP地址和端口号的网络单位

客户端:

 

服务器端:

 

综合案例

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值