最初参考网上资料完成如下:
说点个人的看法吧:
/*
* 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