一个tcp select的socket server,注意里面对select的时候和对信号的处理:
#include <stdio.h>
#include <netinet/in.h> // for sockaddr_in
#include <sys/types.h> // for socket
#include <sys/socket.h> // for socket
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include "sockComm.h"
#define MAX_LISTEN_QUEUE 5
#define RECV_BUFFER_SIZE 8
#define SELECT_TIMEOUT_ITV 5
void ouch(int sig)
{
printf("ctrl C caught!\n");
}
/* 一个基于TCP的socket server */
int main()
{
int serverSockfd;
int ret = -1;
struct sockaddr_in server_in;
struct sockaddr_in client_in[MAX_LISTEN_QUEUE];
int newSockfd[MAX_LISTEN_QUEUE];
char * localIp = "192.168.43.75";
char * recvBuf = NULL;
int recvLen;
int sin_size = 0;
int i = 0;
int opt = 1;
fd_set fdSetRead;
struct timeval tv = {0};
struct sigaction sact;
sact.sa_handler = ouch;
sigemptyset(&sact.sa_mask); //将mask清空
sact.sa_flags = SA_RESTART; //影响系统调用被信号中断后的走向
//sact.sa_flags = SA_RESETHAND; //处理一次后恢复默认动作。
sigaction(SIGINT, &sact, 0);
tv.tv_sec = SELECT_TIMEOUT_ITV;
tv.tv_usec = 0;
serverSockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == serverSockfd)
{
SOCK_ERR("create client socket error");
return -1;
}
server_in.sin_family = AF_INET;
server_in.sin_port = htons(6603);
server_in.sin_addr.s_addr = inet_addr(localIp);
memset(&server_in.sin_zero, 0, sizeof(server_in.sin_zero));
//bzero(&(server_in.sin_zero), sizeof(server_in.sin_zero));
/* linux的bug,重新建立连接会报错 */
if (setsockopt(serverSockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0)
{
close(serverSockfd);
SOCK_ERR("Error: Could not set reuse address option");
return -1;
}
ret = bind(serverSockfd, (struct sockaddr *)&server_in, sizeof(struct sockaddr));
if (-1 == ret)
{
SOCK_ERR("bind err");
close(serverSockfd);
return -1;
}
ret = listen(serverSockfd, MAX_LISTEN_QUEUE);
if (-1 == ret)
{
SOCK_ERR("listen err");
close(serverSockfd);
return -1;
}
recvBuf = (char *)malloc(RECV_BUFFER_SIZE * sizeof(char));
memset(recvBuf, 0, RECV_BUFFER_SIZE * sizeof(char));
//while (1)
{
//for (i = 0; i < MAX_LISTEN_QUEUE; i++)
{
sin_size = sizeof(struct sockaddr);
newSockfd[i] = accept(serverSockfd, (struct sockaddr *)&client_in[i], &sin_size);
if (-1 == newSockfd[i])
{
SOCK_ERR("accept error");
}
else
{
//int flags = fcntl(newSockfd[i], F_GETFL, 0);
//fcntl(newSockfd[i], F_SETFL, flags | O_NONBLOCK);
while (1)
{
tv.tv_sec = SELECT_TIMEOUT_ITV;
tv.tv_usec = 0;
FD_ZERO(&fdSetRead);
FD_SET(newSockfd[i], &fdSetRead); //加入read监听队列
ret = select(newSockfd[i] + 1, &fdSetRead, NULL, NULL, &tv);
printf("tv.sec = %d\n", tv.tv_sec); //每次select完之后,tv被赋值为尚未等待的时长,例如设置5s,但select等了2s就返回了,那tv中保存3s。
if (-1 == ret)
{
SOCK_ERR("select error");
if (errno == EINTR) //注意,即使信号设置了SA_RESTART,select也不会restart
{
continue;
}
close(newSockfd[i]);
break;
}
else if (0 != ret)
{
/* 这时fdSetRead会被设置为只包含那些有数据的sockfd集合 */
/* 判断是不是想要的sockfd有数据了,
因为只要有连接上有数据select就会返回 */
if (!FD_ISSET(newSockfd[i], &fdSetRead))
{
continue;
}
recvLen = recv(newSockfd[i], recvBuf, RECV_BUFFER_SIZE * sizeof(char), 0);
if (recvLen == 0)
{
SOCK_LOG("peer closed.\n");
close(newSockfd[i]); //需要同时将newSockfd[i]从监听队列移除
continue;
}
else if (-1 == recvLen)
{
perror("recv error");
}
else
{
SOCK_LOG("%d recv: %s, len = %d\n", newSockfd[i], recvBuf, recvLen);
}
if (!strncmp("end", recvBuf, 3))
{
break;
}
}
else
{
SOCK_LOG("no data\n");
/* 如果是非阻塞(这里MSG_DONTWAIT或fcntl设置F_SETFL为O_NONBLOCK),没有数据也强收的话,会直接返回EAGAIN,即Resource temporarily unavailable */
recv(newSockfd[i], recvBuf, RECV_BUFFER_SIZE * sizeof(char), 0);
}
//sleep(1);
}
close(newSockfd[i]);
}
}
//sleep(2);
}
free(recvBuf);
close(serverSockfd);
SOCK_LOG("end\n");
return 0;
}
client端的代码:
#if 0
socket可使用的协议族的域最常用的是AF_UNIX(AF_LOCAL)和AF_INET,这些协议族在linux/socket.h中定义。
AF_UNIX的套接字地址定义成sockaddr_un结构,在sys/un.h中:
struct sockaddr_un
{
__SOCKADDR_COMMON (sun_);
char sun_path[108]; // Path name.
};
其中__SOCKADDR_COMMON的定义为:
#define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family
所以实际上是:
struct sockaddr_un
{
sa_family_t sun_family;
char sun_path[108];
};
AF_INET的套接字地址定义为sockaddr_in结构,这里的in代表internet。在linux/in.h中:
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of 'struct sockaddr', 即在后面添0直到整个结构体大小和sizeof(sockaddr)相同. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
/* Internet address. */
struct in_addr {
__be32 s_addr;
};
其他数据结构:
typedef uint32_t in_addr_t;
typedef unsigned short int sa_family_t;
struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
有一点很重要,就是一个指向struct sockaddr_in的指针可以声明指向一个sturct sockaddr的结构。
#endif
#include <stdio.h>
#include <netinet/in.h> // for sockaddr_in
#include <sys/types.h> // for socket
#include <sys/socket.h> // for socket
#include <string.h>
#include <stdlib.h>
#include "sockComm.h"
#define MAX_LISTEN_QUEUE 5
#define SEND_BUFFER_SIZE 20
/* 一个基于TCP的socket client */
int main()
{
int clientSockfd = -1;
int ret = -1;
struct sockaddr_in dest_in;
char * destIp = "192.168.3.100";
char * sendBuf[] =
{
"hello server 000!",
"hello server 111!",
"hello server 222!",
"end",
NULL,
};
int i;
clientSockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == clientSockfd)
{
SOCK_ERR("create client socket error");
goto end;
}
dest_in.sin_family = AF_INET;
dest_in.sin_port = htons(6603);
dest_in.sin_addr.s_addr = inet_addr(destIp);
memset(&dest_in.sin_zero, 0, sizeof(dest_in.sin_zero));
//bzero(&(cin.sin_zero), sizeof(cin.sin_zero));
ret = connect(clientSockfd, (struct sockaddr *)&dest_in, sizeof(struct sockaddr));
if (-1 == ret)
{
SOCK_ERR("connect err");
goto end;
}
i = 0;
while (sendBuf[i] != NULL)
{
sleep(8);
ret = send(clientSockfd, sendBuf[i], SEND_BUFFER_SIZE * sizeof(char), 0);
if (-1 == ret)
{
SOCK_ERR("send err");
goto end;
}
SOCK_LOG("send: %s\n", sendBuf[i]);
i++;
}
end:
if (-1 != clientSockfd)
{
close(clientSockfd);
}
#if 0
if (sendBuf)
{
free(sendBuf);
}
#endif
SOCK_LOG("end\n");
return 0;
}
sockComm.h
#ifndef __SOCK_COMM_H__
#define __SOCK_COMM_H__
#ifdef SOCK_DEBUG
#define SOCK_LOG printf
#define SOCK_ERR perror
#else
#define SOCK_LOG
#define SOCK_ERR
#endif
#endif
Makefile:
CC = gcc
LD = ld
CFLAGS += -DSOCK_DEBUG
CLI_OBJS = client_sel.o
SER_OBJS = server_sel.o
sockclient: sockclient.o
@$(CC) $< -o $@
sockclient.o: $(CLI_OBJS)
@$(LD) -r -o $@ $(CLI_OBJS)
%.o: %.c
@$(CC) -c $(CFLAGS) -o $@ $<
sockserver: sockserver.o
@$(CC) $< -o $@
sockserver.o: $(SER_OBJS)
@$(LD) -r -o $@ $(SER_OBJS)
%.o: %.c
@$(CC) -c $(CFLAGS) -o $@ $<
all: clean sockclient sockserver
clean:
@rm -f *.o sockclient sockserver
使用epoll的server(client完全相同):
#include <stdio.h>
#include <netinet/in.h> // for sockaddr_in
#include <sys/types.h> // for socket
#include <sys/socket.h> // for socket
#include <sys/poll.h> //poll, pollfd
#include <netdb.h> //hostent
#include <string.h> // bzero, memset
#include <stdlib.h>
#include <sys/epoll.h> // epoll
#include <asm/fcntl.h> // for fcntl and its params
#include "sockComm.h"
#define MAX_LISTEN_QUEUE 20
#define RECV_BUFFER_SIZE 8
#define EPOLL_TIMEOUT_ITV_SEC 3
#define EPOLL_MAX_SIZE 128
int setnonblocking(int sockfd)
{
if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1)
return -1;
return 0;
}
/* 一个基于TCP的socket server */
int main()
{
int serverSockfd;
int ret = -1;
struct sockaddr_in server_in;
struct sockaddr_in client_in[MAX_LISTEN_QUEUE];
int newSockfd[MAX_LISTEN_QUEUE];
char * localIp = "192.168.3.100";
char * recvBuf = NULL;
int recvLen;
int sin_size = 0;
int i = 0;
int opt = 1;
int epfd = -1;
struct epoll_event event, events[MAX_LISTEN_QUEUE];
int nfds = 0;
int n;
serverSockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == serverSockfd)
{
SOCK_ERR("create client socket error");
return -1;
}
#if 0 //test: get localhost ip addr
char * hostname;
int hostnameLen;
struct hostent * ht;
hostname = (char *)malloc(20 * sizeof(char));
memset(hostname, 0, 20 * sizeof(char));
gethostname(hostname, 20);
printf("hostname is %s\n", hostname);
ht = gethostbyname(hostname);
localIp = inet_ntoa (*((struct in_addr *)(ht->h_addr_list[0])));
SOCK_LOG("server ip is %s\n", localIp);
free(hostname);
#endif
server_in.sin_family = AF_INET;
server_in.sin_port = htons(6603);
server_in.sin_addr.s_addr = inet_addr(localIp);
//memset(&server_in.sin_zero, 0, sizeof(server_in.sin_zero));
bzero(&(server_in.sin_zero), sizeof(server_in.sin_zero));
/* linux的bug,重新建立连接会报错 */
if (setsockopt(serverSockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0)
{
close(serverSockfd);
SOCK_ERR("Error: Could not set reuse address option");
return -1;
}
ret = bind(serverSockfd, (struct sockaddr *)&server_in, sizeof(struct sockaddr));
if (-1 == ret)
{
SOCK_ERR("bind err");
close(serverSockfd);
return -1;
}
ret = listen(serverSockfd, MAX_LISTEN_QUEUE);
if (-1 == ret)
{
SOCK_ERR("listen err");
close(serverSockfd);
return -1;
}
recvBuf = (char *)malloc(RECV_BUFFER_SIZE * sizeof(char));
if (!recvBuf)
{
SOCK_ERR("malloc recvBuf err");
close(serverSockfd);
return -1;
}
memset(recvBuf, 0, RECV_BUFFER_SIZE * sizeof(char));
//while (1)
{
//for (i = 0; i < MAX_LISTEN_QUEUE; i++)
{
sin_size = sizeof(struct sockaddr);
newSockfd[i] = accept(serverSockfd, (struct sockaddr *)&client_in[i], &sin_size);
if (-1 == newSockfd[i])
{
SOCK_ERR("accept error");
}
else
{
epfd = epoll_create(EPOLL_MAX_SIZE); /* epoll要用到一个fd。 */
if (-1 == epfd)
{
SOCK_ERR("epoll error");
goto end;
}
/* 添加fd进去。并注册要监听的事件。 */
event.events = EPOLLIN;
event.events &= ~EPOLLET; //默认是电平触发,所以这句话没必要。
event.data.fd = newSockfd[i];
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, newSockfd[i], &event);
if (-1 == ret)
{
SOCK_ERR("epoll_ctl error");
}
/* 开始接收数据。 */
while (1)
{
/* 监听到的fd集合以及发生的事件会记录在events中。 */
nfds = epoll_wait(epfd, events, MAX_LISTEN_QUEUE, EPOLL_TIMEOUT_ITV_SEC * 1000);
if (-1 == nfds)
{
SOCK_ERR("epoll wait error");
close(newSockfd[i]);
break;
}
else if (nfds != 0) //有监听到,nfds是fd的数量
{
for (n = 0; n < nfds; n++)
{
if (events[n].data.fd == newSockfd[i] && (events[n].events & EPOLLIN))
{
//setnonblocking(events[n].data.fd);
/* 如果是边沿触发的话,这里要用while保证每次都收完再回去epoll_wait。
不然小buffer的话可能收不完整。 */
/* while ( */recvLen = recv(events[n].data.fd, recvBuf, RECV_BUFFER_SIZE * sizeof(char), 0);
{
if (-1 == recvLen)
{
SOCK_ERR("recv error");
break;
}
else
{
SOCK_LOG("%d recv: %s, len = %d\n", events[n].data.fd, recvBuf, recvLen);
}
if (!strncmp("end", recvBuf, 3))
{
epoll_ctl(epfd, EPOLL_CTL_DEL, events[n].data.fd, NULL);
//break;
goto end;
}
}
}
}
}
else
{
SOCK_LOG("no data\n");
}
sleep(1);
}
close(newSockfd[i]);
}
}
//sleep(2);
}
end:
free(recvBuf);
close(serverSockfd);
if (epfd != -1)
close(epfd);
SOCK_LOG("end\n");
return 0;
}
一个简单UDP server:
#include <stdio.h>
#include <netinet/in.h> // for sockaddr_in
#include <sys/types.h> // for socket
#include <sys/socket.h> // for socket
#include <string.h>
#include <stdlib.h>
#include "sockComm.h"
#define MAX_LISTEN_QUEUE 5
#define RECV_BUFFER_SIZE 4
/* 一个基于UDP的socket server */
int main()
{
int serverSockfd;
int ret = -1;
struct sockaddr_in server_in;
struct sockaddr_in client_in[MAX_LISTEN_QUEUE];
int newSockfd[MAX_LISTEN_QUEUE];
char * localIp = "192.168.3.100";
char * remoteIp = "192.168.3.100";
char * recvBuf = NULL;
int recvLen;
int sin_size = 0;
int i = 0;
int opt = 1;
serverSockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == serverSockfd)
{
SOCK_ERR("create client socket error.\n");
return -1;
}
server_in.sin_family = AF_INET;
server_in.sin_port = htons(6601);
server_in.sin_addr.s_addr = inet_addr(localIp);
memset(&server_in.sin_zero, 0, sizeof(server_in.sin_zero));
//bzero(&(server_in.sin_zero), sizeof(server_in.sin_zero));
/* linux的bug,重新建立连接会报错 */
if (setsockopt(serverSockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0)
{
SOCK_ERR("Error: Could not set reuse address option");
goto end;
}
ret = bind(serverSockfd, (struct sockaddr *)&server_in, sizeof(struct sockaddr));
if (-1 == ret)
{
SOCK_ERR("bind err");
goto end;
}
#if 0
ret = listen(serverSockfd, MAX_LISTEN_QUEUE);
if (-1 == ret)
{
SOCK_ERR("listen err");
goto end;
}
#endif
recvBuf = (char *)malloc(RECV_BUFFER_SIZE * sizeof(char));
memset(recvBuf, 0, RECV_BUFFER_SIZE * sizeof(char));
//while (1)
{
//for (i = 0; i < MAX_LISTEN_QUEUE; i++)
{
#if 0
sin_size = sizeof(struct sockaddr);
newSockfd[i] = accept(serverSockfd, (struct sockaddr *)&client_in[i], &sin_size);
if (-1 == newSockfd[i])
{
perror("accept error.\n");
}
else
#endif
client_in[i].sin_family = AF_INET;
client_in[i].sin_port = htons(6601);
client_in[i].sin_addr.s_addr = inet_addr(remoteIp);
memset(&client_in[i].sin_zero, 0, sizeof(client_in[i].sin_zero));
{
sin_size = sizeof(struct sockaddr);
recvLen = recvfrom(serverSockfd, recvBuf, RECV_BUFFER_SIZE * sizeof(char), 0,
(struct sockaddr *)&client_in[i], &sin_size);
if (-1 == recvLen)
{
SOCK_ERR("recv error");
goto end;
}
else
{
SOCK_LOG("recv: %s, len = %d\n", recvBuf, recvLen);
}
}
}
//sleep(2);
}
end:
if (-1 != serverSockfd)
{
close(serverSockfd);
}
if (recvBuf)
{
free(recvBuf);
}
SOCK_LOG("end\n");
return 0;
}
UDP client:
#if 0
socket可使用的协议族的域最常用的是AF_UNIX(AF_LOCAL)和AF_INET,这些协议族在linux/socket.h中定义。
AF_UNIX的套接字地址定义成sockaddr_un结构,在sys/un.h中:
struct sockaddr_un
{
__SOCKADDR_COMMON (sun_);
char sun_path[108]; // Path name.
};
其中__SOCKADDR_COMMON的定义为:
#define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family
所以实际上是:
struct sockaddr_un
{
sa_family_t sun_family;
char sun_path[108];
};
AF_INET的套接字地址定义为sockaddr_in结构,这里的in代表internet。在linux/in.h中:
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of 'struct sockaddr', 即在后面添0直到整个结构体大小和sizeof(sockaddr)相同. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
/* Internet address. */
struct in_addr {
__be32 s_addr;
};
其他数据结构:
typedef uint32_t in_addr_t;
typedef unsigned short int sa_family_t;
struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
有一点很重要,就是一个指向struct sockaddr_in的指针可以声明指向一个sturct sockaddr的结构。
#endif
#include <stdio.h>
#include <netinet/in.h> // for sockaddr_in
#include <sys/types.h> // for socket
#include <sys/socket.h> // for socket
#include <string.h>
#include <stdlib.h>
#include "sockComm.h"
#define MAX_LISTEN_QUEUE 5
#define SEND_BUFFER_SIZE 20
/* 一个基于UDP的socket client */
int main()
{
int clientSockfd = -1;
int ret = -1;
struct sockaddr_in dest_in;
char * destIp = "192.168.3.100";
char * sendBuf = "hello server!";
clientSockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == clientSockfd)
{
SOCK_ERR("create client socket error");
goto end;
}
dest_in.sin_family = AF_INET;
dest_in.sin_port = htons(6601);
dest_in.sin_addr.s_addr = inet_addr(destIp);
memset(&dest_in.sin_zero, 0, sizeof(dest_in.sin_zero));
//bzero(&(cin.sin_zero), sizeof(cin.sin_zero));
#if 0
ret = connect(clientSockfd, (struct sockaddr *)&dest_in, sizeof(struct sockaddr));
if (-1 == ret)
{
SOCK_ERR("connect err.\n");
goto end;
}
#endif
ret = sendto(clientSockfd, sendBuf, SEND_BUFFER_SIZE * sizeof(char), 0,
(struct sockaddr *)&dest_in, sizeof(struct sockaddr));
if (-1 == ret)
{
SOCK_ERR("send err");
goto end;
}
SOCK_LOG("send: %s\n", sendBuf);
end:
if (-1 != clientSockfd)
{
close(clientSockfd);
}
#if 0
if (sendBuf)
{
free(sendBuf);
}
#endif
SOCK_LOG("end\n");
return 0;
}