源码:tcp_server.c
int main(void) { struct sockaddr_in servaddr, cliaddr; socklen_t cliaddr_len; int listenfd, clifd; char str[INET_ADDRSTRLEN]; char buf[MAXBYTES]; int i, n, len; listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERVER_PORT); bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); listen(listenfd, 128); printf("Accepting connections ...\n"); while (1) { bzero(&buf, sizeof(buf)); cliaddr_len = sizeof(cliaddr); clifd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); if(clifd == -1) { perror("clifd"); continue; } len = read(clifd, buf, sizeof(buf) - 1); if(len != -1) { printf("accept from %s at PORT %d :[%d] %s\n" , inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)) , ntohs(cliaddr.sin_port), len, buf); } else { perror("read"); } close(clifd); } return 0;
测试服务器是否好用可以使用一下命令:
nc 127.0.0.1 8888
2 搭建一个TCP客户端
源码:tcp_client.c
int main(int argc, char *argv[]) { struct sockaddr_in serveraddr; int sockfd, n, ret; char buf[] = "helloworld"; sockfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &serveraddr.sin_addr); serveraddr.sin_port = htons(SERVER_PORT); ret = connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); if(ret == -1) perror("connect"); ret = write(sockfd, buf, strlen(buf)); if(ret == -1) perror("connect"); close(sockfd); return 0; }
需要注意的事:
1.当socket写端关闭的时候,读端read将一直读取到0字节,可以使用这一特性来关闭读端套接字。
2.当socket读端关闭的时候,写端write继续写入数据,将会报错,并且收到一个SIGPIPE信号。
3.当网络异常的时候(如拔掉网线),这个时候read不会返回错误,将阻塞等待数据;write也不会返回错误,仍然可以写入数据,直到写buffer填满,将阻塞write函数。
4.socket有个buffer,并且这个buffer的大小是一定的,当一直往socket write的时候,达到这个buffer的最大值后,write将阻塞。
服务器改写如下:
int main(void) { struct sockaddr_in servaddr, cliaddr; socklen_t cliaddr_len; int listenfd, clifd; char str[INET_ADDRSTRLEN]; char buf[MAXBYTES]; int i, n, len; listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERVER_PORT); bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); listen(listenfd, 128); printf("Accepting connections ...\n"); while (1) { bzero(&buf, sizeof(buf)); cliaddr_len = sizeof(cliaddr); clifd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); //可能被信号中断需要注意 if(clifd == -1) { perror("read"); return -1; } while(1) { len = read(clifd, buf, sizeof(buf) - 1); if(len > 0) { printf("accept from %s at PORT %d :[%d] %s\n" , inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)) , ntohs(cliaddr.sin_port), len, buf); } else if(len == 0) //写端关闭的时候将永远读到0字节,如果读到0字节就可以关闭套接字了 { close(clifd); break; } else { perror("read"); } sleep(1); } } close(listenfd); return 0; }
客户端改写如下:
void sig_handler(int sig) { printf("SIGPIPE\n"); } int main(int argc, char *argv[]) { signal(SIGPIPE, sig_handler); //当读端关闭的时候,如果继续写,则会触发SIGPIPE信号 struct sockaddr_in serveraddr; int sockfd, n, ret; char buf[] = "helloworld"; sockfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &serveraddr.sin_addr); serveraddr.sin_port = htons(SERVER_PORT); int conntimes = 0; while(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1) //有可能链接失败,通过这种方式定时链接 { if(conntimes++ > 10) return -1; sleep(1); perror("connect"); } int num = 0; while(1) { ret = write(sockfd, buf, strlen(buf)); if(ret == -1) { perror("write"); } else num += ret; printf("send bytes %d\n", num); sleep(1); //该值根据需要进行更改。 } close(sockfd); return 0; }