服务器端:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<sys/mman.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include<sys/socket.h>
#include<fcntl.h>
#include<errno.h>
//服务端程序
//定义包头
struct packet
{
int len;
char buf[1024];
};
//因为read函数每次读取的字节数长度不确定,所以我们封装一个函数
//让函数每次都读取确定大小的字节数
ssize_t readn(int fd, void *buf, size_t count)
{
//我要接收count个字节数
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;
else
return -1;
}
else if(nread == 0)//对等方关闭了
{
return count - nleft;
}
bufp += nread;
nleft -= nread;
}
return count - nleft ;
}
ssize_t writen(int fd, const void *buf, size_t count)
{
//我要发送count个字节数
size_t nleft = count;//还剩下的需要发送的字节数
ssize_t nwritten;//已经发送的字节数
char *bufp = (char*)buf;
while(nleft >0)
{
if( ( nwritten = write(fd, bufp, nleft) ) < 0 )
{
if(errno == EINTR)//被信号中断,不退出,继续接收
continue;
else
return -1;
}
else if(nwritten == 0)//对等方关闭了
{
continue;
}
bufp += nwritten;
nleft -= nwritten;
}
return count - nleft ;
}
//子进程的处理函数
void do_server(int conn)
{
struct packet recvbuf;
while(1)
{
memset(&recvbuf, 0, sizeof(recvbuf));
int ret = readn(conn, &recvbuf.len, 4);//如果接收到的字节数小于1024,那么就会在readn中一直循环
if(0 < ret)
{
puts("网络中接收到数据");
}
else if( -1 == ret)
{
puts("接收失败");
return;
}
else
{
puts("client_close");
break;
}
int n = ntohl(recvbuf.len);
ret = readn(conn, recvbuf.buf, n);
if( -1 == ret)
{
puts("接收失败");
return;
}
else if(ret <n)
{
puts("client_close");
break;
}
fputs(recvbuf.buf,stdout );
writen(conn, &recvbuf, 4+n);
}
return ;
}
int main()
{
//1 创建套接字
int listenfd ; //创建监听套接字
listenfd = socket(PF_INET, SOCK_STREAM, 0);//最后一个参数可以为0,IPPROTO_TCP
if( listenfd<0 )
{
perror("listenfd socket");
exit(1);
}
//2 绑定一个地址
//将ipv4强制转换为通用地址
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; //设置地址族
servaddr.sin_port = htons(5188); //将端口号转为网络字节序,s表示两个字节
//地址的初始化,以下三种方式均可
// servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//本机的任意地址
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//inet_aton("127.0.0.1", &servaddr.sin_addr);
//设置REUSEADDR在服务器为TIME_WAIT状态时,还允许重启
int on = 1;
if( (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) <0 )
{
puts("setsockopt err");
exit(1);
}
//进行绑定
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0 )
{
perror("bind");
exit(1);
}
if( listen(listenfd, SOMAXCONN) <0 )//SOMAXCONN队列的最大值
{
perror("listen()");
exit(1);
}
//3 accept获取队列中的套接字
struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int conn;//已连接套接字
pid_t pid;
while(1)
{
if ( (conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen) ) < 0 )
{
perror("accept");
exit(1);
}
else
{
printf("conn: %d\n", conn);
printf("ip = %s, port = %d\n", inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port) );
puts("accepted");
pid = fork();
if(-1==pid )
{
perror("fork()");
}
if( 0 == pid)//子进程 关闭监听套接字
{
puts("子进程创建成功\n");
close(listenfd);
do_server(conn);
close(conn);
return 0;
}
else//父进程 关闭连接套接字
{
close(conn);
}
}
}
close(listenfd);
return 0 ;
}
客户端:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<sys/mman.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include<sys/socket.h>
#include<fcntl.h>
#include<errno.h>
//客服端 程序
//定义包头
struct packet
{
int len;
char buf[1024];
};
//因为read函数每次读取的字节数长度不确定,所以我们封装一个函数
//让函数每次都读取确定大小的字节数
ssize_t readn(int fd, void *buf, size_t count)
{
//我要接收count个字节数
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;
else
return -1;
}
else if(nread == 0)//对等方关闭了
{
return count - nleft;
}
bufp += nread;
nleft -= nread;
}
return count - nleft ;
}
ssize_t writen(int fd, const void *buf, size_t count)
{
//我要发送count个字节数
size_t nleft = count;//还剩下的需要发送的字节数
ssize_t nwritten;//已经发送的字节数
char *bufp = (char*)buf;
while(nleft >0)
{
if( ( nwritten = write(fd, bufp, nleft) ) < 0 )
{
if(errno == EINTR)//被信号中断
continue;
else
return -1;
}
else if(nwritten == 0)//对等方关闭了
{
continue;
}
bufp += nwritten;
nleft -= nwritten;
}
return count - nleft ;
}
//客户端接收函数
//接收到的数据为packet,先读取长度,再读取数据
void recvpacket(int sock, struct packet *recvbufaddr)
{
int ret = readn(sock, &(*recvbufaddr).len , 4);//
if(0 < ret)
{
puts("网络中接收到数据");
}
else if( -1 == ret)
{
puts("接收失败");
return;
}
else
{
puts("client_close");
return;
}
int n = ntohl((*recvbufaddr).len);
ret = readn(sock, (*recvbufaddr).buf, n);
if( -1 == ret)
{
puts("接收失败");
return;
}
else if(ret <n)
{
puts("client_close");
return ;
}
fputs( (*recvbufaddr).buf,stdout );
}
int main()
{
//1 创建套接字
int sock ;
sock = socket(AF_INET, SOCK_STREAM, 0);//最后一个参数可以为0,IPPROTO_TCP
if( sock<0 )
{
perror(" socket");
exit(1);
}
//2 设置服务器地址
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; //设置地址族
servaddr.sin_port = htons(5188); //将端口号转为网络字节序,s表示两个字节
//地址的设置,以下两种方式均可
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//inet_aton("127.0.0.1", &servaddr.sin_addr);
//3 进行连接
if( connect(sock, (struct sockaddr *)&servaddr, sizeof(servaddr) ) < 0 )
{
perror("connect");
exit(1);
}
else
{
puts("connected");
}
struct packet sendbuf;
struct packet recvbuf;
memset(&sendbuf, 0 ,sizeof(sendbuf));
memset(&recvbuf, 0, sizeof(recvbuf));
while( ( fgets(sendbuf.buf, sizeof(sendbuf.buf), stdin)) != NULL )
{
int n = strlen(sendbuf.buf);
sendbuf.len = htonl(n) ;
//发送数据
writen(sock, &sendbuf, 4+n );
//接收数据
recvpacket(sock, &recvbuf);
memset(&sendbuf, 0, sizeof(sendbuf));
memset(&recvbuf, 0, sizeof(sendbuf));
}
close(sock);
return 0 ;
}