目录
Socket编程基础
理解源网络通信本质
认识端口号
端口号的划分
进程号&端口号
源端口&目的端口
理解socket
TCP协议&UDP协议
网络字节序
相关字节序转换系统调用接口
Socket编程接口
常见API
sockaddr 结构
Socket 编程 UDP
V1版本 --echo server
V2版本 --DictServer
V3版本 --简单聊天室
需要注意的一些问题
Socket 编程TCP
V1版本 --echo server
V2版本 --echo server多进程版本
V3版本 --echo server多线程版本
V4版本 --echo server线程池版本
v5版本 --TCP_DIT字典
v6版本 --远程命令执行
Socket编程基础
理解源网络通信本质
进程是人的代表,只要把数据交给进程,人就相当于拿到了数据,在整个网络传输过程中,数据传输并不是目的而是一种手段,最终目的也是把传输的数据交给上层进程被人所看到。
在简化理解:我们所讲的网络之间的通信,可以是主机之间的通信,单最终目的是把数据交给上层进程,所以网络通信的本质也是进程之间的通信(其原理也是让两个进程看到同一份资源)。
认识端口号
数据从网络中传输到目的ip主机上后,经过一层层解包分用,最后到达传输层,然后交给上层应用层的应用进程,但又由于上层应用进程特别多无法辨别到底是把数据交给哪个进程,所以我们引入端口号,来唯一标识一台目的ip主机上的唯一一个进程。
至此:我们可以理解全网唯一的一个网络进程(socket)可以用IP+端口号(port)来进行标识。
端口号的划分
0-1023:知名端口号,比如一些HTTP(80),FTP(21,20),ssh(22)等都是固定的。
1024-65535:可以自动分配的端口号。
进程号&端口号
端口号和进程号都可以标识网络中具体的一个进程,道理上讲我们是可以直接用进程号pid去表示一个确定的进程,但我们又引出了端口号去表示网络唯一进程,这是因为进程号pid属于系统概念,如果用进程号在网络通信中来表示唯一进程,这样做只会让系统管理和网络管理强耦合,引出端口号是为了让系统管理和网络管理进行解耦合。
源端口&目的端口
源端口:来确定源主机哪个进程发送的数据。
目的端口:来确定发送数据是发送给哪个进程的。
理解socket
我们把IP+port叫做socket(套接字)
IP:网络中确定的唯一的一台主机。
port:网络中确定主机的唯一的一个网络进程。
TCP协议&UDP协议
这里只了解一下各自协议特性,方便写代码,后续会详细介绍。
| TCP | UDP |
| 有连接 | 无连接 |
| 可靠的数据传输 | 不可靠的数据传输 |
| 面向字节流 | 面向数据报 |
| 传输层协议 | 传输层协议 |
这里的可靠与不可靠是中兴词,是根据丢包后是否重发决定的。
可靠:需要重新发包意味着做更多工作,实现复杂,占用资源多,但数据传输有保障。
不可靠:实现简单,占用资源少,但数据传输没保障。
所以对于可不可靠:各有各的优缺点,各有各的实现场景。
网络字节序
TCP/IP协议规定:网络数据流应采用大端字节序,即低地址高字节。不管这台主机是大端机还是小端机,都会按照这个TCP/IP规定的网络字节序来发送/接收数据;如果当前发送主机是小端,就需要先将数据转成大端;否则就忽略,直接发送即可;
相关字节序转换系统调用接口
为使网络程序具有可移植性,使同样的 C 代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
h: 主机(host),n:网络(network),l:(32位),s:(16位)
Socket编程接口
常见API
int socket(int domain, int type, int protocol);
创建一个通信的一端,打开网络文件,socket
domain:本地或网络通信 AF_INET:网络通信
type:创建tocket类型 sock_DGRAM //无连接不可靠
protocol:0前两个已经证明时udp// 创建成功 返回 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);// 绑定端口号 (TCP/UDP, 服务器)
int listen(int socket, int backlog);// 开始监听 socket (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);// 接收请求 (TCP, 服务器)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);// 建立连接 (TCP, 客户端)
bzero(&local,sizeof(lockl));//结构体数据清0inet_addr:(IP划分)点分十进制化为4字节+网络字节序
eg:192.1.1.1--->192 1 1 1
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:套接字fd
buf:自定义缓冲区
flags:阻塞式IO如果如果没收到消息,该函数就阻塞等待。
src_addr/addrlen:获取客户端进程
sendto:(同上)
sockaddr结构
AF_INET:网络通信 , AF_UNIX:域间通信(本地通信)
sockaddr_in结构
Socket编程UDP
V1版本 --echo server
简单的回显客户端/服务器端代码,简单的进行通信。
相关项目代码
V2版本 --DictServer
在V1基础上实现一个简单的英汉互译词典。
相关项目代码
V3版本 --简单聊天室
实现简单的聊天功能。
单线程版本
多线程版本
需要注意的一些问题
我们代码中实现的主机ip转网络四字节ip使用的是in_addr。在此我们也可以使用inet_pton函数进行转换。
//主机转网络
int inet_pton(int family,const char *strptr,void *addrptr);
const char* strptr: 原ip字符串
void *addrptr: 写入的addr结构内
eg: inet_pton(AF_INET,ip.c_str(),&_addr.sin_addr);
//网络转主机
inet_ntop(int af, const void *src,char *dst, socklen_t size);
eg:
char ipbuffer[1024];
inet_ntop(AF_INET,&_addr.sin_addr,ipbuffer,sizeof(ipbuffer));
网络转主机也可以使用inet_ntoa(),但我们更推荐使用ntop,因为使用inet_ntoa时,返回的字符串是放在全局的静态存储区,这个区域是共享的,第二次调用的时候会覆盖第一次的结果,而我们使用的inet_ntop()该函数会把转化的字符串型的ip地址放到指定的缓冲区中,不会造成上诉问题。
Socket 编程TCP
本编程主要是为了锻炼编程能力和熟悉相关接口
V1版本 --echo server
简单的echo_server
V2版本 --echo server多进程版本
echo_serve多进程版本
V3版本 --echo server多线程版本
echo_serve多线程版本
V4版本 --echo server线程池版本
echo_serve线程池版本
v5版本 --TCP_DIT字典
TCP_DIT字典版本
v6版本 --远程命令执行
远程命令执行版本