以下内容纯属个人理解,请大神勿喷,还请大神在评论区指教,谢谢
server.cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(){
//创建套接字
int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//将套接字和IP、端口绑定
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充
serv_addr.sin_family = AF_INET; //使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
serv_addr.sin_port = htons(1234); //端口
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//进入监听状态,等待用户发起请求
listen(serv_sock, 20);
//接收客户端请求
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size = sizeof(clnt_addr);
int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
//向客户端发送数据
char str[] = "http://c.biancheng.net/socket/";
write(clnt_sock, str, sizeof(str));
//关闭套接字
close(clnt_sock);
close(serv_sock);
return 0;
}
client.cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
int main(){
//创建套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
//向服务器(特定的IP和端口)发起请求
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充
serv_addr.sin_family = AF_INET; //使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
serv_addr.sin_port = htons(1234); //端口
connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//读取服务器传回的数据
char buffer[40];
read(sock, buffer, sizeof(buffer)-1);
printf("Message form server: %s\n", buffer);
//关闭套接字
close(sock);
return 0;
}
服务端bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));将serv_sock套接字和ip、端口绑定在一起,服务器listen()监听,客户端请求连接connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)),sock是指服务器数据发送到sock接收缓冲区里面(个人理解),serv_addr保存了服务器的ip和端口,为了到达对方那边请求连接,sizeof(serv_addr),不知道为什么需要这个参数,客户端在收发数据前要使用 connect() 函数和服务器建立连接。建立连接的目的是保证IP地址、端口、物理链路等正确无误,为数据的传输开辟通道。到达了服务器那边请求连接,服务器被唤醒,accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);,serv_sock,为了判断是否接受连接,这个里面绑定有ip和端口嘛,clnt_addr接受客户端serv_addr保存的IP地址和端口号,然后和serv_sock的ip和端口比较一下是不是连我的,是就看情况可以接受呗,不是那就不接受呗(个人理解),clnt_addr_size大小(不知道为什么需要这个参数),accept() 返回一个新的套接字来和客户端通信。连接完成(纯属个人理解,勿喷,希望能在评论区指教)
服务端新的套接字clnt_sock往clnt_sock写数据, write(clnt_sock, str, sizeof(str));read(sock, buffer, sizeof(buffer)-1);客户端读数据,具体流程在下方:
每一个TCP套接字有一个发送缓冲区,当某个进程调用write时,内核从该应用进程的缓冲区中复制所有数据到所写套接字的发送缓冲区。如果该套接字的发送缓冲区容不下该应用进程的所有数据,该应用进程将被投入睡眠,这里假设该套接字是阻塞的,它是通常的默认设置,内核将不从write系统调用返回,直到应用进程缓冲区中的所有数据都复制到套接字发送缓冲区。因此,从写一个TCP套接字的write调用成功返回仅仅表示我们可以重新使用原来的应用进程缓冲区,并不表明对端的TCP或应用进程已接收到数据,对端TCP必须确认收到的数据,伴随来自对端的ACK的不断到达,本端TCP至此才能从套接字发送缓冲区中丢弃已确认的数据。TCP必须为已发送的数据保留一个副本,直到它被对端确认为止。(涉及到计算机网络)
以下摘自:http://c.biancheng.net/view/2349.html
每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。
write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。
TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。
read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。
图:TCP套接字的I/O缓冲区示意图
这些I/O缓冲区特性可整理如下:
- I/O缓冲区在每个TCP套接字中单独存在;
- I/O缓冲区在创建套接字时自动生成;
- 即使关闭套接字也会继续传送输出缓冲区中遗留的数据;
- 关闭套接字将丢失输入缓冲区中的数据。
https://www.cnblogs.com/sjks/p/10933885.html
http://c.biancheng.net/view/2128.html