本次实现一个简单的服务器来了解socket,bind, listen等函数,服务器的主要功能是收到客户端的消息后,原封不动的返回给客户端。
在这里插入代码片
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mytcp.h"
#define LISTENQ 10
#define MAXLINE 1024
int main()
{
int listenfd, connfd;
socklen_t len;
struct sockaddr_in servaddr, cliaddr;
char buff[MAXLINE];
pid_t childpid;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(9000);
if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
printf("bind error\n");
}
if (listen(listenfd, LISTENQ) == -1) {
printf("listen error\n");
}
printf("bind and listen success\n");
for(; ;) {
len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &len);
if (connfd < 0) {
printf("accept error\n");
}
inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff));
printf("connection from %s, port %d\n", buff, ntohs(cliaddr.sin_port));
if ((childpid = fork()) == 0) {
close(listenfd);
str_echo(connfd);
exit(0);
}
close(connfd);
}
}
在这里插入代码片
通过nc作为客户端来验证
nc 127.0.0.1 9000
123//输入
123//返回
客户端正常退出后,子进程终止 ,并给父进程发送SIGCHLD信号,但是父进程没有捕获处理该信号,会导致僵尸进程的产生。需要在父进程中调用wait或者waitpid来处理。
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
通过注册signal在父进程中捕获SIGCHLD来实现子进程的资源释放。
void sig_child(int signo)
{
pid_t pid;
int stat;
while((pid) = waitpid(-1, &stat, WNOHANG) > 0)
printf("child %d terminated\n", pid);
return;
}
改进后的代码如下:
在这里插入代码片
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mytcp.h"
#define LISTENQ 10
#define MAXLINE 1024
int main()
{
int listenfd, connfd;
socklen_t len;
struct sockaddr_in servaddr, cliaddr;
char buff[MAXLINE];
pid_t childpid;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(9000);
if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
printf("bind error\n");
}
if (listen(listenfd, LISTENQ) == -1) {
printf("listen error\n");
}
printf("bind and listen success\n");
signal(SIGCHLD, sig_child);
for(; ;) {
len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &len);
if (connfd < 0) {
printf("accept error\n");
}
inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff));
printf("connection from %s, port %d\n", buff, ntohs(cliaddr.sin_port));
if ((childpid = fork()) == 0) {
close(listenfd);
str_echo(connfd);
exit(0);
}
close(connfd);
}
}
同样可以通过nc来模拟客户端查看结果。