已知字符串长度情况下完善回声客户端
# gcc echo_client2.c -o eclient2
# ./eclient2 127.0.0.1 9190
Connected
Input message (Q to quit): orange
Message from server: orange
Input message (Q to quit): hhhhhhhhhhhhhhhhhhhhhhhhhhhh
Message from server: hhhhhhhhhhhhhhhhhhhhhhhhhhhh
Input message (Q to quit): q
服务端同上一章。
定义应用层协议
原书中有些bug,服务端接收到的操作数个数没有转换成int。经fix后代码见附录,运行结果如下:
# ./opserver 9190
Connected client 1
Connected client 2
Connected client 3
# ./opclient 127.0.0.1 9190
Connected
Input operand count: 3
Input operand: 1
Input operand: 5
Input operand: 9
Input operator: +
Operation result: 15
# ./opclient 127.0.0.1 9190
Connected
Input operand count: 4
Input operand: 16
Input operand: 8
Input operand: 4
Input operand: 2
Input operator: -
Operation result: 2
# ./opclient 127.0.0.1 9190
Connected
Input operand count: 2
Input operand: 5
Input operand: 7
Input operator: *
Operation result: 35
TCP原理
I/O缓冲特性:
- I/O缓冲在每个TCP套接字中单独存在。
- I/O缓冲在创建套接字时自动生成。
- 即使关闭套接字也会继续传递输出缓冲区中的遗留数据。
- 关闭套接字将丢失输入缓冲区中的遗留数据。
习题
- 请说明TCP套接字连接设置的三次握手过程。尤其是3次数据交换过程每次收发的数据内容。
1)A向B发起连接,发送SYN包(SYN=1,seg=X)
2)B回复SYN_ACK包(SYN=1,ACK=1,seg=Y,ack number=X+1)
3)最后A向B传输消息(ACK=1,seg=X+1,ack number=Y+1)
从步骤3)开始A就可以携带应用层数据了。- TCP是可靠的数据传输协议,但在通过网络通信的过程中可能丢失数据。请通过ACK和SEQ说明TCP通过何种机制保证丢失数据的可靠传输。
SEQ是当前数据起始字节的序号,加入收到对方返回的ACK刚好是SEQ+数据大小(以字节为单位),则代表正确送达,可以继续发送后续数据包。反之,在计时器超时后进行重传。- TCP套接字中调用write和read函数时数据如何移动?结合I/O缓冲进行说明。
write函数调用瞬间,数据将移至输出缓冲;read函数调用瞬间,从输入缓冲读取数据。- 对方主机的输入缓冲剩余50字节空间时,若本方主机通过write函数请求传输70 字节,请问TCP如何处理这种情况?
将70字节数据放入输出缓冲,然后发送50字节到对方主机,待对方主机的输入缓冲又有剩余时再发送后面的字节。- 第2章示例tcp_server.c和tcp_client.c中,客户端接收服务器端传输的字符串后便退出。现更改程序,使服务器端和客户端各传递1次字符串。考虑到使用TCP协议,所以传递字符串前先以4字节整数型方式传递字符串长度。连接时服务器端和客户端数据传输格式如下。另外,不限制字符串传输顺序及种类,但须进行3次数据交换。
代码见附录。0 0 0 6 H e l l o ? |-- 字符串长度 --|------ 字符串数据 ------|
# gcc tri_server.c -o tserver # ./tserver 9191 Connected client 1 Message from client: Hello? Input message: Hi! Message from client: This is Xiao. Input message: I'm Yao. Message from client: Nice to meet you! Input message: Me, too.
# gcc tri_client.c -o tclient # ./tclient 127.0.0.1 9191 Connected Input message: Hello? Message from server: Hi! Input message: This is Xiao. Message from server: I'm Yao. Input message: Nice to meet you! Message from server: Me, too.
- 创建收发文件的服务器端/客户端,实现顺序如下。
a. 客户端接受用户输入的传输文件名。
b. 客户端请求服务器端传输该文件名所指文件。
c. 如果指定文件存在,服务器端就将其发送给客户端;反之,则断开连接。
代码见附录。# gcc file_server.c -o fserver # ./fserver 9190 Connected client
# gcc file_client.c -o fclient # ./fclient 127.0.0.1 9190 Connected Input file name: exercise6
我的问题
- 把int存到char型数组中后发生了什么?
- fgetc(stdin)的作用?
删除stdin中的"\n"字符,因为前面读取操作数的时候,最后有一个回车,他不能被当做运算符读进来。而前面读操作数之前为什么不fgetc一下呢?因为读取的是%d,它会自动找到整数来读取,忽略先导的空白符(指空格符、制表符、回车符)。但当读取的是%c的时候,scanf把缓冲区的第一个字符返回回去,不管是什么,所以如果不fgetc就会读取出“\n”。 - seq与ack number的关系?
书中的seq与ack number有错误。以图5-4为例进行改正。
A B
| --- seq 1200 (100 bytes data) --> |
| <-- ack number 1300 --- |
| --- seq 1300 (100 bytes data) --> |
| <-- ack number 1400 --- |
- C语言如果不初始化局部整型变量,里面的值是什么?
与编译器有关。对GCC来说,局部整型变量里的值是随机的。所以初始化是必需的。 - 为什么要用fgets读取用户输入,而不是scanf?
scanf遇空白符(空格、回车、制表符)即终止读取,无法一次性读取完整的一个句子。而fgets每次读取一行。
附录
[1] 关于int整数转换存储到字符数组
[2] scanf用法及scanf中有\n的问题
[3] TCP连接建立的三次握手过程可以携带数据吗?
[4] Github