socket() // 创建套接字
|
bind() // 分配套接字地址
|
listen() // 等待连接请求状态
|
accept() // 允许连接
|
read()/write() // 数据交换
|
close() // 断开连接
socket() // 创建套接字
|
connect() // 请求连接
|
read()/write() // 数据交换
|
close() // 断开连接
- 客户端的IP地址和端口在调用connect函数时自动分配;
流程如下:
- 服务端创建套接字后,连续调用bind、listen函数进入等待状态;
- 客户端通过connect函数发起连接请求;
- 客户端只能等到服务端调用listen函数后才能调用connect函数;
- 客户端调用connect函数前,服务器端有可能率先调用accept,此时服务器端在调用accept函数时进入阻塞状态,知道客户端调用connect函数为止;
具体代码见Code/echo_server.cpp、Code/echo_client.cpp;
代码中存在的可能的问题:
write(sock, message, strlen(message));
int len = read(sock, message, BUF_SIZE - 1);
message[len] = '\0';
它每次调用read、write都是以字符串为单位执行实际的I/O操作;
因为TCP不存在数据边界,客户端也是基于TCP,因此多次write函数传递的字符串可能一次性传递到服务器端。还要考虑字符串过长,需要分2个数据包发送。
由上面客户端中可能存在问题的客户端代码可以发现:如果等待的时间长一些,调用read函数可以一次性读取所有的字符串数据。但这个时间并不确定,可能很长!
但是理想的客户端应该能够收到字符串数据后立即读取并输出!
解决方案:因为可以确定接收数据的大小;若之前传输了多少数据,则在接收时循环调用read函数读取多少数据即可。
修改后部分的代码:
int len = write(sock, message, strlen(message));
int recv_len = 0;
while (recv_len < len) {
int cnt = read(sock, &message[recv_len], BUF_SIZE - 1);
if (cnt == -1)
error_handling("read() error");
recv_len += cnt;
}
message[recv_len] = '\0';
- 注意上述循环的条件,如果改成recv_len != len,在发生异常情况使得recv_len > len时,将会无限循环!
- I/O缓冲在每个TCP套接字中单独存在;
- I/O缓冲在创建套接字时自动生成;
- 即使关闭套接字也会继续传递输出缓冲中遗留的数据;
- 关闭套接字将丢失输入缓冲区中的数据;
write函数和windows的send函数并不会在完成向对方主机的数据传输时返回,而实在数据移到输出缓冲时。
但是TCP会保证对输出缓冲数据的传输,所以说write函数在数据传输完成时返回。
3次握手!
- 与对方套接字建立连接;
- 与对方套接字进行数据交换;
- 断开与对方套接字的连接;
套接字以全双工方式工作;
- 如果发生传输错误和超时,会进行重传;
4次握手!