您的位置:首页 > 理论基础 > 计算机网络

Linux网络编程——TCP服务器

2017-08-17 08:57 155 查看
学习笔记,小白可以相互学习,大佬看到能告诉咱理解不对的地方就好了。

1.TCP服务器流程

 1.socket,创建服务器(创建socket套接字)

2.bind,设置服务器的IP地址和端口号(将socket和服务器的IP地址和端口号进行绑定)

3.listen,启动监听(启动服务器)

4. accept,等待服务器的连接请求(如果没有连接请求则等待,如果有连接请求,则建立连接)

while(1) {

5. 读写数据:read/write(具体的操作)

}

6. close关闭

2.TCP客户端流程

1.socket,创建客户端(创建一socket套接字)
2.connect向服务器请求连接,并且建立连接
while(1)
{
3.读写数据:write/read(具体的操作)
}
4close,关闭

3.网络编程相关API

1.socket()创建一个通信套接字

socket
头文件#include<sys/types.h>

#include<sys/socket.h>
原型int socket(int domain,int type,int protocol)
参数domain:协议族表示创建通信套接口的作用范围
AF_INET:
IPV4
AF_INET6:
IPV6
AF_UNIX
: 本地多进程之间的通信

type:类型,表示创建的套接字类型
SOCK_STREAM
流式套接字
SOCKDGRAM
数据报套接字
SOCK_RAM
原始套接字

protocol:协议,通常为0(原始套接字时不为0)
返回值成功:返回新的套接字文件描述符

失败:-1,并设置erron
2.bind()绑定服务器的IP地址和端口号,注意(IP地址本机的IP地址)

bind
头文件#include <sys/types.h> 

#include <sys/socket.h>
原型int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数sockfd:套接字文件描述符,同过socket创建

addr:结构体指针,指向的是存储服务器的IP地址和端口号的结构体

通用地址结构:struct sockaddr {

               sa_family_t sa_family;//地址族协议

               char        sa_data[14];//存储服务器的IP地址和端口号使用麻烦。

           }

专用地址结构: struct sockaddr_in {

               sa_family_t    sin_family; /* 地址族 AF_INET,2byte */

               in_port_t      sin_port;   /* 端口,2byte */

               struct in_addr sin_addr;   /* ipv4地址 */
char sin_zero[8]     //8 byte unsued作为填充

           };

struct in_addr {

               uint32_t       s_addr;     /* address in network byte order */

           }

addlen:地址长度,也即是结构体的大小
返回值成功:地址长度,也就是结构体大小

失败:-1,并设置errno
3.connect()客户端使用的系统调用

connect
头文件#include <sys/types.h> 

#include <sys/socket.h>
原型int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数sockfd:套接字文件描述符,同过socket创建

addr:结构体指针,指向的是存储服务器的IP地址和端口号的结构体

通用地址结构:struct sockaddr {

                sa_family_t sa_family;//地址族协议

                char        sa_data[14];//存储服务器的IP地址和端口号使用麻烦。

            }

专用地址结构: struct sockaddr_in {

                sa_family_t    sin_family; /* 地址族 AF_INET,2byte */

                in_port_t      sin_port;   /* 端口,2byte */

                struct in_addr sin_addr;   /* ipv4地址 */
char sin_zero[8]     //8 byte unsued作为填充

            };

struct in_addr {

               uint32_t       s_addr;     /* address in network byte order */

            }

addlen:地址长度,也即是结构体的大小
返回值成功:地址长度,也就是结构体大小

失败:-1,并设置errno
4.listen()启动服务器,启动监听。启动监听之后,套接字由主动变为被动

listen
头文件#include<sys/types.h>

#include<sys/socket.h>
原型int listen(int sockfd, int backlog);
参数sockfd:套接字文件描述符,通过socket创建之后需要bind

backlog:指定处理请求队列的大小。而不是最大连接数
返回值成功:返回0

失败:-1,并设置erron
5.accept()等待客户端的连接请求,如果没有连接请求则等待,如果有连接请求,则建立连接


socket
头文件#include<sys/types.h>

#include<sys/socket.h>
原型int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
参数sockfd:套接字文件描述符,监听套接字

addr: 用来存储客户端的IP地址和端口号

addrlen: 客户端地址所占空间大小。在使用的时候,必须赋初值
返回值成功:返回新的套接字文件描述符

失败:-1,并设置erron
6.send()

send
头文件#include<sys/types.h>

#include<sys/socket.h>
原型ssize_t send(int sockfd, const void *buf, size_t len, int flags)
参数sockfd:套接字文件描述符

buf:发送缓冲区的首地址

len:发送的字节数

flags:发送的方式()通常为0)
返回值成功:返回发送的字节数

失败:-1,并设置erron
7.recv()

recv
头文件#include<sys/types.h>

#include<sys/socket.h>
原型ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数sockfd:套接字文件描述符

buf:发送缓冲区的首地址

len:发送的字节数

flags:接收的方式()通常为0)
返回值成功:返回接收的字节数

失败:-1,并设置erron
read()write()可以取代recv()send()

/***************服务器server.c********************************/

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<string.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<unistd.h>
int main()
{
int listenfd;
int ret;
int connfd;
char buf[256];
struct sockaddr_in srvaddr;
struct sockaddr_in cltaddr;
socklen_t addlen;
//1.创建服务器(创建socket)
listenfd = socket(AF_INET,SOCK_STREAM,0);
if(listenfd == -1)
{
perror("server->socket");
return -1;
}
printf("creat listenfd = %d success\n",listenfd);
//2.设置服务器的IP地址和端口号(绑定本机地址和端口)
memset(&srvaddr,0,sizeof(struct sockaddr));//从srvaddr开始后面sizeof()个字节写为0
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9999);
srvaddr.sin_addr.s_addr = inet_addr("192.168.2.27");
ret = bind(listenfd,(const struct sockaddr *)&srvaddr,sizeof(struct sockaddr));
if(-1 == ret)
{
perror("server->bind");
return -1;
}
//3.启动监听(启动服务器)
ret = listen(listenfd,0);
if(-1 == ret)
{
perror("server->listen");
return -1;
}
printf("listen success\n");
//4.等待客户端的连接请求
addlen = sizeof(socklen_t);
connfd = accept(listenfd,(struct sockaddr *)&cltaddr,&addlen);
if(-1 == connfd)
{
perror("server->accept");
return -1;
}

while(1)
{
//5.读写数据read/write
memset(buf,0,sizeof(buf));
ret = read(connfd,buf,sizeof(buf));
if(-1 == ret)
{
perror("client->read");
return -1;
}
printf("buf : %s\n",buf);
ret = write(connfd,buf,sizeof(buf));
if(-1 == ret)
{
perror("client->write");
return -1;
}
}
//6.关闭
close(connfd);
close(listenfd);
return 0;
}


/******************客户端client.c**************************************/
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<string.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<unistd.h>
int main()
{
int sockfd;
int ret;
int connfd;
char buf[256];
struct sockaddr_in srvaddr;
socklen_t addlen;
//1.创建客户端(创建socket)
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1)
{
perror("client->socket");
return -1;
}
printf("creat listenfd = %d success\n",sockfd);
//2.向服务器请求连接并且建立连接(connect)
memset(&srvaddr,0,sizeof(struct sockaddr));//从srvaddr开始后面sizeof()个字节写为0
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9999);
srvaddr.sin_addr.s_addr = inet_addr("192.168.2.27");
ret = connect(sockfd,(const struct sockaddr *)&srvaddr,sizeof(struct sockaddr));
if(-1 == ret)
{
perror("client->bind");
return -1;
}
while(1)
{
//3.读写数据read/write

fgets(buf,sizeof(buf),stdin);
ret = write(sockfd,buf,sizeof(buf));
if(-1 == ret)
{
perror("client->write");
return -1;
}
printf("buf : %s\n",buf);

memset(buf,0,sizeof(buf));
ret = read(sockfd,buf,sizeof(buf));
if(-1 == ret)
{
perror("client->read");
return -1;
}
printf("buf : %s\n",buf);

if(0 == strncmp(buf,"quit",4))
{
printf("quit success\n");
break;
}
}
//4.关闭
close(sockfd);
return 0;
}


//***********makefile******************
all:
gcc server.c -o server
gcc client.c -o client

2.TCP循环服务器流程

b400
socket();
bind();
listen();
while(1)
{
accepr();
while(1)
{
write();/read();
}

close();
}

3.TCP并发服务器流程

并发服务器的设计思想是服务器接受客户端的连接请求后创建子进程来为客服端服务
使用signal() 和waitpid()函数对进程进行收尸

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>

int client(int connfd)	//connfd;
{
int ret;
char buf[256];

while(1) {
memset(buf, 0, sizeof(buf));
ret = read(connfd, buf, sizeof(buf));
if (ret == -1) {
perror("server->read");
return -1;
} else if (ret == 0) {
return -1;
}

printf("buf : %s\n", buf);

ret = write(connfd, buf, sizeof(buf));
if (ret == -1) {
perror("server->write");
return -1;
}
}
return 0;
}

void fun(int sig)
{
printf("client quit\n");
while(waitpid(-1, NULL, WNOHANG) > 0);
}

int main(int argc, char *argv[])
{
int listenfd;
int ret;
socklen_t addrlen;
int connfd;
pid_t pid;
char buf[256];
struct sockaddr_in srvaddr;
struct sockaddr_in cltaddr;
pthread_t client_pthread;

signal(SIGCHLD, fun);

/* 1. 创建服务器(创建一socket套接字);socket */
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd == -1) {
perror("server->socket");
return -1;
}
printf("create listenfd = %d success\n", listenfd);

/* 2. 设置服务器的IP地址和端口号(将socket和服务器的IP地址和端口号进行绑定);bind */
memset(&srvaddr, 0, sizeof(struct sockaddr_in));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9999);
srvaddr.sin_addr.s_addr = inet_addr("192.168.2.100");
ret = bind(listenfd, (const struct sockaddr *)&srvaddr, sizeof(srvaddr));

printf("port : %d\n", ntohs(srvaddr.sin_port));
if (ret == -1) {
perror("server->bind");
return -1;
}
printf("bind success !\n");

/* 3. 启动监听(启动服务器);  listen */
ret = listen(listenfd, 1024);
if (ret == -1) {
perror("server->listen");
return -1;
}
printf("listen success !\n");

while(1) {
/* 4. 等待服务器的连接请求,如果没有连接请求则等待,如果有连接请求,则建立连接; accept */
memset(&cltaddr, 0, sizeof(cltaddr));
addrlen = sizeof(cltaddr);
connfd = accept(listenfd, (struct sockaddr *)&cltaddr, &addrlen);
if (connfd == -1) {
perror("server->accept");
return -1;
}
printf("connect connfd = %d\n", connfd);
printf("IP : %s\n", inet_ntoa(cltaddr.sin_addr));
printf("PORT: %d\n", ntohs(cltaddr.sin_port));

pid_t pid;
pid = fork();
if (pid == -1) {
perror("fork");
return -1;
} else if (pid == 0) {
close(listenfd);
client(connfd);
exit(0);
}
close(connfd);

}
close(listenfd);

return 0;
}


4.采用线程方式实现TCP服务器

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <pthread.h>

void * client(void *arg)	//connfd;
{
int connfd;
int ret;
char buf[256];

connfd = *(int *)arg;
while(1) {
memset(buf, 0, sizeof(buf));
ret = read(connfd, buf, sizeof(buf));
if (ret == -1) {
perror("server->read");
pthread_exit(NULL);
} else if (ret == 0) {
close(connfd);
pthread_exit(NULL);
}

printf("buf : %s\n", buf);

ret = write(connfd, buf, sizeof(buf));
if (ret == -1) {
perror("server->write");
pthread_exit(NULL);
}
}
pthread_exit(NULL);
}

void fun(int sig)
{
printf("client quit\n");
while(waitpid(-1, NULL, WNOHANG) > 0);
}

int main(int argc, char *argv[])
{
int listenfd;
int ret;
socklen_t addrlen;
int connfd;
pid_t pid;
char buf[256];
struct sockaddr_in srvaddr;
struct sockaddr_in cltaddr;
pthread_t client_pthread;

/* 1. 创建服务器(创建一socket套接字);socket */
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd == -1) {
perror("server->socket");
return -1;
}
printf("create listenfd = %d success\n", listenfd);

/* 2. 设置服务器的IP地址和端口号(将socket和服务器的IP地址和端口号进行绑定);bind */
memset(&srvaddr, 0, sizeof(struct sockaddr_in));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9999);
srvaddr.sin_addr.s_addr = inet_addr("192.168.2.100");
ret = bind(listenfd, (const struct sockaddr *)&srvaddr, sizeof(srvaddr));

printf("port : %d\n", ntohs(srvaddr.sin_port));
if (ret == -1) {
perror("server->bind");
return -1;
}
printf("bind success !\n");

/* 3. 启动监听(启动服务器);  listen */
ret = listen(listenfd, 1024);
if (ret == -1) {
perror("server->listen");
return -1;
}
printf("listen success !\n");

while(1) {
/* 4. 等待服务器的连接请求,如果没有连接请求则等待,如果有连接请求,则建立连接; accept */
memset(&cltaddr, 0, sizeof(cltaddr));
addrlen = sizeof(cltaddr);
connfd = accept(listenfd, (struct sockaddr *)&cltaddr, &addrlen);
if (connfd == -1) {
perror("server->accept");
return -1;
}
printf("connect connfd = %d\n", connfd);
printf("IP : %s\n", inet_ntoa(cltaddr.sin_addr));
printf("PORT: %d\n", ntohs(cltaddr.sin_port));

/* 创建一个子线程,处理客户端的请求*/
ret = pthread_create(&client_pthread, NULL, client, (void *)&connfd);
if (ret != 0) {
printf("create client pthread fail\n");
return -1;
}
printf("create client_pthread = %d success\n", client_pthread);
ret = pthread_detach(client_pthread);
if (ret != 0) {
printf("detach client pthread fail\n");
return -1;
}
printf("detach cltaddr pthread success\n");

}
close(listenfd);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: