已经学习的时候没有想过这个问题,只知道用监听套接字来执行accept操作,获取与客户端的连接,从来就没想过如果对监听套接字执行普通套接字的操作会怎样,今天特意做了个实验,对这三种情况进行测试。
一、 read操作
代码如下:
#include <sys/types.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int listenfd = -1;
int connfd = -1;
int ret;
int epfd = -1;
char buf[1024];
struct epoll_event ep;
struct sockaddr_in sa;
struct sockaddr_in conn_addr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(INADDR_ANY);
sa.sin_port = htons(8080);
bind(listenfd, (struct sockaddr *)&sa, sizeof (sa));
listen(listenfd, 20);
conn_addr.sin_family = AF_INET;
inet_pton(AF_INET, "192.168.9.143", &conn_addr.sin_addr);
conn_addr.sin_port = htons(80);
memcpy(buf, "woaini", sizeof ("woaini"));
if (connect(listenfd, (struct sockaddr *)&conn_addr, sizeof (conn_addr)) < 0) {
perror("connect");
return -1;
}
return 0;
}
上面代码的输出结果,如下图所示:
connect: Transport endpoint is already connected
这个错误对应的错误码是EISCONN,也就是说监听套接字是处于“连接”状态的。至于为什么监听套接字处于“连接”状态,只能去看协议栈了,我现在也回答不上来,弄明白之后再发文章了。
二、 read操作
如果监听套接字处于“连接”状态的话,那我是不是就可以直接进行read或write操作了呢,试一下,代码如下:
#include <sys/types.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int listenfd = -1;
int connfd = -1;
int ret;
int epfd = -1;
char buf[1024];
struct epoll_event ep;
struct sockaddr_in sa;
struct sockaddr_in conn_addr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(INADDR_ANY);
sa.sin_port = htons(8080);
bind(listenfd, (struct sockaddr *)&sa, sizeof (sa));
listen(listenfd, 20);
if (read(listenfd, buf, sizeof (buf)) <=0) {
perror("read");
return -1;
}
return 0;
}
上面代码的输出结果如下:
read: Transport endpoint is not connected
这里返回的错误码是ENOTCONN。奇怪了,connect的时候不是说已经连接了吗,怎么read的时候又报没有连接的错误呢?这个问题也得去看协议栈才能解答,所以学习网络编程一定得学习协议栈,否则不要说自己精通网络编程!
三、 write操作
如果执行write操作会怎样呢,代码如下:
#include <sys/types.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int listenfd = -1;
int connfd = -1;
int ret;
int epfd = -1;
char buf[1024];
struct epoll_event ep;
struct sockaddr_in sa;
struct sockaddr_in conn_addr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(INADDR_ANY);
sa.sin_port = htons(8080);
bind(listenfd, (struct sockaddr *)&sa, sizeof (sa));
listen(listenfd, 20);
if (write(listenfd, buf, sizeof (buf)) <= 0) {
perror("write");
return -1;
}
printf("write: %d\n", ret);
return 0;
}
上面代码运行后,没有任何输出,也就是说代码根本就没执行到printf语句,而且也没有错误输出,这是为什么呢?gdb调试,原来在执行write的时候收到了SIGPIPE信号,所以进程终止了。但是没有任何输出,有点不理解。因为如果进程接收到SIGSEGV信号时,会输出“segment fault”,但是SIGPIPE信号时却没有任何输出。
上面的实验和一些问题可能平时都很少考虑,但就是这些细节问题我相信真正回答上来的人很少,或者根本不屑于回答,但这就是细节,就是在内核代码中的细节。所以在linux下编程,一定要去学些linux的内核源码,学习其中的机制和编程方法。希望能给刚接触linux编程的同学一些启示,不要像我一样走一些弯路。