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);
//創(chuàng)建一個 UDP socket
fd=socket(AF_INET,SOCK_DGRAM,0);//IPV4 SOCK_DGRAM 數(shù)據報套接字(UDP協(xié)議)
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:包含待發(fā)送數(shù)據的緩沖區(qū)卦方。
* len:buf緩沖區(qū)中數(shù)據的長度俩檬。
* 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));//發(fā)送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]; // 客戶端連接套接字數(shù)組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對每個套接字進行監(jiān)聽
if (ret < 0)
{
printf("select error\n");
break;
}
else if (ret == 0)
{
printf("timeout\n");
continue;
}
// check every fd in the set
// 處理已有的連接通信的,收發(fā)數(shù)據和斷開連接
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]; //數(shù)據傳送的緩沖區(qū)
memset (&my_addr ,0,sizeof(my_addr)); //數(shù)據初始化--清零
my_addr.sin_family=AF_INET; //設置為IP通信
my_addr.sin_addr.s_addr=INADDR_ANY;// 服務器IP地址--允許連接到所有本地地址上
my_addr.sin_port=htons(8000); //服務器端口號
//創(chuàng)建服務器端套接字--IPv4協(xié)議嚼沿,面向連接通信估盘,TCP協(xié)議
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;
}
//監(jiān)聽連接請求--監(jiān)聽隊列長度為5
listen(server_sockfd,5);
sin_size=sizeof(struct sockaddr_in) ;
//創(chuàng)建一個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];//事件監(jiān)聽隊列
ev.events=EPOLLIN;
ev.data.fd=server_sockfd;
//向epoll注冊server_sockfd監(jiān)聽事件
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監(jiān)聽事件發(fā)生的個數(shù)
while(1)
{
//等待事件發(fā)生
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監(jiān)聽事件
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));
}
//客戶端有數(shù)據發(fā)送過來
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]; // 數(shù)據傳送的緩沖區(qū)
memset(&remote_addr ,θ ,sizeof(remote_addr)); //數(shù)據初始化--清零
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); //服務器端口號
//創(chuàng)建客戶端套接字--IPv4協(xié)議,面向連接通信骡尽,TCP協(xié)議
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);
}
//循環(huán)監(jiān)聽服務器請求
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區(qū)別
Select與POLL遣妥,EPOLL都是C種同步的10多路復用模型實現(xiàn)機制,它們的區(qū)別為:
1.select的句柄數(shù)目受限攀细,在linux/posix_types.h頭 文件有這樣的聲明: #define __FD_SETSIZE 1024表示select最多同時監(jiān)聽1024個fd(64位機默認是2048)箫踩。而epoll沒有爱态,它的限制是最大的打開文件句柄數(shù)目。select需要維護一個用來存放大量fd的數(shù)據結構境钟,這樣會使得用戶空間和內核空間在傳遞該結構時復制開銷大锦担。
2.epoll不會隨著FD的數(shù)目增長而降低效率,select采用輪詢的方式掃描文件描述符慨削,fd數(shù)量越多洞渔,性能越差; epoll維護 了一個隊列,直接看隊列是不是空就可以了缚态。epoll只會對“活躍”的socket進行操作磁椒。這是因為在內核實現(xiàn)中epoll是根據每個fd上面的callback函數(shù)實現(xiàn)的,那么只有活躍socket才會主動去調用callback函數(shù)(把這個句柄加入隊列)
3.無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間玫芦,epoll使用mmap減少復制開銷浆熔,加速內核與用戶空間的消息傳遞。
4.poll本質上和select沒有區(qū)別桥帆,它將用戶傳入的數(shù)組拷貝到內核空間医增,然后查詢每個fd對應的設備狀態(tài),但沒有最大連接數(shù)限制(基于鏈表來存儲的)