天天看点

Linux下TCP通信简单实例

 基于tcp(面向连接)的socket编程,分为服务器端和客户端

  服务器端的流程如下:

  (1)创建套接字(socket)

  (2)将套接字绑定到一个本地地址和端口上(bind)

  (3)将套接字设为监听模式,准备接收客户端请求(listen)

  (4)等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)

  (5)用返回的套接字和客户端进行通信(send/recv)

  (6)返回,等待另一个客户请求。

  (7)关闭套接字。

  客户端的流程如下:

  (2)向服务器发出连接请求(connect)

  (3)和服务器端进行通信(send/recv)

  (4)关闭套接字

  下面通过一个具体例子讲解一下具体的过程和相关的函数,环境是suse linux。

#include <stdio.h>

#include <stdlib.h>

#include <strings.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <memory.h>

#include <unistd.h>

//#include <linux/in.h>

#include <netinet/in.h>

//#include <linux/inet_diag.h>

#include <arpa/inet.h>

#include <signal.h>

/**

关于 sockaddr  sockaddr_in  socketaddr_un说明

http://maomaozaoyue.blog.sohu.com/197538359.html

*/

#define port    11910   //定义通信端口

#define backlog 5       //定义侦听队列长度

#define buflen  1024

void process_conn_server(int s);

void sig_pipe(int signo);

int ss,sc;  //ss为服务器socket描述符,sc为某一客户端通信socket描述符

int main(int argc,char *argv[])

{

struct sockaddr_in server_addr; //存储服务器端socket地址结构

struct sockaddr_in client_addr; //存储客户端 socket地址结构

int err;    //返回值

pid_t pid;  //分叉进行的id

/*****************socket()***************/

ss = socket(af_inet,sock_stream,0); //建立一个序列化的,可靠的,双向连接的的字节流

if(ss<0)

return -1;

}

//注册信号

sighandler_t ret;

ret = signal(sigtstp,sig_pipe);

if(sig_err == ret)

printf("信号挂接失败\n");

else

printf("信号挂接成功\n");

/******************bind()****************/

//初始化地址结构

memset(&server_addr,0,sizeof(server_addr));

server_addr.sin_family = af_inet;           //协议族

server_addr.sin_addr.s_addr = htonl(inaddr_any);   //本地地址

server_addr.sin_port = htons(port);

err = bind(ss,(struct sockaddr *)&server_addr,sizeof(sockaddr));

if(err<0)

printf("server : bind error\n");

/*****************listen()***************/

err = listen(ss,backlog);   //设置监听的队列大小

if(err < 0)

printf("server : listen error\n");

/****************accept()***************/

  为类方便处理,我们使用两个进程分别管理两个处理:

  1,服务器监听新的连接请求;2,以建立连接的c/s实现通信

  这两个任务分别放在两个进程中处理,为了防止失误操作

  在一个进程中关闭 侦听套接字描述符 另一进程中关闭

  客户端连接套接字描述符。注只有当所有套接字全都关闭时

  当前连接才能关闭,fork调用的时候父进程与子进程有相同的

  套接字,总共两套,两套都关闭掉才能关闭这个套接字

for(;;)

socklen_t addrlen = sizeof(client_addr);

//accept返回客户端套接字描述符

sc = accept(ss,(struct sockaddr *)&client_addr,&addrlen);  //注,此处为了获取返回值使用 指针做参数

if(sc < 0)  //出错

continue;   //结束此次循环

printf("server : connected\n");

//创建一个子线程,用于与客户端通信

pid = fork();

//fork 调用说明:子进程返回 0 ;父进程返回子进程 id

if(pid == 0)        //子进程,与客户端通信

close(ss);

process_conn_server(sc);

close(sc);

服务器对客户端连接处理过程;先读取从客户端发送来的数据,

然后将接收到的数据的字节的个数发送到客户端

//通过套接字 s 与客户端进行通信

void process_conn_server(int s)

ssize_t size = 0;

char buffer[buflen];  //定义数据缓冲区

//等待读

for(size = 0;size == 0 ;size = read(s,buffer,buflen));

//输出从客户端接收到的数据

printf("%s",buffer);

//结束处理

if(strcmp(buffer,"quit") == 0)

close(s);   //成功返回0,失败返回-1

return ;

sprintf(buffer,"%d bytes altogether\n",size);

write(s,buffer,strlen(buffer)+1);

void sig_pipe(int signo)

printf("catch a signal\n");

if(signo == sigtstp)

printf("接收到 sigtstp 信号\n");

int ret1 = close(ss);

int ret2 = close(sc);

int ret = ret1>ret2?ret1:ret2;

if(ret == 0)

printf("成功 : 关闭套接字\n");

else if(ret ==-1 )

printf("失败 : 未关闭套接字\n");

exit(1);

  客户端代码:

#include <signal.h> //添加信号处理  防止向已断开的连接通信

信号处理顺序说明:在linux操作系统中某些状况发生时,系统会向相关进程发送信号,

信号处理方式是:1,系统首先调用用户在进程中注册的函数,2,然后调用系统的默认

响应方式,此处我们可以注册自己的信号处理函数,在连接断开时执行

#define port    11910

void process_conn_client(int s);

void sig_pipe(int signo);    //用户注册的信号函数,接收的是信号值

int s;  //全局变量 , 存储套接字描述符

sockaddr_in server_addr;

int err;

char server_ip[50] = "";

int port = 0;

strcpy(server_ip, argv[1]);

port = atoi(argv[2]);

/********************socket()*********************/

s= socket(af_inet,sock_stream,0);

if(s<0)

printf("client : create socket error\n");

return 1;

printf("client : socket fd = %d\n", s);

//信号处理函数  sigint 是当用户按一个 ctrl-c 建时发送的信号

printf("信号挂接成功\n") ;

/*******************connect()*********************/

//设置服务器地址结构,准备连接到服务器

server_addr.sin_family = af_inet;

server_addr.sin_addr.s_addr = htonl(inaddr_any);

/*将用户数入对额字符串类型的ip格式转化为整型数据*/

//inet_pton(af_inet,argv[1],&server_addr.sin_addr.s_addr);

printf("please input server ip address : \n");

read(0,server_ip,50);

//err = inet_pton(af_inet,server_ip,&server_addr.sin_addr.s_addr);

server_addr.sin_addr.s_addr = inet_addr(server_ip);

err = connect(s,(struct sockaddr *)&server_addr,sizeof(sockaddr));

if(err == 0)

printf("client : connect to server\n");

printf("client : connect error\n");

//与服务器端进行通信

process_conn_client(s);

close(s);

void process_conn_client(int s)

char buffer[buflen];

memset(buffer,'\0',buflen);

/*从标准输入中读取数据放到缓冲区buffer中*/

size = read(0,buffer,buflen);   // 0,被默认的分配到标准输入  1,标准输出  2,error

if(size >  0)

//当向服务器发送 “quit” 命令时,服务器首先断开连接

write(s,buffer,strlen(buffer)+1);   //向服务器端写

//等待读取到数据

for(size = 0 ; size == 0 ; size = read(s,buffer,buflen) );

write(1,buffer,strlen(buffer)+1);   //向标准输出写

void sig_pipe(int signo)    //传入套接字描述符

int ret = close(s);

最新内容请见作者的github页:http://qaseven.github.io/

继续阅读