/* * tcpserver.c *Author:coder_xia * Description:a simple tcp server */ #include <sys/socket.h> #include <netinet/in.h> //sockaddr_in #include <arpa/inet.h> //inet_addr #include <unistd.h> //close #include <stdio.h> #include <stdlib.h> #include <string.h> #define BUFFERSIZE 128 #define TCPPORT 8001 #define BACKLOG 5 //max connection number int main(void) { int server_sockfd, client_sockfd; int addr_len, result, recv_len, on = 1; struct sockaddr_in server_address; struct sockaddr_in client_address; char recvbuf[BUFFERSIZE]; //init address server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = htonl(INADDR_ANY); server_address.sin_port = htons(TCPPORT); addr_len = sizeof(struct sockaddr_in); //new server socket server_sockfd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == server_sockfd) { perror("socket"); exit(EXIT_FAILURE); } //Enable address reuse result = setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); //bind and listen result = bind(server_sockfd, (struct sockaddr*)&server_address, addr_len); if (-1 == result) { perror("bind"); close(server_sockfd); exit(EXIT_FAILURE); } result = listen(server_sockfd, BACKLOG); if (-1 == result) { perror("listen"); close(server_sockfd); exit(EXIT_FAILURE); } while (1) { memset(recvbuf, 0, BUFFERSIZE); printf("server is waiting\n"); //Accept a connection, accept will block here, //so ,there's no need to sleep in this loop like in udp client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_address, &addr_len); //recv message from client recv_len = recv(client_sockfd, recvbuf, BUFFERSIZE, 0); if (-1 == recv_len) { //error while recv perror("recv"); close(client_sockfd); close(server_sockfd); exit(EXIT_FAILURE); } else if (0 == recv_len) { //peer closed the socket close(client_sockfd); close(server_sockfd); exit(EXIT_FAILURE); } printf("server receieve : %s\n", recvbuf); printf("server port : %d\n", ntohs(server_address.sin_port)); printf("client port : %d\n", ntohs(client_address.sin_port)); printf("client addr : %s\n", inet_ntoa(client_address.sin_addr)); //send to client via client_sockfd result = send(client_sockfd, recvbuf, recv_len, 0); if (-1 == result) { perror("send"); close(client_sockfd); close(server_sockfd); exit(EXIT_FAILURE); } } close(server_sockfd); return 0; }
不过这个程序是存在一些问题的,虽然情况下都能运行,不过对异常情况没有任何抵抗力
1、client_sockfd未关闭
2、%s输出方式,如果收到的字符串不包含\0等特殊情况?(关于%s,更多参考http://blog.sina.com.cn/s/blog_514885cf0100thxk.html)
3、server为所有client服务,如果一个client端出错或者关闭,则关闭server,逻辑处理失当
修改while部分如下:
while (1) { memset(recvbuf, 0, BUFFERSIZE); printf("server is waiting\n"); //Accept a connection, accept will block here, //so ,there's no need to sleep in this loop like in udp client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_address, &addr_len); //recv message from client recv_len = recv(client_sockfd, recvbuf, BUFFERSIZE-1, 0); if (-1 == recv_len) { //error while recv perror("recv"); close(client_sockfd); continue; } else if (0 == recv_len) { //peer closed the socket close(client_sockfd); continue; } recvbuf[recv_len] = '\0'; printf("server receieve : %s\n", recvbuf); printf("server port : %d\n", ntohs(server_address.sin_port)); printf("client port : %d\n", ntohs(client_address.sin_port)); printf("client addr : %s\n", inet_ntoa(client_address.sin_addr)); sleep(3); //send to client via client_sockfd result = send(client_sockfd, recvbuf, recv_len, 0); if (result != result) { perror("send"); close(client_sockfd); continue; } close(client_sockfd); }client部分如下:
/* * tcpclient.c * Author : coder_xia * Description:a simple tcp client */ #include <sys/socket.h> #include <netinet/in.h> //sockaddr_in #include <arpa/inet.h> //inet_addr #include <unistd.h> //close #include <stdio.h> #include <stdlib.h> #include <string.h> #define TCPPORT 8001 #define LOCALHOST "127.0.0.1" #define BUFFERSIZE 128 int main(void) { int sockfd, addr_len; struct sockaddr_in address; int result; char strsend[] = "hello server,I am client \n"; char recvbuf[BUFFERSIZE]; //Init address. address.sin_family = AF_INET; address.sin_addr.s_addr = inet_addr(LOCALHOST); address.sin_port = htons(TCPPORT); //zero the left part of struct bzero(&(address.sin_zero), sizeof(address.sin_zero)); addr_len = sizeof(address); //Create socket for the client. //After this, we need not to call bind() sockfd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == sockfd) { perror("socket"); exit(EXIT_FAILURE); } //Connect to the server's socket via sockfd //connect will autoly find an unused port and call bind() to bind socket on that port result = connect(sockfd, (struct sockaddr*)&address, addr_len); if (result == -1) { perror("connect"); exit(1); } //Now we can read/write via sockfd. memset(recvbuf, 0, BUFFERSIZE); result = send(sockfd, strsend, strlen(strsend) + 1, 0); if (-1 == result) { //send error perror("write"); close(sockfd); exit(EXIT_FAILURE); } result = recv(sockfd, recvbuf, BUFFERSIZE, 0); if (-1 == result) { //error while recv perror("recv"); close(sockfd); exit(EXIT_FAILURE); } else if (0 == result) { //server has been closed close(sockfd); exit(EXIT_FAILURE); } printf("recieve from server : %s\n", recvbuf); close(sockfd); return EXIT_SUCCESS; }存在隐患如下:
1、发送时采用strlen(数组)+1方法,如果发送缓冲区包含\0,则无法发送所有数据
2、接收输出%s显示,如接收字符串不包含\0?(实际上,由于是接收保存到数组,所以会查找到数组结束位置,如果为char*,则可能带来越界访问的危害)
解决办法为:修改send一行如下:
result = send(sockfd, strsend, sizeof(strsend), 0);
网上的很多参考的东东,在功能实现上,的确是不会出太大问题的,正常情况下,一般不会出bug,不过健壮性欠佳,当然,也是对我们思考的一个好锻炼。不能只是拿来用啊。
关于linux套接字,更多可以参考http://www.ibm.com/developerworks/cn/linux/l-hisock.html,和http://www.ibm.com/developerworks/cn/linux/l-sockpit/(其中用到了这里面的地址复用)
就这样吧,have a good day