服务器端代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <pthread.h> #define PORT 1234 #define MAXSIZE 1024 struct ARG { int connfd; struct sockaddr_in clientAddr; }; void savedata(char *recvbuf, int len, char *cli_data) { static int index = 0; int i = 0; while (i < len-1) { cli_data[index++] = recvbuf[i]; i++; } cli_data[index] = '\0'; } static int clientProcess(int connfd, struct sockaddr_in clientSock) { int num = 0; int i = 0; char recvBuf[MAXSIZE]; char sendBuf[MAXSIZE]; char clientName[MAXSIZE]; char cli_data[MAXSIZE]; struct timeval val; fd_set fd; int ret; val.tv_sec = 0; val.tv_usec = 0; num = recv(connfd, clientName, MAXSIZE, 0); if (num < 0) { printf("recv clientName message from client!\n"); return -1; } clientName[strlen(clientName)-1] = '\0'; printf("You got's a connection from %s, client name is %s\n", inet_ntoa(clientSock.sin_addr), clientName); while (1) { FD_ZERO(&fd); FD_SET(connfd, &fd); ret = select(FD_SETSIZE, &fd, NULL, NULL, &val); num = recv(connfd, recvBuf, MAXSIZE, 0); if (num == 0) { printf("Client(%s) closed connection\nUser's data:%s\n",clientName, cli_data); close(connfd); return -1; } recvBuf[strlen(recvBuf)-1] = '\0'; printf("received client (%s) message :%s\n", clientName, recvBuf); //保存接收到的信息 savedata(recvBuf, num, cli_data); //将接收到的客户端数据加密返回给客户端 for (i=0; i<num-1; i++) { if ((recvBuf[i]>='a' && recvBuf[i]<='z') || (recvBuf[i]>='A' && recvBuf[i]<='Z')) { recvBuf[i] = recvBuf[i] + 3; if ((recvBuf[i]>'Z' && recvBuf[i]<'Z'+3) || (recvBuf[i]>'z')) { recvBuf[i] = recvBuf[i] -26; } } sendBuf[i] = recvBuf[i]; } sendBuf[num-1] = '\0'; send(connfd, sendBuf, MAXSIZE, 0); memset(sendBuf, 0, MAXSIZE); memset(recvBuf, 0, MAXSIZE); } close(connfd); return 1; } void *funThread(void *arg) { struct ARG *info; info = (struct ARG *)arg; clientProcess(info->connfd, info->clientAddr); free(arg); arg = NULL; pthread_detach(pthread_self()); pthread_exit(NULL); } int main(int argc, char **argv) { int listenfd, connfd; pthread_t pid_t; struct ARG *arg; struct sockaddr_in serveraddr; struct sockaddr_in clientaddr; socklen_t len; listenfd = socket(AF_INET, SOCK_STREAM, 0); if (listenfd < 0) { printf("Creating socket failed!\n"); return -1; } int opt = SO_REUSEADDR; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(PORT); serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { printf("bind fails!\n"); return -1; } if (listen(listenfd, 5) < 0) { printf("listen fails!\n"); return -1; } len = sizeof(clientaddr); for (;;) { connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &len); if (connfd < 0) { printf("accept fails!\n"); return -1; } arg = (struct ARG *)malloc(sizeof(struct ARG)); if (arg == NULL) { printf("malloc memory fails!\n"); return -1; } memset(arg, 0, sizeof(struct ARG)); arg->connfd = connfd; memcpy((void *)&arg->clientAddr, &clientaddr, sizeof(clientaddr)); //处理客户端信息 if (pthread_create(&pid_t, NULL, funThread, (void *)arg) < 0) { printf("Create thread fails!\n"); return -1; } } close(listenfd); }
客户端代码:
#include <string.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #define PORT 1234 #define MAXSIZE 1024 static int clientProcess(FILE *fp, int connfd) { char readLine[MAXSIZE]; char recvLine[MAXSIZE]; int num; printf("connect to server success!\n"); printf("Input client's name:"); if (fgets(readLine, MAXSIZE, fp) == NULL) { printf("fgets fails!\n"); return -1; } send(connfd, readLine, MAXSIZE, 0); while (fgets(readLine, MAXSIZE, fp) != NULL) { if (strncmp(readLine, "quit", strlen("quit")) == 0) { fflush(stdin); exit(-1); } send(connfd, readLine, strlen(readLine), 0); num = recv(connfd, recvLine, MAXSIZE, 0); if (num < 0) { printf("received fails!\n"); return -1; } recvLine[num-1] = '\0'; printf("Server message:%s\n", recvLine); } return 1; } int main(int argc, char **argv) { int sockfd; struct sockaddr_in serverSock; struct hostent *he; if (argc != 2) { printf("Usage: %s <IP Address>\n", argv[0]); return -1; } he = gethostbyname(argv[1]); if (he == NULL) { printf("gethostbyname fails!\n"); return -1; } sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { printf("Create socket fails!\n"); return -1; } bzero(&serverSock, sizeof serverSock); serverSock.sin_family = AF_INET; serverSock.sin_port = htons(PORT); serverSock.sin_addr = *((struct in_addr *)he->h_addr); if (connect(sockfd, (struct sockaddr *)&serverSock, sizeof serverSock) < 0) { printf("connect fails!\n"); return -1; } clientProcess(stdin, sockfd); close(sockfd); return 1; }
编译运行。
首先运行服务器程序,然后打开两个客户端终端并连接上服务器程序。如下图所示:
服务器运行情况:
客户端1运行情况:
客户端2运行情况:
仔细看服务端运行情况就能看到问题,当客户端1退出后,执行
savedata(recvBuf, num, cli_data);
的信息能够打印出来,当客户端2退出时,结果却不打印保存的信息。这是为什么?这就是非线程安全的问题。