【網路】TCP伺服器的實現
socket程式設計基本概念
在TCP/IP協議中,IP地址+埠號標識個唯一的一個程序,“IP地址+埠號”就是socket
在TCP協議中,建立連線需要兩個程序各自有一個socket識別符號,這兩個socket組成的socket pair就標識著唯一的連線
相關概念介紹
網路位元組序
首先呢,我們都知道記憶體中的多位元組資料相對於記憶體地址有大端和小端之分,磁碟檔案中的多位元組資料相對於檔案的偏移也有大端和小端之分。
同理,網路資料流其實也有大端和小端之分。
網路資料流的地址這樣規定:先發出的資料是低地址,後發出的資料是高地址。
TCP/IP協議規定:網路資料流應採用大端位元組序,低地址高位元組
socket地址資料型別
用到的相關函式
socket
作用
建立socket套接字
標頭檔案
#include<sys/types.h>
#include<sys/socket.h>
函式原型
int socket(int domain,int type, int protocol);
引數
domain我們一般選擇AF_INET,表示IPv4
type表示傳輸資料的型別,有位元組流型別和SOCK_STREAM資料報型別SOCK_DGRAM
protocol表示建立的型別方式,我們此時設定為0
返回值
成功返回對應的檔案描述符
錯誤返回-1
bind
作用
進行伺服器的IP地址和埠號的繫結
標頭檔案
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
函式原型
int bind(int sockfd,const struct sockaddr *addr, socklen_t addrlen);
引數
sockfd是伺服器的套接字,就是socket函式的返回值
addr是socket伺服器的地址內容
addrlen是傳入的協議地址的大小
返回值
成功返回0
錯誤返回-1
listen
作用
設定套接字為監聽的狀態
標頭檔案
#include<sys/types.h>
#include<sys/socket.h>
函式原型
int listen(int sockfd,int backlog);
引數
sockfd是要設定的套接字
backlog是伺服器的最大等待佇列的個數
返回值
成功返回0
錯誤返回-1
connect
作用
使客戶端連線到伺服器
標頭檔案
#include<sys/types.h>
#include<sys/socket.h>
函式原型
int connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen);
引數
sockfd表示需要連線到伺服器的客戶端套接字
addr表示伺服器的地址和埠號
addrlen表示的是addr的大小
返回值
成功返回0
錯誤返回-1
accpet
作用
伺服器用來接受連線
標頭檔案
#include<sys/types.h>
#include<sys/socket.h>
函式原型
int accept(int sockfd, struct sockaddr * addr, socklen_t *addrlen);
引數
sockfd表示需要連線的客戶端的套接字
addr是傳出引數,用來傳出客戶端的IP地址和埠號
addrlen標示傳出引數的長度
返回值
成功返回0錯誤返回-1
TCP伺服器的程式碼實現
普通版本
server.c
#include<stdio.h>
#include<sys/socket.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<error.h>
#define SERV_PORT 9999
#define _BACKLOG_ 10
void Usage()
{
printf("Usage: [ipaddr]\n");
}
int main(int argc,char* argv[])
{
if(argc != 2)
{
Usage();
return -1;
}
//1、呼叫socket來開啟一個網路通訊埠
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
perror("socket");
exit(1);
}
//2、伺服器來繫結一個固定的IP地址和埠號
struct sockaddr_in serverSocket;//定義服務端的套接字
struct sockaddr_in clientSocket;//定義客戶端的套接字
bzero(&serverSocket,sizeof(serverSocket));//歸零初始化
serverSocket.sin_family = AF_INET;//設定地址的型別
serverSocket.sin_addr.s_addr = inet_addr(argv[1]);
serverSocket.sin_port = htons(SERV_PORT);//設定埠號,SERV_PORT為自定義的巨集,值設定為9999
if(bind(sock,(struct sockaddr*)&serverSocket,sizeof(struct sockaddr_in))< 0)//進行繫結,並進行判斷
{
perror("bind");
close(sock);
exit(2);
}
//3、進行監聽
if(listen(sock,_BACKLOG_)< 0)
{
perror("listen");
close(sock);
exit(3);
}
printf("繫結和監聽成功...請等待連線...\n");
while(1)
{
//4、接受連線
socklen_t len = 0;//定義長度len
int clientSock = accept(sock, (struct sockaddr*)&clientSocket,&len);//接受連線
if(clientSock < 0)
{
perror("accpet");
exit(4);
}
char buf[INET_ADDRSTRLEN];//定義緩衝區,用來存放IP字串
memset(buf,0,sizeof(buf));//初始化為0
while(1)
{
char tmpBuf[1024];//定義空間來儲存收到與傳送的訊息
ssize_t ret = read(clientSock,tmpBuf,sizeof(tmpBuf));//從客戶端套接字中讀入資料
if(ret < 0)
{
perror("read");
exit(5);
}
tmpBuf[ret] = '\0';
printf("#client :# %s\n",tmpBuf);//列印從客戶端讀入的資料
printf("#server :# ");
memset(tmpBuf,'\0',sizeof(tmpBuf));//將buf空間置零
fgets(tmpBuf,sizeof(tmpBuf),stdin);//讀入需要傳送的資料
write(clientSock,tmpBuf,strlen(tmpBuf));
printf("請等待...\n");
}
}
close(sock);
return 0;
}
client.c
#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<unistd.h>
#include<sys/types.h>
#include<netinet/in.h>
#define SERVER_PORT 9999
void Usage()
{
printf("Usage: [client_ip] [client_port]\n");
}
int main(int argc, char* argv[])
{
if(argc != 3)
{
Usage();
return 1;
}
//呼叫socket來開啟一個網路埠
char buf[1024];
char* client_ip = argv[1];
memset(buf,'\0',sizeof(buf));
int sock = socket(AF_INET,SOCK_STREAM,0);
//定義sockaddr_in結構體,並進行初始化
struct sockaddr_in serverSocket;
serverSocket.sin_family = AF_INET;
serverSocket.sin_addr.s_addr = inet_addr(argv[1]);
serverSocket.sin_port = htons(SERVER_PORT);
//進行伺服器的連結
int ret = connect(sock,(struct sockaddr*)&serverSocket,sizeof(serverSocket));
if(ret < 0)
{
printf("連線失敗...\n");
return 2;
}
printf("連線成功...\n");
while(1)
{
printf("#client :# ");
fflush(stdin);
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = '\0';
if(strcmp(buf,"quit")==0)
break;
write(sock,buf,sizeof(buf));
printf("請等待伺服器響應...\n");
read(sock,buf,sizeof(buf));
printf("#server :# %s",buf);
}
close(sock);
return 0;
}
多程序版本
server.c
#include<stdio.h>
#include<sys/types.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
void Usage()
{
printf("Usage: [ipaddr] [port]\n");
exit(1);
}
int main(int argc,char* argv[])
{
if(argc != 3)
{
Usage();
}
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
perror("sock");
exit(2);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);
if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
{
perror("bind");
exit(3);
}
if(listen(sockfd,10) < 0)
{
perror("listen");
exit(4);
}
printf("wait connect...\n");
while(1)
{
struct sockaddr_in saddr;
socklen_t straddr = sizeof(saddr);
int fd = accept(sockfd,(struct sockaddr *)&saddr,&straddr);
printf("accept connect...\n");
if(fd < 0)
{
continue;
}
pid_t pid = fork();
if(pid < 0)
{
perror("fork");
exit(6);
}
else if(pid > 0)
{
printf("find a new client... ip : %s ... port : %d\n",inet_ntoa(saddr.sin_addr),ntohs(saddr.sin_port));
char buf[1024];
while(1)
{
int s = read(fd, buf,sizeof(buf)-1);
if(s < 0)
{
perror("read");
exit(7);
}
else if(s == 0)
{
printf("connect quit... ip : %s ... port : %d\n",inet_ntoa(saddr.sin_addr),ntohs(saddr.sin_port));
close(fd);
break;
}
else
{
buf[s] = '\0';
printf("#client: %s\n",buf);
write(fd,buf,strlen(buf));
}
}
close(fd);
wait(pid,NULL,0);
}
else
{
continue;
}
}
close(sockfd);
return 0;
}
client.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<string.h>
#include<sys/socket.h>
void Usage()
{
printf("Usage: [ipaddr] [port]\n");
exit(1);
}
int main(int argc,char* argv[])
{
if(argc != 3)
{
Usage();
}
//開啟一個網路埠
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
perror("socket");
exit(2);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);
if(connect(sockfd,(struct sockaddr*)&addr, sizeof(addr))< 0)
{
perror("connect");
exit(3);
}
printf("連線成功...\n");
char buf[1024];
while(1)
{
printf("#client: ");
fflush(stdout);
int s = read(0,buf,sizeof(buf)-1);
if(s <= 0)
{
perror("read");
exit(4);
}
else
{
buf[s-1] = 0;
write(sockfd,buf,strlen(buf));
}
int ret = read(sockfd,buf,sizeof(buf)-1);
if(ret < 0)
{
perror("read");
exit(5);
}
else if(ret == 0)
{
printf("連線已斷開...\n");
break;
}
else
{
buf[s] = '\0';
printf("#server: %s\n",buf);
}
}
close(sockfd);
return 0;
}
多執行緒版本
server.c
#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<unistd.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<pthread.h>
#include<string.h>
void Usage()
{
printf("Usage: [addrip] [port]\n");
exit(1);
}
int StartUp(char* argv[])
{
//建立介面
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
perror("socket");
exit(2);
}
//進行繫結
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);
if(bind(sockfd,(struct sockaddr *)&addr,sizeof(addr)) < 0)
{
perror("bind");
exit(3);
}
//進行監聽
if(listen(sockfd,10) < 0)
{
perror("listen");
exit(4);
}
return sockfd;
}
void* Myhandler(void* arg)
{
int fd = (int)arg;
char buf[1024];
while(1)
{
ssize_t s = read(fd,buf,sizeof(buf));
if(s < 0)//error
{
perror("read");
exit(6);
}
else if(s == 0)//quit
{
printf("client quit...\n");
close(fd);
break;
}
else//write
{
buf[s] = 0;
printf("#client : %s\n",buf);
write(fd,buf,strlen(buf));
}
}
}
int main(int argc,char* argv[])
{
if(argc != 3)
{
Usage();
}
int sockfd = StartUp(argv);
printf("初始化成功...等待連線...\n");
while(1)
{
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int fd = accept(sockfd,(struct sockaddr*)&addr,&addrlen);
if(fd < 0)
{
continue;
}
printf("連線成功... ip : %s , port: %d\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
pthread_t td;
pthread_create(&td,NULL,Myhandler,(void*)fd);
pthread_detach(td);
}
close(sockfd);
return 0;
}
client.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<error.h>
#include<pthread.h>
#include<string.h>
static void Usage()
{
printf("Usage : [ipaddr] [port]\n");
exit(1);
}
int StartUp(char* argv[])
{
//開啟介面
int sockfd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);
if(connect(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
{
perror("connect");
exit(2);
}
return sockfd;
}
int main(int argc,char* argv[])
{
if(argc != 3)
{
Usage();
}
int sockfd = StartUp(argv);
printf("連線成功...\n");
while(1)
{
printf("#client : ");
fflush(stdout);
char buf[1024];
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s <= 0)
{
perror("read");
exit(2);
}
else
{
buf[s-1] = '\0';
write(sockfd,buf,strlen(buf));
}
s = read(sockfd,buf,sizeof(buf)-1);
if(s == 0)
{
printf("server quit...\n");
break;
}
else if(s < 0)
{
perror("read");
exit(3);
}
else
{
buf[s] = '\0';
printf("#server : %s\n",buf);
}
}
close(sockfd);
return 0;
}