从写一个TCP套接字的write调用成功返回仅仅表示我们可以重新使用原来的应用进程缓冲区,并不代表对端TCP或应用进程已接收到数据。
对端TCP必须确认收到的数据,伴随来自对端的ACK的不断到达,本端TCP至此才能从套接字发送缓冲区中丢弃已确认的数据,TCP必须为已发送的数据保留一个副本,直到它被对端确认为止。
UDP不保存应用进程数据的副本因此无需一个真正的发送缓冲区,write调用成功返回表示所写的数据报或其所有分片已被加入数据链路层的输出队列。
对于read调用(套接字标志为阻塞),如果接收缓冲区中有20字节,请求读100个字节,就会返回20。对于write调用(套接字标志为阻塞),如果请求写100个字节,而发送缓冲区中只有20个字节的空闲位置,那么write会阻塞,直到把100个字节全部交给发送缓冲区才返回,如果write中得套接字标志为非阻塞,则直接返回20,因此我们可以实现自己的readn和writen函数。
每个TCP套接字都有一个发送缓冲区和一个接收缓冲区,每个UDP套接字都有一个接收缓冲区;
先模拟服务端阻塞:
client:
- struct sockaddr_in serverAdd;
-
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
- serverAdd.sin_port = htons(SERV_PORT);
-
- connfd = socket(AF_INET, SOCK_STREAM, 0);
-
- int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (connResult < 0) {
- printf("连接失败,errno = %d\n",errno);
- close(connfd);
- return ;
- }
- else
- {
- printf("连接成功\n");
- }
-
- ssize_t writeLen;
-
- char sendMsg[246988] = {0};
-
- int count = 0;
- while (1)
- {
- count++;
- if (count == 5) {
- exit(0);
- }
- writeLen = write(connfd, sendMsg, sizeof(sendMsg));
- if (writeLen < 0) {
- printf("发送失败\n");
- close(connfd);
- return ;
- }
- else
- {
- printf("发送成功\n");
- }
-
- }
server:
- int main(int argc, const char * argv[])
- {
-
- struct sockaddr_in serverAdd;
- struct sockaddr_in clientAdd;
-
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);
- serverAdd.sin_port = htons(SERV_PORT);
-
- socklen_t clientAddrLen;
-
- int listenfd = socket(AF_INET, SOCK_STREAM, 0);
- int yes = 1;
- setsockopt(listenfd,
- SOL_SOCKET, SO_REUSEADDR,
- (void *)&yes, sizeof(yes));
-
- if (listenfd < 0) {
- printf("创建socket失败\n");
- close(listenfd);
- return -1;
- }
-
- int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (bindResult < 0) {
- close(listenfd);
- printf("绑定端口失败,errno = %d\n",errno);
- return -1;
- }
- else
- {
- printf("绑定端口成功\n");
- }
-
- listen(listenfd, 20);
-
- int connfd;
- unsigned char recvMsg[246988];
-
-
- clientAddrLen = sizeof(clientAdd);
- connfd = accept(listenfd,(struct sockaddr *)&clientAdd,&clientAddrLen);
- if (connfd < 0) {
- close(listenfd);
- printf("连接失败\n");
- return -1;
- }
- else
- {
- printf("连接成功\n");
- int rcvbuf_len;
- socklen_t len = sizeof(rcvbuf_len);
-
- if( getsockopt( connfd, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf_len, &len ) < 0 ){
- perror("getsockopt: ");
- }
- printf("the recv buf len: %d\n", rcvbuf_len );
-
- }
-
- ssize_t totalLen = 0;
- while (1)
- {
- sleep(1);
- ssize_t readLen = read(connfd, recvMsg, sizeof(recvMsg));
- printf("readLen:%ld\n",readLen);
- if (readLen < 0) {
- printf("读取失败\n");
- return -1;
- }
- else if (readLen == 0) {
- printf("读取完成-len = %ld\n",totalLen);
- close(listenfd);
- return 0;
- }
- else
- {
- totalLen += readLen;
- }
-
- }
-
- close(connfd);
-
- return 0;
- }