socket编程——TCP

上篇文章中对一些函数有了详细的介绍,本篇使用这些函数来实现基于TCP的socket编程

服务器程序端:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>                                                             
#include <errno.h>
#include <string>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <netinet/in.h>
 
#define BLOCK 6//等待连接队列的允许最大长度,在listen()中用到
 
using namespace std;
 
typedef struct 
{
    int port;
    int fd; 
}C;

void usage(string proc)//输入参数错误会调用该函数
{   
    cout<<proc<<" [ip] [port]"<<endl;
}   
    
void *thread_run(void *arg)
{   
    char buf[1024];
    while(1){
        memset(buf,‘\0‘,sizeof(buf));
        int size=recv(((C*)arg)->fd,buf,sizeof(buf)-1,0);
        if(size>0){
            cout<<"client# "<<buf<<endl;
        }else if (size==0){
            continue;                                                           
            break;
        }else{
            cerr<<strerror(errno)<<endl;
        }
    }
    close(((C*)arg)->fd);
}   
    
int create_sock(char *port,const char * inaddr)
{   
    //1.创建一个监听套接字
    int listenfd=socket(AF_INET,SOCK_STREAM,0);
    if(listenfd<-1){
        cerr<<strerror(errno)<<endl;
        exit(1);
    }
    //2.创建用来保存本地信息的结构体,用于bind
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    int _port=atoi(port);
    local.sin_port=htons(_port); 
    //下面三条语句实现的功能是一样的                                         
    local.sin_addr.s_addr=inet_addr(inaddr);
  //  local.sin_addr.s_addr=inet_network(inaddr);
  //  inet_aton(inaddr,&local.sin_addr);
    //3.用来绑定套接字listenfd和本地ip和端口号等
     if(bind(listenfd,(struct sockaddr*)&local,sizeof(local))<0){
        cerr<<strerror(errno)<<endl;
        exit(2);
    }
    //4.设置监听状态
    if(listen(listenfd,BLOCK)<0){
        cerr<<strerror(errno)<<endl;
        exit(3);
    }
    
    return listenfd;
}
 
int main(int argc,char* argv[])
{                                                                               
    if(argc!=3){
        usage(argv[0]);
        exit(1);
    }
    
    int listen_fd=create_sock(argv[2],argv[1]);
       
    struct sockaddr_in client;//保存客户端的信息
    socklen_t len=sizeof(client);
    while(1){
            //从listen_fd的请求连接队列中取出一个请求并创建一个新的套接字,这个新的套接字用来和客户端进行通信
        int connfd=accept(listen_fd,(struct sockaddr*)&client,&len);
        if(connfd<0){
            continue;
        }
        cout<<"get a connect..."<<" sock : "<<connfd            <<" ip: "<<inet_ntoa(client.sin_addr)<<" port: "            <<ntohs(client.sin_port)<<endl;
//以下用到了3个版本的
//1.单进程,这个版本只能连接一个客户端,当有第二条连接请求到来时,会被阻塞在accept处   
#ifdef _V1_ 
        char buf[1024];                                                         
        while(1){
            memset(buf,‘\0‘,sizeof(buf));
            int size=recv(connfd,&buf,sizeof(buf)-1,0);
            if(size>0){
                cout<<"client# "<<buf<<endl;
            }else if (size==0){//client close
                close(connfd);
            }else{
                cerr<<strerror(errno)<<endl;
            }
        } 
//2.多进程,每有一条连接请求到来的时候,都会创建出一个子进程,由子进程来完成通信,父进程则一直处于监听状态,这样就可以在多条连接上通信
#elif _V2_
        pid_t id=fork();
        if(id==0){
            close(listen_fd);
        }else if(id>0){
            close(connfd);
            break;
        }else{
            cerr<<strerror(errno)<<endl;
            exit(4);
        }                                                                       
        char buf[1024];
        while(1){
            memset(buf,‘\0‘,sizeof(buf));
            int size=recv(connfd,&buf,sizeof(buf)-1,0);
            if(size>0){
                cout<<"client# "<<buf<<endl;
            }else if (size==0){
                break;                                                          
            }else{
            }
        }
//3.多线程,由于创建进程的开销比较大,所以用多线程来实现 
#elif _V3_
        pthread_t tid;
        C info;
        info.port=client.sin_port;
        info.fd=connfd;
        
        int err=pthread_create(&tid,NULL,thread_run,(void*)&info);
        if(err!=0){
            cerr<<strerror(errno)<<endl;
            exit(5);
        }
        //线程被设置成可分离状态,当线程函数通信完成之后,由系统自动回收资源
        if(pthread_detach(tid)<0){
            cerr<<errno<<endl;
        }
#else  
        cout<<"default"<<endl;
    
#endif 
        
    }                                                 
    return 0;
}

客户端程序:

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>                                                         
#include <string>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
 
using namespace std;
 
void usage(string proc)//输入参数错误会调用该函数,和server端一样
{
    cout<<proc<<" [ip] [port]"<<endl;
}
 
int creat_socket()
{
        //创建套接字,该套接字可直接用于和服务器端进行通信
    int fd=socket(AF_INET,SOCK_STREAM,0);
    if(fd<0){
        cerr<<strerror(errno)<<endl;
        exit(1);
    }
    
    return fd;
}   
    
int main(int argc,char* argv[])
{   
    if(argc!=3){
        usage(argv[0]);
        exit(1);
    }
    
    int fd=creat_socket();                                                      
    
    int _port=atoi(argv[2]);
    //创建结构体用于保存服务器端信息
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(_port);
    inet_aton(argv[1],&addr.sin_addr);
    socklen_t addrlen=sizeof(addr);
    //主动连接服务器端
    if(connect(fd,(struct sockaddr*)&addr,addrlen)<0){
        cerr<<strerror(errno)<<endl;
        exit(2);
    }
    //给服务器端发送数据
    char buf[1024];
    while(1){
        memset(buf,‘\0‘,sizeof(buf));
        cin>>buf;
        if(send(fd,buf,sizeof(buf)-1,0)<0){
            cerr<<strerror(errno)<<endl;
            continue;
        }
        cout<<"I say#"<<buf<<endl;                                              
    }
    
    return 0;
}

Makefile

bin_server=tcp_server
bin_client=tcp_client                                                           
src_server=tcp_server.cpp
src_client=tcp_client.cpp
cc=g++
 
.PHONY:all
all:$(bin_server) $(bin_client)
 
$(bin_server):$(src_server)
    cc -o [email protected] $^ -lstdc++ -D_V3_ -lpthread -g//此处使用的是服务器端的_V3_版本
$(bin_client):$(src_client)
    cc -o [email protected] $^ -lstdc++ -g 
 
.PHONY:clean
clean:
    rm -f $(bin_server) $(bin_client)

以上程序实现了客户端和服务器端的通信,客户端可以向服务器端直接发送消息

如果此时Ctrl+C服务器端程序,再次运行服务器端程序,会出现以下一句话:

Address already in use

这是因为主动关闭连接的一方会进入TIME_WAIT状态,linux下一般为30秒,这样我们必须等上一段时间才可以重启服务器端程序,但我们有一种方法可以使主动关闭的一方不用进入TIME_WAIT状态

int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

sockfd:标识一个套接口的描述字

level:选项定义的层次;支持SOL_SOCKET(基本套接口)、IPPROTO_TCP(TCP套接口)、                   IPPROTO_IP( IPv4套接口)和IPPROTO_IPV6(IPv6套接口)

optname:需设置的选项

optval:指针,指向存放选项待设置的新值的缓冲区

optlen:optval缓冲区长度

SO_REUSEADDR      // 允许套接口和一个已在使用中的地址捆绑

在socket()和bind()之间插入下面代码,设置listenfd描述符

    struct linger lig;
    int iLen;
    lig.l_onoff=1;
    lig.l_linger=0;
    iLen=sizeof(struct linger);
    setsockopt(listenfd,SOL_SOCKET,SO_LINGER,(char *)&lig,iLen);

setsockopt()详解链接:http://blog.sina.com.cn/s/blog_6ede0d160100q9li.html

《完》

时间: 2024-10-07 13:48:57

socket编程——TCP的相关文章

python socket编程 TCP

python socket编程 TCP by wuxy server.py import socket import commands BUF_SIZE = 1024 server_addr = ('127.0.0.1',8888) server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(se

Socket编程(TCP/UDP) - 初学(C语言)

Socket socket函数 TCP代码实现 TCP Server TCP Client UDP代码实现 UDP Server UDP Client Socket 先介绍一下socket的启动过程: 根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认. (1)服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态. (2)客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器

Socket编程--TCP粘包问题

TCP是个流协议,它存在粘包问题 产生粘包的原因是: TCP所传输的报文段有MSS的限制,如果套接字缓冲区的大小大于MSS,也会导致消息的分割发送. 由于链路层最大发送单元MTU,在IP层会进行数据的分片. 应用层调用write方法,将应用层的缓冲区中的数据拷贝到套接字的发送缓冲区.而发送缓冲区有一个SO_SNDBUF的限制,如果应用层的缓冲区数据大小大于套接字发送缓冲区的大小,则数据需要进行多次的发送. 粘包问题的解决 ①:发送定长包 这里需要封装两个函数: ssize_t readn(int

python基础之socket编程(TCP三次握手和四次挥手)

TCP协议中中的三次握手和四次挥手 建立TCP需要三次握手才能建立,而断开连接则需要四次握手.整个过程如下图所示: 先来看看如何建立连接的. 首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源.Client端接收到ACK报文后也向Server段发生ACK报文,并分配资源,这样TCP连接就建立了. 那如何断开连接呢?简单的过程如下: [注意]中断连接端可以是Client端,也可以是Server端. 假设Client端发起中断连接请求,也就是发送FIN报

socket编程-tcp

server.py import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 第一个参数为套接字的地址家族AF_INET代表网络套接字,第二个参数SOCK_STREAM代表tcp协议 phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 当服务端关闭时,再重启服务端,有时地址还没有释放,会出现Address already in use,

网络层、传输层、应用层、端口通信协议编程接口 - http,socket,tcp/ip 网络传输与通讯知识总结

引: http://coach.iteye.com/blog/2024511 什么是TCP和UDP,以及二者区别是什么? TCP的全称为传输控制协议.这种协议可以提供面向连接的.可靠的.点到点的通信. UDP全称为用户数据报协议,它可以提供非连接的不可靠的点到多点的通信. 使用TCP还是UDP,那要看你的程序注重哪一个方面,可靠(tcp)还是快速(udp). TCP/IP 建立连接的过程 手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过无线网络建立TCP连接.   

网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程

Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服务器进程B1同时为客户进程A1.A2和B2提供服务. Socket概述 ①   所谓Socket通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄.应用程序通常通过“套接字”向网络发出请求或者应答网络请求. ②   Socket是连接运行在网络上的两个程序间的双向通信的端点. ③  

基于socket的TCP和UDP编程

一.概述 TCP(传输控制协议)和UDP(用户数据报协议是网络体系结构TCP/IP模型中传输层一层中的两个不同的通信协议. TCP:传输控制协议,一种面向连接的协议,给用户进程提供可靠的全双工的字节流,TCP套接口是字节流套接口(STream socket)的一种. UDP:用户数据报协议.UDP是一种无连接协议.UDP套接口是数据报套接口(datagram Socket)的一种. 二.TCP和UDP介绍 1)基本TCP客户—服务器服务器 服务器是指在网络环境下运行相应的应用软件,为网上用户提供

1.socket编程:socket编程,网络字节序,函数介绍,IP地址转换函数,sockaddr数据结构,网络套接字函数,socket相关函数,TCP server和client

 1  Socket编程 socket这个词可以表示很多概念: 在TCP/IP协议中,"IP地址+TCP或UDP端口号"唯一标识网络通讯中的一个进程,"IP 地址+端口号"就称为socket. 在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接.socket本身有"插座"的意思,因此用来描述网络连 接的一对一关系. TCP/IP协议最早在BSD UNIX上实现,