readn()和writen()并非任何标准的组成部分,这两个函数是我们自己定义的,只是为了使用方便。以下是笔者自己实现的readn和writen函数,可用于客户端和服务器端进行通信,测试时先将服务器端程序运行,然后再运行客户端程序,客户端在linux终端下输入消息,回车变可发送给服务器daunt,只后服务器端会回射消息给客户端。
read()和writen()函数可用于处理tcp传输的粘包问题。
server.c
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <sys/types.h>
#define ERR_EXIT(m) do{ perror(m); exit(EXIT_FAILURE); }while(0)
ssize_t readn(int fd, void *buf, size_t count)
{
int ret;
size_t nleft = count;
ssize_t nread;
char *bufp = (char *) buf;
while(nleft>0)
{
if((nread = read(fd, bufp, nleft)) <0)
{
if(errno == EINTR) /* 被信号中断的情况 */
continue;
return -1;
}
else if(nread == 0) /* 对等方关闭 */
return count - nleft;
bufp += nread;
nleft -= nread;
}
return count;
}
ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char *) buf;
while(nleft>0)
{
/* 通常只要bufp中数据大于nleft,就不会引起阻塞 */
if((nwritten = write(fd, bufp, nleft)) <0)
{
if(errno == EINTR) /* 被信号中断的情况 */
continue;
return -1;
}
else if(nwritten == 0)
continue;
bufp += nwritten;
nleft -= nwritten;
}
return count;
}
void do_service(int conn)
{
char recvbuf[1024];
while(1)
{
/* readn一次读取 sizeof(recvbuf), 也就是1024字节,如果发送方发送数据少于这么多,readn将会阻塞在此处 */
memset(recvbuf, 0, sizeof(recvbuf));
int ret = readn(conn, recvbuf, sizeof(recvbuf));
printf("ret = %d\n", ret);
if(ret == 0)
{
printf("client close.\n");
break;//return -1;
}
else if(ret == -1)
ERR_EXIT("readn error.");
fputs(recvbuf, stdout);
writen(conn, recvbuf, ret);
}
}
int main(void)
{
int listenfd;
signal(SIGCHLD, SIG_IGN); //避免僵尸进程
signal(SIGCHLD, SIG_IGN); //避免僵尸进程
if((listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) <0)
ERR_EXIT("socket error.");
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
{
ERR_EXIT("setsockopt error.");
}
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("bind error.");
if(listen(listenfd, 8) < 0)
ERR_EXIT("listen error.");
struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int connfd;
pid_t pid;
while(1)
{
if((connfd = accept(listenfd, (struct sockaddr *)&peeraddr, &peerlen)) < 0)
ERR_EXIT("accept error.");
else
printf("connect success.\n");
printf("ip = %s, port = %d\n", inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
pid = fork();
if(pid == -1)
ERR_EXIT("fork error.");
/* 子进程 */
if(pid == 0)
{
close(listenfd);
do_service(connfd);
exit(EXIT_SUCCESS);
}
else
close(connfd);
}
return 0;
}
client.c
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <sys/types.h> /* See NOTES */
#include <netinet/in.h>
#define ERR_EXIT(m) do{ perror(m); exit(EXIT_FAILURE); }while(0)
ssize_t readn(int fd, void *buf, size_t count)
{
int ret;
size_t nleft = count;
ssize_t nread;
char *bufp = (char *) buf;
while(nleft>0)
{
if((nread = read(fd, bufp, nleft)) <0)
{
if(errno == EINTR) /* 被信号中断的情况 */
continue;
return -1;
}
else if(nread == 0) /* 对等方关闭 */
return count - nleft;
bufp += nread;
nleft -= nread;
}
return count;
}
ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char *) buf;
while(nleft>0)
{
/* 通常只要bufp中数据大于nleft,就不会引起阻塞 */
if((nwritten = write(fd, bufp, nleft)) <0)
{
if(errno == EINTR) /* 被信号中断的情况 */
continue;
return -1;
}
else if(nwritten == 0)
continue;
bufp += nwritten;
nleft -= nwritten;
}
return count;
}
int main(void)
{
int sockfd;
int writen_len;
if((sockfd =socket(AF_INET, SOCK_STREAM, 0)) < 0)
ERR_EXIT("socket error");
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) <0)
ERR_EXIT("connect error.");
char sendbuf[1024];
char recvbuf[1024];
while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
writen_len = writen(sockfd, sendbuf, sizeof(sendbuf));
printf("writen_len = %d\n", writen_len);
if(writen_len < 0)
{
printf("writen error.\n");
}
readn(sockfd, recvbuf, sizeof(recvbuf));
fputs(recvbuf, stdout);
memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
}
close(sockfd);
return 0;
}