1、概述
2、字节序
双字的意思:一个字节八位。两个字节就是三十二位
一个十六进制要用四个二进制表示
对内存来说,一个字节可以存放01
01 放一个字节,02放一个字节,03放一个字节,04 放一个字节
LE是小端的意思
低序字节就是04
高字节就是01
3、socket编程步骤
服务器的开发步骤 :
1、套接字是为了后面的网络的操作提供一个基本对接的描述符的接口
(就像我们open 一个文件的fd 一样)
2、通过网络往外面走的通道已经建立好了(socket),但是这个通道是一个空白的通道,我们需要给这个通道添加一些信息(你的ip地址,端口号),把这个ip地址和端口号,绑定到套接字
客户端:
只要给我一个通道,让我知道你的Ip 地址和端口号,调用connect()就可以链接
这个图的connect()的键号应该放在listen()后面有人监听,客户端发起connect(),服务端accept()
客户端建立连接。连接到服务端的时候,他们就建立数据的通道了,
你的 socket()通过bind()确定了Ip地址和端口号
四、linux 提供的API 简析
1、创建套接字
socket函数有点像open函数,它的目标是返回一个int 型 ,int 型也是一个文件描述符,只不过它是个网络描述符,跟我们做文件描述符,有稍微的区别,但是他们的意义是相似的
我们后面对网络的访问,都是基于文件描述符操作的
一般如果我们用 AF_IENT 就配合SOCK_STREAM
如果第一个用AF_IENT
第二个用SOCK_STREAM
第三个写0 ,就会自己分配 IPPROTO_TCP(你写这个宏也可以,写0也可以)
这就是第一步,创建套接字,创建套接字的时候,已经写明我是用哪种协议连接的
2、为套接字添加信息
你创建的套接字是个空的东西 ,
bind()里面的参数
第一个跟fd的意思一样,通过socket 的返回值的sockfd,就是我们的套接字描述符了
第二个是个结构体指针
第三个参数是第二个参数的长度
一般用下面的
补充:地址转化API
第一个函数:
第一个参数是字符串,第二个参数就是我们的ip
第二个函数:
比如我们的服务器接入到客户的连接,我们想知道客户端的ip地址怎么办呢?
3、监听
第二个参数,是监听的总数
4、连接
第二个参数是客户端的地址,
第三个参数是客户端地址的长度
补充:数据的收发
这个read 和write和我们文件的read write是一样的
你可以用read 和write也可以用其他的函数
flags一般作为控制,是否有阻塞
其实除了这些还有呢
后面两套一般用于udp 的连接的
tcp一般用read(),write() 和recv()/send()
send() 发送 recv()接收
5、客户端的connect函数
第一个参数 客户端socket()出来的文件描述符
第二个参数服务器的ip地址和端口号
第三个参数是整个地址的大小
五、socket服务端的代码实现
1、 创建套接字
返回值:
成功,返回一个新的套接字描述符,错误返回负1
创建套接字的代码实现
2、 为套接字添加信息
那么sockadr_in 里面有哪些结构呢?
你可以在 usr/include底下搜索
记得空格然后再大括号
这种方式能搜到它定义的地方
在当前目录底下递归的来找
r:是递归的意思
n:是找出来显示行号
i:是不区分大小写
(找到一个头文件)
然后我们再来看看里面的184行
端口号一般高于5000,小于3000是操作系统用的
作为用户一般用5000 到9000之间比较多
这个端口号会传到网络上去,我们必须调用某个函数,把他变成网络字节序
h是主机的意思
n是网络的意思
s代表短的意思
l代表长的意思
看看需要啥头文件
有时候需要转化一下地址,要不然机器不认识
找一下地址转化的头文件
记得bind()第二个参数强制转化
注意:第一个参数要一个字符串,第二个参数要一个指针
inet_aton的第二个参数取地址
3、监听和连接
当listen 没有收到数据的时候,accept会卡在那里
accept的返回值是一个新的建立的socket的通道。
我们通过套接字连接到这个客户端了,后续的连接选项的操做用accept 的返回值操作
之前的s_fd 可能还要接受别人的连接,一但连上以后,我们就先用c_fd操作
整体的思路:1、调用socket创建一个套接字
2、配置那些东西的时候,首先是协议,
第二个是端口号,端口号要注意字节序
第三个是ip地址,ip地址要注意人眼和机器认识的不一样
最终把你的ip地址和fd 绑定起来
3、listen 去监听套接字,这里面监听了10个套接
4、accept没连接连上的时候,他会卡在这里
写个while(1)不让他退出,当有数据连上来的时候,打印一句话
如何验证写的是成功的还是不成功的呢?
可以在命令提示符下ping 一下,看一看地址能不能通
(虽然是台虚拟机,可是从软件层面上来说,还是两台不一样的电脑)
地址能通
然后再用telnet
如果我们端口号写错了,写成7777
连接失败
记得先运行程序
这就是连接成功的
这样是因为,咱们没有对数据的收发做操作
4、补充
上面的listen 一但配置完就会很快的结束的,程序会卡在accept那边
listen 只是告诉内核,我的这个服务器可以监听10个,10个里面有已经完成三次握手的和正在完成三次握手的,有两个队列合计是10个
accept会去判断你这个队列是否存在已经完成三次握手的客户端,已经完成三次握手的客户端,accept就会继续往下走,跟那个客户端进行连接,通过accept的返回值和客户端连接
我们来改一下上面的代码
首先我们想要获取客户端的信息,然后打印window的地址
memset做数据的清空
运行的结果
用telnet连接
这就是连接上了
连接上以后我们做数据的交互
定义一个readBuf来收取数据
从c_fd里面读,读到readBuf里面,读128个字节
读到以后再给人回复一下(理论上计算一下回复的长度)
运行一下,连接成功后按个s试试
发现是个cmd那面变成了乱码
会不会是cmd 的问题呢?
再用虚拟机试试,先连接
在传输信息
发现还是乱码
原因是,我们发送的数据量太多了
改动一下:定义一个字符串
运行一下,先连接上
然后打一句话试试
六、socket客户端的代码实现
第一个是fd,
第二个是指针
第三个是大小
客户端的代码实现
运行结果
先运行server 会自己卡在那里
等着client运行的时候server 也会运行
7、实现双方聊天
作为服务器我们不希望执行一次就退出
首先呢,如果我们的端口经常被占用,我们可以传参来配置端口
传进来的是字符串,我们要把ASCLL转化成整型数
客户端现在也是只写一次,多次的一会再改
服务端的代码
客户端的代码
运行结果
并且我的服务端不会退出,支持多次连接
运行一次的结果
运行好几次的结果
得出的结论
我的服务端不会退出,支持多次连接
我们可以试试cmd 环境下
连接后按一下s试试
这就实现了我们想要的效果:服务器接入多少个都不会退出
那么现在我想实现父进程和子进程聊天、
客户端:
子进程先改,connect以后我们不想让他退出,就做个while(1)循环,让父进程一直读
读的时候没数据会一直阻塞,写的话,我想有空就写,我可以不断创建子进程来写
服务端也改一下
if(fork() ==0)代表进入子进程
有新客户端进入的时候,调用子进程,子进程在这面等待读取对方的数据
等待之前先fork一下,目的是在这边去输入
记得加参数的判断
运行的结果
先连接上
然后运行
运行一遍可以,但是运行多了就不会接收数据
思路:
有数据进来的时候,我创建一个子进程操作,子进程我希望他干两件事,同时执行两个while(1)
(想要执行两个while(1)要么用线程,要么用fork创建子进程,才能让两个while(1)同时跑)
这个能一直往套接字里面发送,同时还能读取套接字里面的信息
客户端的也修改一下
运行的结果:
可以正常的聊天了
八、多方消息收发
我们实现自动回复,我们不实现获取用户的输入
(对我们来说,客户端1 客户端2 共用一个光标)
我们做一个mrck 按连接进来的顺序,标记一下,当我收到一个连接的时候,让mark++
现在服务端的功能,只能接收每一个客户端发上来的消息,
做个子进程每隔三秒给客户端发一句话,我就知道客户端的消息有没有丢失
对服务端来说呢?都能收到客户端的请求的
运行结果
这边每隔三秒就会收到welcome NO1