网络编程 TCP/UDP

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Dopamy_BusyMonkey/article/details/89432690

目录

1、参考文章:

2、TCP 编程流程:

2.1、端口状态:

3、UDP 编程流程:

3.1、端口状态:

4、WSASocket()和socket()函数区别:

5、SOCK_DGRAM和SOCK_STREAM:

6、send/sendto和recv/recvfrom区别:

7、select

8、epoll


1、参考文章:

理论:基础参考文章

代码:参考代码

2、TCP 编程流程:

2.1、端口状态:

listen():

accept():

connect():

可以看到7688进程有两个socket管道、一个是1002本地监听、另一个是与外界通信socket。

1、这里先关闭客户端socket:

因为是客户端主动触发的关闭,所以服务端的状态变为CLOSE_WAIT状态,客户端状态变为FIN_WAIT_1->FIN_WAIT_2;这时不关闭服务端接受到的客户端的socket,等待2MSL后,就只剩服务端的监听端口了;

如果在2MSL之内,关闭服务端接受到的客户端的socket,则客户端socket进入TIME_WAIT状态:

2、反之先关闭服务端接收到的客户端socket,也是一样的效果;

3、这里直接先关闭服务端的监听socket:

所以任然可以用已建立连接的socket发消息;

3、UDP 编程流程:

3.1、端口状态:

bind():

没有连接状态;

并且关闭客户端socket不会改变服务器进程的状态,只有当服务端的socket关闭时进程才会结束,并且是立即结束。

4、WSASocket()和socket()函数区别:

WSASocket是Windows专用,支持异步操作;socket是unix标准,只能同步操作。

Socket可采用多线程实现非阻塞,winsock是socket的windows平台的实现。winsock是微软专门为windows操作系统开发的socket网络编程接口,而socket是通用网络编程接口。

socket() 函数创建一个通讯端点并返回一个套接口。但是在socket库中例程在应用于阻塞套接口时会阻塞。WSASocket()的发送操作和接收操作都可以被重叠使用。接收函数可以被多次调用,发出接收缓冲区,准备接收到来的数据。发送函数也可以被多次调用,组成一个发送缓冲区队列。可是socket()却只能发过之后等待回消息才可做下一步操作!

5、SOCK_DGRAM和SOCK_STREAM:

SOCK_STREAM 是有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料(如文件)传送。

SOCK_DGRAM 是无保障的面向消息的socket , 主要用于在网络上发广播信息。

SOCK_STREAM 是基于TCP的,数据传输比较有保障。SOCK_DGRAM是基于UDP的,专门用于局域网,基于广播

SOCK_STREAM 是数据流,一般是tcp/ip协议的编程,SOCK_DGRAM分是数据抱,是udp协议网络编程

6、send/sendto和recv/recvfrom区别:

send(),recv()用于TCP,sendto()及recvfrom()用于UDP;

但是send(),recv()也可以用于UDP,sendto()及recvfrom()也可以用于TCP;

如果send(),recv()用于UDP,在send之前先要使用connect(客户端能发服务端能收、反之不行,why??,如果服务端使用recvfrom/sendto,客户端使用recv/send就可以正常收发);

int send(SOCKET s, const char FAR *buf, int len, int flags);
int recv(SOCKET s, char FAR * buf, int len, int flags);
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
int recvfrom(int sockfd, void *buf ,int len ,unsigned int lags ,struct sockaddr *from ,int *fromlen);

recvfrom注意第六个参数,是指针,意思是这个参数是会被修改的;

关于flags参数:

int send(int s, const void *msg, size_t len, int flags); 
flags取值有:
0: 与write()无异
MSG_DONTROUTE:告诉内核,目标主机在本地网络,不用查路由表
MSG_DONTWAIT:将单个I/O操作设置为非阻塞模式
MSG_OOB:指明发送的是带外信息

int recv(int s, void *buf, size_t len, int flags);
flags取值有:
0:常规操作,与read()相同
MSG_DONTWAIT:将单个I/O操作设置为非阻塞模式
MSG_OOB:指明发送的是带外信息
MSG_PEEK:可以查看可读的信息,在接收数据后不会将这些数据丢失
MSG_WAITALL:通知内核直到读到请求的数据字节数时,才返回。

int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); 
flags取值有:
0:常规操作,与read()相同
MSG_OOB:指明发送的是带外信息
MSG_PEEK:可以查看可读的信息,在接收数据后不会将这些数据丢失

int sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen); 
flags取值有:
0: 与write()无异
MSG_DONTROUTE:告诉内核,目标主机在本地网络,不用查路由表
MSG_OOB:指明发送的是带外信息

7、select

int select(int maxfdp, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);

maxfdp:需要检查的文件描述字个数(即检查到fd_set的第几位),数值应该比三组fd_set中所含的最大fd值更大,一般设为三组fd_set中所含的最大fd值加1(如在readset,writeset,exceptset中所含最大的fd为5,则nfds=6,因为fd是从0开始的)。设这个值是为提高效率,使函数不必检查fd_set的所有1024位;

readfds:用来检查可读性的一组文件描述字,(数组)可读句柄,先从该参数中读到可读句柄,然后复制给第一个元素(windows上测试结果);

writefds:同上,用来检查可写性的一组文件描述字;

errorfds:同上,用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内);

timeout:用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。 
有三种可能:

  • timeout=NULL(阻塞:select将一直被阻塞,直到某个文件描述符上发生了事件)
  • timeout所指向的结构设为非零时间(等待固定时间:如果在指定的时间段里有事件发生或者时间耗尽,函数均返回)
  • timeout所指向的结构,时间设为0(非阻塞:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生)

fd_set(它比较重要所以先介绍一下)是一组文件描述字(fd)的集合,它用一位来表示一个fd(下面会仔细介绍),对于fd_set类型通过下面四个宏来操作:

FD_ZERO(fd_set *fdset):将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。

FD_SET(fd_set *fdset):用于在文件描述符集合中增加一个新的文件描述符。 

FD_CLR(fd_set *fdset):用于在文件描述符集合中删除一个文件描述符。 (windows上测试的时候,并没有清除掉

FD_ISSET(int fd,fd_set *fdset):用于测试指定的文件描述符是否在该集合中。

8、epoll

https://www.cnblogs.com/panfeng412/articles/2229095.html

https://www.cnblogs.com/lhzhou/p/4148782.html

https://blog.csdn.net/qq_29108585/article/details/60468522

猜你喜欢

转载自blog.csdn.net/Dopamy_BusyMonkey/article/details/89432690