TCP服务器
在网络通信中,服务器端通常需要处理多个客户端,由于多个客户端的请求可能会同时到达,服务器端可采用两种模型来实现:循环服务器和并发服务器。
简单的tcp传输数据的编程实例
TCP编程
server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 128
int main(int argc, char *argv[])
{
int socketfd,connfd;
struct sockaddr_in serveraddr, clientaddr;
socklen_t peerlen;
char buf[BUFFER_SIZE];
if(argc < 3){
printf("Usage : %s <ip> <port>\n",argv[0]);
exit(-1);
}
/*建立 socket 连接*/
if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) ==-1){
perror("socket");
exit(1);
}
printf("server socket succeed,socketfd = %d\n", socketfd);
//清空数组
bzero(&serveraddr, sizeof(serveraddr));
/*设置socketaddr_in结构体中的相关参数*/
serveraddr.sin_family = AF_INET; //Addressfamily一般来说AF_INET(地址族)PF_INET(协议族)
serveraddr.sin_port = htons(atoi(argv[2])); //Portnumber(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //Internetaddress存储IP地址
/*绑定函数bind*/
if((bind(socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0){
perror("bind");
exit(1);
}
printf("bind success\n");
/*设置监听模式,listen*/
if((listen(socketfd, 10) == -1)){
perror("listen\n");
exit(1);
}
printf("server is listening.......\n");
/*调用accept,等待客户端的连接*/
peerlen = sizeof(clientaddr);
while(1){
connfd = accept(socketfd, (struct sockaddr *)&clientaddr, &peerlen);
if(connfd < 0){
perror("accept");
exit(1);
}
printf("accept success\n");
/*接收客户端发送的数据*/
memset(buf, 0, BUFFER_SIZE);
if((recv(connfd, buf, BUFFER_SIZE, 0)) == -1){
perror("recv");
exit(1);
}
printf("Received message : %s\n",buf);
strcpy(buf, "welcome to server");
send(connfd, buf, BUFFER_SIZE,0);
close(connfd);
}
close(socketfd);
return 0;
}
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 128
int main(int argc, char *argv[])
{
int socketfd;
struct sockaddr_in serveraddr;
socklen_t peerlen;
char buf[BUFFER_SIZE]="hello server";
if(argc < 3){
printf("Usage : %s <ip> <port>\n",argv[0]);
exit(-1);
}
/*建立 socket 连接*/
if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) ==-1){
perror("socket");
exit(1);
}
printf("client socket succeed,socketfd = %d\n", socketfd);
//清空数组
bzero(&serveraddr, sizeof(serveraddr));
/*设置socketaddr_in结构体中的相关参数*/
serveraddr.sin_family = AF_INET; //Addressfamily一般来说AF_INET(地址族)PF_INET(协议族)
serveraddr.sin_port = htons(atoi(argv[2])); //Portnumber(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //Internetaddress存储IP地址
/*调用connect,向服务器建立TCP连接*/
if((connect(socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) == -1){
perror("connect");
exit(1);
}
printf("connect success\n");
send(socketfd, buf, BUFFER_SIZE,0);
if((recv(socketfd, buf, BUFFER_SIZE, 0)) == -1){
perror("recv");
exit(1);
}
printf("recv from server:%s\n",buf);
close(socketfd);
return 0;
}
运行结果:
运行时,先运行服务器在运行客户端
循环型服务器
循环型服务器是指服务器端一次的处理每个客户端,直到当前客户端的所有请求全部处理结束,再处理下一个客户端的请求。
**优点:**模型简单;
**缺点:**因为要处理完当前客户端的所有请求,会造成其他客户端等待时间过长。
运行介绍:
- server从连接请求队列中提取请求,建立连接并返回新的已连接的套接字;
- server通过已连接的套接字循环接收数据,处理并发送给client,直到client关闭连接;
- server关闭连接套接字,返回步骤1.
循环型服务器的特点:
- 服务器采用循环嵌套来实现。外层循环依次提取每个客户端的连接请求,建立TCP连接。内鞥循环接收并处理当前客户端的所有数据,直到客户端关闭连接;
- 如果当前客户端的请求没有处理结束,其他客户端就必须等待。
采用这种模型的server不能同时处理多个client的数据请求
编程实例:
下面实现 TCP ECHO 的server和client。
**server:**接收到client数据后,原封不动的发送回去(回射服务);
**client:**用户从键盘输入字符,发送给server并接受返回的数据,直到用户输入quit后UI出。
server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 128
int main(int argc, char *argv[])
{
int socketfd, connfd;
struct sockaddr_in serveraddr, clientaddr;
socklen_t peerlen;
char buf[BUFFER_SIZE];
if(argc < 3){
printf("Usage : %s <ip> <port>\n",argv[0]);
exit(-1);
}
/*建立 socket 连接*/
if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) ==-1){
perror("socket");
exit(1);
}
printf("server socket succeed,socketfd = %d\n", socketfd);
//清空数组
bzero(&serveraddr, sizeof(serveraddr));
/*设置socketaddr_in结构体中的相关参数*/
serveraddr.sin_family = AF_INET; //Addressfamily一般来说AF_INET(地址族)PF_INET(协议族)
serveraddr.sin_port = htons(atoi(argv[2])); //Portnumber(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //Internetaddress存储IP地址
/*绑定函数bind*/
if((bind(socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0){
perror("bind");
exit(1);
}
printf("bind success\n");
/*设置监听模式,listen*/
if((listen(socketfd, 10) == -1)){
perror("listen\n");
exit(1);
}
printf("server is listening.......\n");
/*调用accept,等待客户端的连接*/
peerlen = sizeof(clientaddr);
while(1){
connfd = accept(socketfd, (struct sockaddr *)&clientaddr, &peerlen);
if(connfd < 0){
perror("accept");
exit(1);
}
printf("accept success\n");
/*接收客户端发送的数据*/
memset(buf, 0, BUFFER_SIZE);
while((recv(connfd, buf, BUFFER_SIZE, 0)) > 0){
printf("echo : %s\n",buf);
send(connfd, buf, BUFFER_SIZE,0);
exit(-1);
}
printf("client is closed!\n");
close(connfd);
}
close(socketfd);
exit(0);
}
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 128
int main(int argc, char *argv[])
{
int socketfd;
struct sockaddr_in serveraddr;
socklen_t peerlen;
char buf[BUFFER_SIZE]="hello server";
if(argc < 3){
printf("Usage : %s <ip> <port>\n",argv[0]);
exit(-1);
}
/*建立 socket 连接*/
if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) ==-1){
perror("socket");
exit(1);
}
printf("client socket succeed,socketfd = %d\n", socketfd);
//清空数组
bzero(&serveraddr, sizeof(serveraddr));
/*设置socketaddr_in结构体中的相关参数*/
serveraddr.sin_family = AF_INET; //Addressfamily一般来说AF_INET(地址族)PF_INET(协议族)
serveraddr.sin_port = htons(atoi(argv[2])); //Portnumber(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //Internetaddress存储IP地址
/*调用connect,向服务器建立TCP连接*/
if((connect(socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) == -1){
perror("connect");
exit(1);
}
/*循环从键盘键入*/
while(1){
printf("input> ");
fgets(buf, BUFFER_SIZE, stdin); //从键盘上输入,并存到buf数组中
/*输入 quit 则退出*/
if((strcmp(buf, "quit\n")) == 0)
break;
send(socketfd, buf, BUFFER_SIZE, 0); //发送消息给server
bzero(buf, BUFFER_SIZE);
recv(socketfd, buf, BUFFER_SIZE, 0);
printf("recv from server s : %s", buf);
}
printf("client exit\n");
close(socketfd);
return 0;
}
运行结果:
运行时,先运行服务器在运行客户端
并发服务器
为了提高副武器的并发处理能力,又引入了并发服务器。
**并发服务器:**在服务器端采用多任务机制(多进程或者多线程),分别为每个客户端创建一个任务来处理,极大地提高了服务器的并发处理能力,提升了处理速度。
运行介绍:
- server的父进程从连接请求队列中提取请求,建立连接并返回新的已连接套接字。
- server父进程创建子进程为client服务,client关闭时,子进程结束。
- server父进程关闭已连接套接字,返回步骤1.
特点:
- server父进程一旦接收到client的连接请求,便建立好连接并创建新的子进程。这就意味着每个client在server中都有一个专门的子进程为它服务;
- server的多个子进程同时运行(宏观上),处理多个client
- server的父进程不具体处理client的数据请求。
采用这种模型的server需要避免僵死进程
**编程实例:**多进程实现并发服务器
server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 128
int main(int argc, char *argv[])
{
int socketfd, connfd, n;
pid_t pid;
struct sockaddr_in serveraddr, clientaddr;
socklen_t peerlen;
char buf[BUFFER_SIZE];
if(argc < 3){
printf("Usage : %s <ip> <port>\n",argv[0]);
exit(-1);
}
/*建立 socket 连接*/
if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) ==-1){
perror("socket");
exit(1);
}
printf("server socket succeed,socketfd = %d\n", socketfd);
//清空数组
bzero(&serveraddr, sizeof(serveraddr));
/*设置socketaddr_in结构体中的相关参数*/
serveraddr.sin_family = AF_INET; //Addressfamily一般来说AF_INET(地址族)PF_INET(协议族)
serveraddr.sin_port = htons(atoi(argv[2])); //Portnumber(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //Internetaddress存储IP地址
/*绑定函数bind*/
if((bind(socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0){
perror("bind");
exit(1);
}
printf("bind success\n");
/*设置监听模式,listen*/
if((listen(socketfd, 10) == -1)){
perror("listen\n");
exit(1);
}
printf("server is listening.......\n");
/*调用accept,等待客户端的连接*/
peerlen = sizeof(clientaddr);
while(1){
connfd = accept(socketfd, (struct sockaddr *)&clientaddr, &peerlen);
if(connfd < 0){
perror("accept");
exit(1);
}
printf("accept success\n");
/*接收客户端发送的数据*/
memset(buf, 0, BUFFER_SIZE);
//创建子进程
pid = fork();
if(pid < 0){
perror("fork");
exit(1);
}
else if(pid == 0){ //子进程
//调用recv函数接收数据
while((n = recv(connfd, buf, BUFFER_SIZE, 0)) > 0){
printf("echo : %s\n",buf);
send(connfd, buf, BUFFER_SIZE,0);
exit(-1);
}
printf("client is closed!\n");
exit(0); //子进程结束
}else{
close(connfd);
}
}
close(socketfd);
exit(0);
}
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 128
int main(int argc, char *argv[])
{
int socketfd;
struct sockaddr_in serveraddr;
socklen_t peerlen;
char buf[BUFFER_SIZE]="hello server";
if(argc < 3){
printf("Usage : %s <ip> <port>\n",argv[0]);
exit(-1);
}
/*建立 socket 连接*/
if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) ==-1){
perror("socket");
exit(1);
}
printf("client socket succeed,socketfd = %d\n", socketfd);
//清空数组
bzero(&serveraddr, sizeof(serveraddr));
/*设置socketaddr_in结构体中的相关参数*/
serveraddr.sin_family = AF_INET; //Addressfamily一般来说AF_INET(地址族)PF_INET(协议族)
serveraddr.sin_port = htons(atoi(argv[2])); //Portnumber(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //Internetaddress存储IP地址
/*调用connect,向服务器建立TCP连接*/
if((connect(socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) == -1){
perror("connect");
exit(1);
}
/*循环从键盘键入*/
while(1){
printf("input> ");
fgets(buf, BUFFER_SIZE, stdin); //从键盘上输入,并存到buf数组中
/*输入 quit 则退出*/
if((strcmp(buf, "quit\n")) == 0)
break;
send(socketfd, buf, BUFFER_SIZE, 0); //发送消息给server
bzero(buf, BUFFER_SIZE);
recv(socketfd, buf, BUFFER_SIZE, 0);
printf("recv from server s : %s", buf);
}
printf("client exit\n");
close(socketfd);
return 0;
}
运行结果:
运行时,先运行服务器在运行客户端