linux中每个进程默认情况下,最多可以打开1024个文件,最多有1024个文件描述符
文件描述符的特点:
1.非负整数
2.从最小可用的数字来分配
3.每个进程启动时默认打开0,1,2三个文件描述符
多路复用针对不止套接字fd,也针对普通的文件描述fd
sendto不阻塞
轮询不需要阻塞,一般写成这样很欠揍
int main (void)
{
fd_set rset;
int maxfd = -1;
struct timeval tout;
fd = socket ( ...);
bind (fd, ...);
listen (fd, ...);
while (1) {
maxfd = fd;
FD_ZERO (&rset);
FD_SET (fd, &rset);
//依次把已经建立好连接fd加入到集合中,记录下来最大的文件描述符maxfd
//...FIXME!!
#if 0
select (maxfd + 1, &rset, NULL, NULL, NULL);
#else
tout.tv_sec = 5;
tout.tv_usec = 0;
select (maxfd + 1, &rset, NULL, NULL, &tout);
#endif
if (FD_ISSET (fd, &rset)) {
newfd = accept (fd, ....);
}
//依次判断已建立连接的客户端是否有数据
//...FIXME!
}
return 0;
}
/*./client serv_ip serv_port */
#include "net.h"
void usage (char *s)
{
printf ("\n%s serv_ip serv_port", s);
printf ("\n\t serv_ip: server ip address");
printf ("\n\t serv_port: server port(>5000)\n\n");
}
int main (int argc, char **argv)
{
int fd = -1;
int port = -1;
struct sockaddr_in sin;
if (argc != 3) {
usage (argv[0]);
exit (1);
}
/* 1. 创建socket fd */
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
perror ("socket");
exit (1);
}
port = atoi (argv[2]);
if (port < 5000) {
usage (argv[0]);
exit (1);
}
/*2.连接服务器 */
/*2.1 填充struct sockaddr_in结构体变量 */
bzero (&sin, sizeof (sin));
sin.sin_family = AF_INET;
sin.sin_port = htons (port); //网络字节序的端口号
#if 0
sin.sin_addr.s_addr = inet_addr (SERV_IP_ADDR);
#else
if (inet_pton (AF_INET, argv[1], (void *) &sin.sin_addr) != 1) {
perror ("inet_pton");
exit (1);
}
#endif
if (connect (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
perror ("connect");
exit (1);
}
printf ("Client staring...OK!\n");
int ret = -1;
fd_set rset;
int maxfd = -1;
struct timeval tout;
char buf[BUFSIZ];
while (1) {
FD_ZERO (&rset);
FD_SET (0, &rset);
FD_SET (fd, &rset);
maxfd = fd;
tout.tv_sec = 5;
tout.tv_usec = 0;
select (maxfd + 1, &rset, NULL, NULL, &tout);
if (FD_ISSET (0, &rset)) { //标准键盘上有输入
//读取键盘输入,发送到网络套接字fd
bzero (buf, BUFSIZ);
do {
ret = read (0, buf, BUFSIZ - 1);
} while (ret < 0 && EINTR == errno);
if (ret < 0) {
perror ("read");
continue;
}
if (!ret)
continue;
if (write (fd, buf, strlen (buf)) < 0) {
perror ("write() to socket");
continue;
}
if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) { //用户输入了quit字符
printf ("Client is exiting!\n");
break;
}
}
if (FD_ISSET (fd, &rset)) { //服务器给发送过来了数据
//读取套接字数据,处理
bzero (buf, BUFSIZ);
do {
ret = read (fd, buf, BUFSIZ - 1);
} while (ret < 0 && EINTR == errno);
if (ret < 0) {
perror ("read from socket");
continue;
}
if (!ret)
break; /* 服务器关闭 */
//There is a BUG,FIXME!!
printf ("server said: %s\n", buf);
if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) { //用户输入了quit字符
printf ("Sender Client is exiting!\n");
break;
}
}
}
/*4.关闭套接字 */
close (fd);
}
#ifndef __MAKEU_NET_H__
#define __MAKEU_NET_H__
#include
#include
#include
#include
#include
#include
#include /* See NOTES */
#include
#include
#include /* superset of previous */
#include
#include
//#include
#include
#define SERV_PORT 5002
#define SERV_IP_ADDR "192.168.7.246"
#define BACKLOG 5
#define QUIT_STR "quit"
#endif
#include
#include
#include "net.h"
void cli_data_handle (void *arg);
void sig_child_handle(int signo)
{
if(SIGCHLD == signo) {
waitpid(-1, NULL, WNOHANG);
}
}
int main (void)
{
int fd = -1;
struct sockaddr_in sin;
signal(SIGCHLD, sig_child_handle);
/* 1. 创建socket fd */
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
perror ("socket");
exit (1);
}
/*优化4: 允许绑定地址快速重用 */
int b_reuse = 1;
setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));
/*2. 绑定 */
/*2.1 填充struct sockaddr_in结构体变量 */
bzero (&sin, sizeof (sin));
sin.sin_family = AF_INET;
sin.sin_port = htons (SERV_PORT); //网络字节序的端口号
/*优化1: 让服务器程序能绑定在任意的IP上 */
#if 1
sin.sin_addr.s_addr = htonl (INADDR_ANY);
#else
if (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {
perror ("inet_pton");
exit (1);
}
#endif
/*2.2 绑定 */
if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
perror ("bind");
exit (1);
}
/*3. 调用listen()把主动套接字变成被动套接字 */
if (listen (fd, BACKLOG) < 0) {
perror ("listen");
exit (1);
}
printf ("Server starting....OK!\n");
int newfd = -1;
/*4. 阻塞等待客户端连接请求 */
struct sockaddr_in cin;
socklen_t addrlen = sizeof (cin);
while(1) {
pid_t pid = -1;
if ((newfd = accept (fd, (struct sockaddr *) &cin, &addrlen)) < 0) {
perror ("accept");
break;
}
/*创建一个子进程用于处理已建立连接的客户的交互数据*/
if((pid = fork()) < 0) {
perror("fork");
break;
}
if(0 == pid) { //子进程中
close(fd);
char ipv4_addr[16];
if (!inet_ntop (AF_INET, (void *) &cin.sin_addr, ipv4_addr, sizeof (cin))) {
perror ("inet_ntop");
exit (1);
}
printf ("Clinet(%s:%d) is connected!\n", ipv4_addr, ntohs(cin.sin_port));
cli_data_handle(&newfd);
return 0;
} else { //实际上此处 pid >0, 父进程中
close(newfd);
}
}
close (fd);
return 0;
}
void cli_data_handle (void *arg)
{
int newfd = *(int *) arg;
printf ("Child handling process: newfd =%d\n", newfd);
//..和newfd进行数据读写
int ret = -1;
char buf[BUFSIZ];
char resp_buf[BUFSIZ+10];
while (1) {
bzero (buf, BUFSIZ);
do {
ret = read (newfd, buf, BUFSIZ - 1);
} while (ret < 0 && EINTR == errno);
if (ret < 0) {
perror ("read");
exit (1);
}
if (!ret) { //对方已经关闭
break;
}
printf ("Receive data: %s\n", buf);
if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) { //用户输入了quit字符
printf ("Client(fd=%d) is exiting!\n", newfd);
break;
}
bzero(resp_buf, BUFSIZ+10);
strncpy(resp_buf, SERV_RESP_STR, strlen(SERV_RESP_STR));
strcat(resp_buf, buf);
do {
ret = write(newfd, resp_buf, strlen(resp_buf));
}while(ret < 0 && EINTR == errno);
}
close (newfd);
}
1 #Makefile
2 #CROSS_COMPILE = arm-linux-gnu-
3 #CC = $(CROSS_COMPILE)gcc
4 CC = gcc
5 DEBUG = -g -o0 -Wall
6 CFLAGS += $(DEBUG)
7 PROGS = ${patsubst %.c, %, ${wildcard *.c}}
8 % : %.c
9 $(CC) $(CFLAGS) $< -o $@ -lpthread
10 all : $(PROGS)
11
这里出现了点问题老师的ubuntu和我的版本不同所以我还需要加两个头文件
具体可看报错合集2
然后有个错误有个宏不认识,man了半天,最后发现是自己定义的
#define SERV_RESP_STR "SERVER:"
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;
typedef struct Node{
int fd;
struct Node *next;
}Node;
#define BACKLOG 5
void Argment(int argc, char *argv[]);
void NodeCreate(Node **p);
void AcceptHandle(int sfd, Node **H);
int ClientHandle(int fd);
int main(int argc, char *argv[])
{
int ret, sfd, nfd = 0;
Addr_in saddr;
fd_set rfds;
Node *H, *p = NULL;
Argment(argc, argv);
NodeCreate(&H);
sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd < 0)
{
perror("socket");
exit(0);
}
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr(argv[1]);
if(bind(sfd, (Addr *)&saddr, sizeof(Addr_in)))
{
perror("bind");
exit(0);
}
if(listen(sfd, BACKLOG))
{
perror("listen");
exit(0);
}
H->fd =sfd;
while(1)
{
FD_ZERO(&rfds);
p = H;
nfd = 0;
while(p != NULL)
{
if(p->fd > nfd)
nfd = p->fd;
FD_SET(p->fd, &rfds);
p = p->next;
}
printf("nfd = %d run select...\n", nfd);
ret =select(nfd+1, &rfds, NULL, NULL, NULL);
if(!ret)
continue;
if(ret < 0)
{
perror("select");
exit(0);
}
p = H;
while(p->next != NULL)
{
if(FD_ISSET(p->fd, &rfds))
{
if(ClientHandle(p->fd) <= 0)
{
close(p->fd);
Node *q = p->next;
p->fd = q->fd;
p->next = q->next;
free(q);
continue;
}
}
p = p->next;
}
if(FD_ISSET(p->fd, &rfds))
AcceptHandle(p->fd, &H);
#if 1
p = H;
puts("");
printf("Node:");
while(p != NULL)
{
printf("%d ", p->fd);
p = p->next;
}
#endif
if(H->next == NULL)
break;
}
close(sfd);
free(H);
return 0;
}
void Argment(int argc, char *argv[])//错误输出
{
if(argc != 3)
{
fprintf(stderr, "%s [addr] [port]\n", argv[0]);
exit(0);
}
}
void NodeCreate(Node **p)//创建节点
{
*p = malloc(sizeof(Node));
if(p == NULL)
{
perror("malloc");
exit(0);
}
bzero(*p, sizeof(Node));
}
void AcceptHandle(int sfd, Node **H)//Accept句柄
{
Node *p = NULL;
Addr_in caddr;
socklen_t caddr_len = sizeof(Addr_in);
int cfd = accept(sfd, (Addr *)&caddr, &caddr_len);
if(cfd < 0)
{
perror("accept");
exit(0);
}
fprintf(stderr, "client %s:%d connect success.\n",
inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
NodeCreate(&p);
p->fd = cfd;
p->next = *H;
*H = p;
}
int ClientHandle(int fd)//客户端句柄
{
int ret;
char buf[1024] = {};
ret = recv(fd, buf, 1024, 0);
if(ret <= 0)
return 0;
printf("fd=%d buf = %s\n",fd, buf);
if(buf[0] == '#')
return 0;
return ret;
}