Linux 网络编程之TCP(上)

1.TCP通信流程

TCP网络编程主要是用套接字来实现网络服务器与客户端通信,一个标准的套接字是由协议族,IP地址与端口号组成,它是TCP服务器与客户端通信的基础,

TCP的服务器端流程:

(1)建立套接字(socket)

(2)将地址结构绑定到套接字上(bind)

(3)监听本地端口,设置监听队列的长度,服务器将不能同时处理多个请求,将不能处理的请求放入等待队列中(listen)

(4)接受客户端的请求,返回客户端套接字描述符,服务器与客户端利用这个新的描述符进行传输数据(accept)

(5)数据传输(read,write)

(6)关闭套接字(close)

TCP客户端流程:

(1)建立套接字(socket)

(2)连接目标服务器(connect)

(3)数据传输(read,write)

(4)关闭客户端套接字(close)

这样,就可以实现服务器与客户端通信了.

2. 相关数据结构

(1)通用套接字地址类型

struct sockaddr{

sa_family_t sa_family;//协议族

char data[14];//协议数据

}

typedef unsigned short sa_family

以太网中常用的地址结构:

struct sockaddr_in{

u8 sin_len;//struct sockaddr_in的长度

u8 sin_family;//协议族

u16 sin_port;//端口号

struct in_addr sin_addr;//32位IP地址

char sin_zero[8];//没使用

}

struct in_addr{

u32 s_addr;//32位地址

};

(2)相关函数

#include <sys/socket.h>

#include <sys/types.h>

int socket(int domain,int type,int protocol);

参数:

domain-通信的域,AF_INET表示IPV4协议,PF_INET6表示IPV6协议

type-设置套接字通信的类型 SOCK_STREAM流式套接字,SOCK_DGRAM数据报套接字, SOCK_RAW原始套接字

protocol-表示某个通信类型的特定类型,如果type只有一个类型,那么这个参数设置为0

返回值:

成功返回套接字描述符,失败返回-1

#include <sys/types.h>

#include<sys/socket.h>

int bind(int sockfd,const struct sockaddr*my_addr,socket_t addrlen);

参数:

sockfd-套接字描述符

my_addr-sockaddr指针类型,指向一个套接字的地址结构

addrlen-地址结构的长度

返回值:

成功绑定返回0,失败绑定返回-1

#include <sys/socket.h>

int listen(int sockfd,int backlog);

参数:

sockfd-套接字描述符

backlog-等待队列的长度

返回值:

成功返回0,失败返回-1

#include <sys/types.h>

#include <sys/socket.h>

int accept(int sockfd,struct sockaddr*addr,socklen_t *addrlen);

参数:

sockfd-套接字描述符

addr-指向地址结构的指针

addrlen-地址结构的长度指针

其中addr包含的是客户端的地址结构,包括客户端的IP,端口号,协议族等等.

返回值:

成功返回的是用于服务器与客户端通信的套接字描述符,失败返回-1

#include <sys/types.h>

#include <sys/socket.h>

int connect(int sockfd,struct sockaddr*addr,int addrlen);

参数:

sockfd-客户端套接字描述符

addr-需要连接的服务器的地址结构

addrlen-服务器的地址结构的长度

返回值:

连接成功返回0,失败返回-1

#include <sys/socket.h>

close(int s);

参数:

s-套接字描述符

返回值:

成功关闭套接字返回0,失败返回-1

3. TCP通信的实例

服务器端:

#include <stdio.h>#include <stdlib.h>#include <strings.h>#include <sys/types.h>#include <sys/socket.h>#include <unistd.h>#include <linux/in.h>#define PORT 8888 #define BACKLOG 2//服务器端程序/**(1)建立一个套接字描述符,定义网络类型,协议类型和具体的协议标号 socket()(2)将套接字与具体的地址结构绑定,这个地址结构包括端口号,IP地址,网络类型 bind()(3)侦听客户端发出的请求,设置队列的长度 listen()(4)在客户端发出请求后,处理客户端的连接,返回一个新的套接字描述符用于服务器端与客户端的通信 accept()(5)读写数据 read() vs. write()(6)当服务器处理完数据要结构通信过程,关闭连接 close()**/void process(int sc);int main(int argc,char* argv[]){int ss,sc;//ss为服务器端的套接字描述符,sc为客户端的套接字描述符,即返回的新的套接字描述符struct sockaddr_in server_addr; //服务器端的地址结构struct sockaddr_in client_addr;//客户端的地址结构int err;//返回值pid_t pid;//进程的pid//建立一个流式套接字ss=socket(AF_INET,SOCK_STREAM,0);//第一个参数用来设置通信的域,AF_INET表示使用IPv4 Internet协议,第二个参数是套接字通信的类型,表示流式套接字,第三个参数表示某种协议的特定类型,如果只有一种类型,设置为0if(ss<0) { printf("socket error\n"); return -1;}//设置服务器的地址结构bzero(&server_addr,sizeof(server_addr));//清0server_addr.sin_family=AF_INET;//通信的域,通常与socket函数的domain一致server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//本地地址server_addr.sin_port=htons(PORT);//服务器端口//绑定地址结构到套接字描述符err=bind(ss,(struct sockaddr*)&server_addr,sizeof(server_addr));//第一个参数是套接字描述符,第二个参数是sockaddr指针,第三个参数是server_addr的长度if(err<0){perror("bind error");return -1;}//设置侦听err=listen(ss,BACKLOG);//第一个参数是服务器端的套接字描述符,第二个参数是侦听队列的长度if(err<0){perror("listen error"); return -1;}for(;;){int addrlen=sizeof(struct sockaddr);//接收客户端的连接sc=accept(ss,(struct sockaddr*)&client_addr,&addrlen);//第一个参数是服务器端的套接字描述符,第二个参数是sockaddr结构体指针,第三个参数是sockaddr长度的指针if(sc<0){ continue;}//客户端的信息存储在sockaddr结构体int sin_port=client_addr.sin_port;//返回连接的客户端端口号printf("port is %d\n",sin_port);//建立一个子进程来处理连接pid=fork();if(pid==0){//子进程 close(ss);//关闭服务器的侦听 process(sc);//处理连接,用返回的新的客户端描述符来处理连接}else{close(sc);//在父进程关闭客户端的连接}}}void process(int sc){ ssize_t size=0; char buffer[1024]; for(;;){ size=read(sc,buffer,1024);//返回读取的字节数 if(size==0) return; write(1,buffer,size);//终端输出 sprintf(buffer,"%d bytes\n",size); write(sc,buffer,strlen(buffer)+1);//发送给客户端 }}客户端:

#include <stdio.h>#include <stdlib.h>#include <strings.h>#include <sys/types.h>#include <sys/socket.h>#include <unistd.h>#include <linux/in.h>#define PORT 8888/**(1)建立套接字描述符(2)connect与服务器端三次握手,连接服务器端(3)read vs write(4)关闭连接**/void process(int s);int main(int argc,char* argv[]){int s;struct sockaddr_in server_addr;//服务器端的地址结构int err;s=socket(AF_INET,SOCK_STREAM,0);//建立一个套接字描述符,参数1表示通信的域IPv4协议,参数2表示通信类型流式套接字,参数3表示某种协议的特定类型如果只有一种类型为0if(s<0){ perror("socket error\n"); return -1;}//设置服务器地址bzero(&server_addr,sizeof(server_addr));//清0server_addr.sin_family=AF_INET;server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//本地地址,可接受任意IPserver_addr.sin_port=htons(PORT);//服务器端口inet_pton(AF_INET,argv[1],&server_addr.sin_addr);//用户输入IP地址connect(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));//连接服务器端第一个参数是客户端套接字描述符,第二个参数是sockaddr指针,第三个参数是sockaddr的长度process(s);//客户端接收到数据处理close(s);//客户端关闭连接}void process(int s){ssize_t size=0;char buffer[1024];for(;;){size=read(0,buffer,1024);//从标准输入流读取数据if(size>0){write(s,buffer,size);//发送给客户端size=read(s,buffer,1024);//从客户端读取数据write(1,buffer,size);//将数据写到标准输出}}}

测试:

[root@localhost ~]# ./Serverport is 62361hello[root@localhost ~]# ./Client 127.0.0.1hello6 bytes总结:

本文主要介绍了TCP网络数据发送与接收的流程,相关函数的使用,最后给出了一个具体的服务器与客户端通信的例子.

未经一番寒彻骨,焉得梅花扑鼻香

Linux 网络编程之TCP(上)

相关文章:

你感兴趣的文章:

标签云: