Linux网络编程
TCP
service.c
服务端
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(void)
{
int sfp,nfp;
struct sockaddr_in s_add,c_add;
int sin_size;
unsigned short portnum=0x8888;
char buff[100]={0};
printf("Hello,welcome to my server !\r\n");
sfp = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sfp)
{
printf("socket fail ! \r\n");
return -1;
}
printf("socket ok !\r\n");
bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr=htonl(INADDR_ANY);
s_add.sin_port=htons(portnum);
if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
printf("bind fail !\r\n");
return -1;
}
printf("bind ok !\r\n");
if(-1 == listen(sfp,5))
{
printf("listen fail !\r\n");
return -1;
}
printf("listen ok\r\n");
while(1)
{
sin_size = sizeof(struct sockaddr_in);
nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
if(-1 == nfp)
{
printf("accept fail !\r\n");
return -1;
}
read(nfp,buff,100);
printf("received:%s\n", buff);
if(-1 == write(nfp,buff,strlen(buff)+1))
{
printf("write fail!\r\n");
return -1;
}
printf("write ok!\r\n");
close(nfp);
}
close(sfp);
return 0;
}
makefile:
server:server.o
gcc -o server server.o
server.o:server.c
gcc -c server.c -o server.o
clean:
rm -f *.o server
=====================
客户端
client.c
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(void)
{
int cfd;
int recbytes;
int sin_size;
char buffer[1024]={0};
struct sockaddr_in s_add,c_add;
unsigned short portnum=0x8888;
printf("Hello,welcome to client !\r\n");
cfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == cfd)
{
printf("socket fail ! \r\n");
return -1;
}
printf("socket ok !\r\n");
bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr= inet_addr("127.0.0.1");
s_add.sin_port=htons(portnum);
printf("s_addr = %#x ,port : %#x\r\n",s_add.sin_addr.s_addr,s_add.sin_port);
if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
printf("connect fail !\r\n");
return -1;
}
printf("connect ok !\r\n");
write(cfd,"hello tcp socket", strlen("hello tcp socket")+1);
if(-1 == (recbytes = read(cfd,buffer,1024)))
{
printf("read data fail !\r\n");
return -1;
}
printf("read ok\r\nREC:\r\n");
buffer[recbytes]='\0';
printf("%s\r\n",buffer);
getchar();
close(cfd);
return 0;
}
makefile:
client:client.o
gcc -o client client.o
client.o:client.c
gcc -c client.c -o client.o
clean:
rm -f *.o client
UDP
a端
a.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
int port=6789;
int main(int argc, char** argv)
{
int fd; //套接口描述字
int i=0;
char buf[80];
struct sockaddr_in address;//处理网络通信的地址
bzero(&address,sizeof(address));
address.sin_family=AF_INET;
address.sin_addr.s_addr=inet_addr("127.0.0.1");//这里不一样
address.sin_port=htons(port);
//创建一个 UDP socket
fd=socket(AF_INET,SOCK_DGRAM,0);//IPV4 SOCK_DGRAM 数据报套接字(UDP协议)
for(i=0;i<20;i++)
{
/*
* sprintf(s, "%8d%8d", 123, 4567); //产生:" 123 4567"
* 将格式化后到 字符串存放到s当中
*/
sprintf(buf,"hello udp socket %d",i);
/*int PASCAL FAR sendto( SOCKET s, const char FAR* buf, int len, int flags,const struct sockaddr FAR* to, int tolen);
* s:一个标识套接口的描述字。
* buf:包含待发送数据的缓冲区。
* len:buf缓冲区中数据的长度。
* flags:调用方式标志位。
* to:(可选)指针,指向目的套接口的地址。
* tolen:to所指地址的长度。
*/
sendto(fd,buf,sizeof(buf),0,(struct sockaddr *)&address,sizeof(address));
sleep(1);
}
sprintf(buf,"stop");
sendto(fd,buf,sizeof(buf),0,(struct sockaddr *)&address,sizeof(address));//发送stop 命令
close(fd);
printf("Messages Sent,terminating\n");
exit(0);
return (EXIT_SUCCESS);
}
makefile:
client:a.o
gcc -o client a.o
a.o:a.c
gcc -c a.c -o a.o
clean:
rm -f *.o client
=========================
b端
b.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
int port=6789;
int main(int argc, char** argv)
{
int sin_len;
char message[256];
int fd;
struct sockaddr_in sin;
printf("Waiting for data from sender \n");
bzero(&sin,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_addr.s_addr=htonl(INADDR_ANY);
sin.sin_port=htons(port);
sin_len=sizeof(sin);
fd=socket(AF_INET,SOCK_DGRAM,0);
bind(fd,(struct sockaddr *)&sin,sizeof(sin));
while(1)
{
recvfrom(fd,message,sizeof(message),0,(struct sockaddr *)&sin,&sin_len);
printf("Response from server:%s\n",message);
if(strncmp(message,"stop",4) == 0)//接受到的消息为 “stop”
{
printf("Sender has told me to end the connection\n");
break;
}
}
close(fd);
exit(0);
return (EXIT_SUCCESS);
}
makefile:
server:b.o
gcc -o server b.o
b.o:b.c
gcc -c b.c -o b.o
clean:
rm -f *.o server
服务端select模型
服务器端
main.cpp
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
bool g_bwillexit = false;
typedef struct _CmdParam
{
char type;
char quename[128];
char conntype;
}CmdParam, *PCmdParam;
typedef struct _CmdReply
{
char reply[20];
}CmdReply;
#define MYPORT 1234 // the port users will be connecting to
#define BACKLOG 1500 // how many pending connections queue will hold
#define BUF_SIZE 1024
int fd_A[BACKLOG]; // 客户端连接套接字数组accepted connection fd
int conn_amount; // current connection amount
int writen(int s, char *buff, int len)
{
int nLeft,idx, ret;
nLeft = len;
idx = 0;
ret = 0;
while(nLeft>0)
{
if ((ret=send(s, &buff[idx], nLeft,0))== -1)
{
break;
}
nLeft -= ret;
idx += ret;
}
return idx;
}
int readn(int s,char *buf,int len)
{
int nRev = 0, recvCount = 0;
int length = len;
if(buf == NULL)
return 0;
while(length>0)
{
nRev = recv(s,buf+recvCount,length, 0);
if(nRev == -1 || nRev == 0)
{
break;
}
length -= nRev;
recvCount += nRev;
}
return recvCount;
}
static void GetMyProcPath(char *buff)
{
char exec_name [1024] = {0};
readlink ("/proc/self/exe", exec_name, 1024);
char *pSlash = strrchr(exec_name, '/');
*pSlash = '\0';
strcpy(buff, exec_name);
return;
}
int main(int argc, char *argv[])
{
int sock_fd, new_fd; // listen on sock_fd, new connection on new_fd
struct sockaddr_in server_addr; // server address information
struct sockaddr_in client_addr; // connector's address information
socklen_t sin_size;
int yes = 1;
char buf[BUF_SIZE];
int ret;
int i;
if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("socket\n");
exit(1);
}
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
printf("setsockopt\n");
exit(1);
}
server_addr.sin_family = AF_INET; // host byte order
server_addr.sin_port = htons(MYPORT); // short, network byte order
server_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));
if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
{
printf("bind error\n");
exit(1);
}
if (listen(sock_fd, BACKLOG) == -1)
{
printf("listen\n");
exit(1);
}
printf("listen port %d\n", MYPORT);
fd_set fdsr;
int maxsock;
struct timeval tv;
conn_amount = 0;
sin_size = sizeof(client_addr);
maxsock = sock_fd;
while (g_bwillexit == false)
{
// initialize file descriptor set
FD_ZERO(&fdsr);
FD_SET(sock_fd, &fdsr);
// timeout setting
tv.tv_sec = 30;
tv.tv_usec = 0;
// add active connection to fd set
for (i = 0; i < BACKLOG; i++)
{
if (fd_A[i] != 0)
{
FD_SET(fd_A[i], &fdsr);//fd_set fdsr;里面记录了每个客户端的套接字
}
}
ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);//开始通过fdsr对每个套接字进行监听
if (ret < 0)
{
printf("select error\n");
break;
}
else if (ret == 0)
{
printf("timeout\n");
continue;
}
// check every fd in the set
// 处理已有的连接通信的,收发数据和断开连接
for (i = 0; i < conn_amount; i++)
{
if (FD_ISSET(fd_A[i], &fdsr))//某个客户端套接字有响应
{
CmdParam cmd ={0};
ret = readn(fd_A[i], (char *)&cmd, sizeof(cmd));
if (ret <= 0)
{ // client close
printf("client[%d] close\n", i);
close(fd_A[i]);
FD_CLR(fd_A[i], &fdsr);
fd_A[i] = fd_A[conn_amount-1];
fd_A[conn_amount-1]=0;
conn_amount--;
}
else
{ // receive data
printf("read:%d byetes\n", ret);
if (cmd.type == 'c' && (cmd.conntype == 's' || cmd.conntype=='l'))
{
printf("c and (s or l) command received\n");
CmdReply reply = {0};
strcpy(reply.reply, "ok!");
writen(fd_A[i], (char *)&reply, sizeof(reply));
}
else if (cmd.type == 'c')
{
printf("c command received\n");
CmdReply reply = {0};
strcpy(reply.reply, "ok!");
writen(fd_A[i], (char *)&reply, sizeof(reply));
}
else if (cmd.type == 'q')
{
printf("q command received\n");
}
else
{
printf("unknown command received\n");
CmdReply reply = {0};
strcpy(reply.reply, "unknown cmd");
writen(fd_A[i], (char *)&reply, sizeof(reply));
}
}
}
}
// check whether a new connection comes
// 接受处理新的客户端连接
if (FD_ISSET(sock_fd, &fdsr))//sock_fd belongs to server
{
new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);
if (new_fd <= 0)
{
printf("accept error\n");
continue;
}
// add to fd queue
if (conn_amount < BACKLOG)
{
fd_A[conn_amount++] = new_fd;
printf("new connection client[%d] %s:%d\n", conn_amount,
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
if (new_fd > maxsock)
maxsock = new_fd;
}
else
{
printf("max connections arrive, exit\n");
CmdReply reply = {0};
strcpy(reply.reply, "reject");
writen(new_fd, (char *)&reply, sizeof(reply));
close(new_fd);
}
}
}
// close other connections
for (i = 0; i < BACKLOG; i++)
{
if (fd_A[i] != 0)
{
close(fd_A[i]);
}
}
return 0;
}
makefile:
overpasspull_server:main.o
g++ -g -o overpasspull_server main.o
main.o:main.cpp
g++ -g -c main.cpp -o main.o
clean:
rm -f *.o overpass
=======================
客户端
main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#define MYPORT 1234 // the port users will be connecting to
#define BACKLOG 5 // how many pending connections queue will hold
#define BUF_SIZE 200
int writen(int s, char *buff, int len)
{
int nLeft,idx, ret;
nLeft = len;
idx = 0;
ret = 0;
while(nLeft>0)
{
if ((ret=send(s, &buff[idx], nLeft,0))== -1)
{
break;
}
nLeft -= ret;
idx += ret;
}
return idx;
}
int readn(int s,char *buf,int len)
{
int nRev = 0, recvCount = 0;
int length = len;
if(buf == NULL)
return 0;
while(length>0)
{
nRev = recv(s,buf+recvCount,length, 0);
if(nRev == -1 || nRev == 0)
{
break;
}
length -= nRev;
recvCount += nRev;
}
return recvCount;
}
#define MYPORT 1234
typedef struct _CmdParam
{
char type;
char quename[128];
char conntype;
}CmdParam, *PCmdParam;
typedef struct _CmdReply
{
char reply[20];
}CmdReply;
int main(int argc, char *argv[])
{
int sockfd,sock_dt;
printf("#####################################################\n");
printf("socket test by pafone 19th,April,2009\n");
printf("#####################################################\n");
struct sockaddr_in my_addr;//local ip info
struct sockaddr_in dest_addr; //destnation ip info
//int destport = atoi(argv[2]);
if(-1 == (sockfd = socket(AF_INET,SOCK_STREAM,0)) )
{
printf("error in create socket\n");
exit(0);
}
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(MYPORT);
dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//bzero(&dest_addr.sin_zero,0,8);
memset(&dest_addr.sin_zero,0,8);
//connect
if(-1 == connect(sockfd,(struct sockaddr*)&dest_addr,sizeof(struct sockaddr)))
{
printf("connect error\n");
exit(0);
}
int n_send_len;
CmdParam cmd ={0};
cmd.type = 'c';
//strcpy(cmd.queueip, "10.200.193.169");
strcpy(cmd.quename, "message_stream");
//cmd.conntype = 's';
cmd.conntype = 's';
//strcpy(cmd.queueport, "3371");
n_send_len = writen(sockfd, (char *)&cmd, sizeof(cmd));
printf("%d bytes sent\n",n_send_len);
sleep(5);
CmdReply reply = {0};
int n_read_len = readn(sockfd, (char *)&reply, sizeof(reply));
if (n_read_len)
{
printf("reply:%s, length:%d\n", reply.reply, n_read_len);
}
sleep(5);
memset(&cmd, 0, sizeof(cmd));
cmd.type = 'q';
writen(sockfd, (char *)&cmd, sizeof(cmd));
close(sockfd);
return 0;
}
makefile:
overpasspull_client:main.o
g++ -g -o overpasspull_client main.o
main.o:main.cpp
g++ -g -c main.cpp -o main.o
clean:
rm -f *.o overpass
服务端epoll模型
服务端
server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#define BUFFER_SIZE 40
#define MAX_EVENTS 10
int main(int argc, char * argv[])
{
int server_sockfd;//服务器端套接字
int client_sockfd;//客户端套接字
int len;
struct sockaddr_in my_addr; //服务器网络地址结构体
struct sockaddr_in remote_addr; //客户端网络地址结构体
int sin_size;
char buf[BUFFER_SIZE]; //数据传送的缓冲区
memset (&my_addr ,0,sizeof(my_addr)); //数据初始化--清零
my_addr.sin_family=AF_INET; //设置为IP通信
my_addr.sin_addr.s_addr=INADDR_ANY;// 服务器IP地址--允许连接到所有本地地址上
my_addr.sin_port=htons(8000); //服务器端口号
//创建服务器端套接字--IPv4协议,面向连接通信,TCP协议
if((server sockfd=socket(PF_INET, SOCK_STREAM,0))<0)
{
perror("socket");return 1 ;
}
//将套接字绑定到服务器的网络地址上
if (bind(server_sockfd, (struct sockaddr *)&my_addr ,sizeof(struct sockaddr))<0)
{
perror("bind");
return 1;
}
//监听连接请求--监听队列长度为5
listen(server_sockfd,5);
sin_size=sizeof(struct sockaddr_in) ;
//创建一个epoll句柄
int epoll_fd;
epoll_fd=epoll_create (MAX_EVENTS);
if(epoll_fd==-1)
{
perror("epoll_create failed");
exit(EXIT_FAILURE) ;
}
struct epoll_event ev;//epoll事件结构体
struct epoll_event events [MAX_EVENTS];//事件监听队列
ev.events=EPOLLIN;
ev.data.fd=server_sockfd;
//向epoll注册server_sockfd监听事件
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD,server_sockfd,&ev)==-1)
{
perror("epll_ctl:server_sockfd register failed");
exit(EXIT_FAILURE);
}
int nfds;//epoll监听事件发生的个数
while(1)
{
//等待事件发生
nfds=epoll_wait(epoll_fd,events ,MAX_EVENTS,-1);
if(nfds==-1)
{
printf("start epoll_wait failed\n");
break;
}
int i;
for(i=0;i<nfds;i++)
{
//客户端有新的连接请求
if(events[i].data.fd==server_sockfd)
{
//等待客户端连接请求到达
if((client_sockfd=accept(server_sockfd, (struct sockaddr * )&remote_addr ,&sin_size))<0)
{
printf("accept client_sockfd failed\n");
exit(EXIT_FAILURE);
}
//向epoll注册client_sockfd监听事件
ev.events=EPOLLIN;
ev.data.fd=client_sockfd;
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD,client_sockfd,&ev)==-1)
{
printf("epoll_ctl:client_sockfd register failed\n");
exit(EXIT_FAILURE);
printf("accept client %s\n",inet_ntoa(remote_addr.sin_addr));
}
//客户端有数据发送过来
else
{
client_sockfd=events[i].data.fd;
len=recv(client_sockfd , buf , BUFFER_SIZE,0);
if(len<=0)
{
ev.events=EPOLLIN;
ev.data.fd=client_sockfd;
epoll_ctl(epoll_fd,EPOLL_CTL_DEL,client_sockfd ,&ev);
close(client_sockfd);
printf("client exit %d\n" ,client_sockfd);
break;
}
printf("receive from client:%s\n" ,buf) ;
send(client_spckfd,buf, BUFFER_SIZE,0);
}
}
}
return 0;
}
=============================
客户端
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#define BUFFER_SIZE 40
int main(int argc, char *argv[]){
int client_sockfd;int len;
struct sockaddr_in remote_addr; //服务器端网络地址结构体
char buf[BUFFER_SIZE]; // 数据传送的缓冲区
memset(&remote_addr ,θ ,sizeof(remote_addr)); //数据初始化--清零
remote_addr.sin_family=AF_INET; //设置为IP通信
remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");// 服务器IP地址
remote_addr.sin_port=htons(8000); //服务器端口号
//创建客户端套接字--IPv4协议,面向连接通信,TCP协议
if((client_sockfd=socket(PF_INET , SOCK_STREAM,0))<0)
{
perror("client socket creation failed");
exit(EXIT_FAILURE);
}
//将套接字绑定到服务器的网络地址上
if(connect(client_sockfd, (struct sockaddr *)&remote_addr , sizeof(struct sockaddr))<0)
{
perror ("connect to server failed");
exit(EXIT_FAILURE);
}
//循环监听服务器请求
while(1)
{
printf("Please input the message:");
scanf("%s" ,buf);
//exit
if(strcmp(buf,"exit")==0)
{
break;
}
send(client_sockfd, buf , BUFFER_SIZE,0);
//接收服务端信息
len=recv(client_sockfd, buf , BUFFER_SIZE,0);
printf("receive from server :%s\n",buf);
if(len<0)
{
perror("receive from server failed");
exit(EXIT_FAILURE);
}
}
close(client_sockfd);//关闭套接字
return 0;
}
makefile:
.PHONY:all
all:server client
server:
gcc server.c -o server
gcc client.c -o client
clean:
rm -f server client
Select与POLL区别
Select与POLL,EPOLL都是C种同步的10多路复用模型实现机制,它们的区别为:
1.select的句柄数目受限,在linux/posix_types.h头 文件有这样的声明: #define __FD_SETSIZE 1024表示select最多同时监听1024个fd(64位机默认是2048)。而epoll没有,它的限制是最大的打开文件句柄数目。select需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大。
2.epoll不会随着FD的数目增长而降低效率,select采用轮询的方式扫描文件描述符,fd数量越多,性能越差; epoll维护 了一个队列,直接看队列是不是空就可以了。epoll只会对“活跃”的socket进行操作。这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的,那么只有活跃socket才会主动去调用callback函数(把这个句柄加入队列)
3.无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,epoll使用mmap减少复制开销,加速内核与用户空间的消息传递。
4.poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,但没有最大连接数限制(基于链表来存储的)