linux socket talkclient talkserver示例

cleint:

#define _GNU_SOURCE 1
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <poll.h>
#include <fcntl.h>

#define BUFFER_SIZE 64

int main( int argc, char* argv[] )
{
    if( argc <= 2 )
    {
        printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi( argv[2] );

    struct sockaddr_in server_address;
    bzero( &server_address, sizeof( server_address ) );
    server_address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &server_address.sin_addr );
    server_address.sin_port = htons( port );

    int sockfd = socket( PF_INET, SOCK_STREAM, 0 );
    assert( sockfd >= 0 );
    //here use block mode connect socket, there will be 21 minutes timeout if failed.
    if ( connect( sockfd, ( struct sockaddr* )&server_address, sizeof( server_address ) ) < 0 )
    {
        printf( "connection failed\n" );
        close( sockfd );
        return 1;
    }

    pollfd fds[2];
    fds[0].fd = 0; //stdin
    fds[0].events = POLLIN;
    fds[0].revents = 0;
    fds[1].fd = sockfd;
    //why don‘t concern about POLLOUT event?
    //fds[1].events = POLLIN | POLLRDHUP; //concern read & hangup event
    fds[1].events = POLLIN | POLLRDHUP | POLLOUT;
    fds[1].revents = 0;
    char read_buf[BUFFER_SIZE];
    int pipefd[2];
    int ret = pipe( pipefd );
    assert( ret != -1 );

    while( 1 )
    {
        ret = poll( fds, 2, -1 );
        if( ret < 0 )
        {
            printf( "poll failure\n" );
            break;
        }

        //connection was hang up by peer
        if( fds[1].revents & POLLRDHUP )
        {
            printf( "server close the connection\n" );
            break;
        }
        //have data from server to read
        else if( fds[1].revents & POLLIN )
        {
            memset( read_buf, ‘\0‘, BUFFER_SIZE );
            recv( fds[1].fd, read_buf, BUFFER_SIZE-1, 0 );
            printf( "recv data from server: %s\n", read_buf );
        }
        else if (fds[1].revents & POLLOUT)
        {
            //always writable, will print so much message, so comment it
            //printf("connection got POLLOUT event, it is writable now.\n");
        }

        if( fds[0].revents & POLLIN ) //user input --> stdin have data to read
        {
            //stdin --> pipefd[1]
            ret = splice( 0, NULL, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );
            //pipefd[1] --> pipefd[0] --> sockfd
            ret = splice( pipefd[0], NULL, sockfd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );
        }
    }

    close( sockfd );
    return 0;
}

  

server:

#define _GNU_SOURCE 1
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <poll.h>

#define USER_LIMIT 5
#define BUFFER_SIZE 64
#define FD_LIMIT 65535

struct client_data
{
    sockaddr_in address;
    char* write_buf;
    char buf[ BUFFER_SIZE ];

    client_data(): write_buf(0) {}
};

int setnonblocking( int fd )
{
    int old_option = fcntl( fd, F_GETFL );
    int new_option = old_option | O_NONBLOCK;
    fcntl( fd, F_SETFL, new_option );
    return old_option;
}

int main( int argc, char* argv[] )
{
    if( argc <= 2 )
    {
        printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi( argv[2] );

    int ret = 0;
    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &address.sin_addr );
    address.sin_port = htons( port );

    int listenfd = socket( PF_INET, SOCK_STREAM, 0 );
    assert( listenfd >= 0 );

    ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret != -1 );

    ret = listen( listenfd, 5 ); //backlog=5
    assert( ret != -1 );

    client_data* users = new client_data[FD_LIMIT];
    pollfd fds[USER_LIMIT+1]; //1 listen sock fd + 5 user sock fd
    int user_counter = 0;
    for( int i = 1; i <= USER_LIMIT; ++i )
    {
        fds[i].fd = -1;
        fds[i].events = 0;
    }
    fds[0].fd = listenfd;
    fds[0].events = POLLIN | POLLERR; //read & error event
    fds[0].revents = 0;

    while( 1 )
    {
        ret = poll( fds, user_counter+1, -1 );
        if ( ret < 0 )
        {
            printf( "poll failure\n" );
            break;
        }

        for( int i = 0; i < user_counter+1; ++i )
        {
            //client call connect to this server
            if( ( fds[i].fd == listenfd ) && ( fds[i].revents & POLLIN ) )
            {
                struct sockaddr_in client_address;
                socklen_t client_addrlength = sizeof( client_address );
                //listenfd is block mode by default, but use poll we know the event is ready, accept will return right now
                int connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength );
                if ( connfd < 0 )
                {
                    printf( "accept errno is: %d\n", errno );
                    continue;
                }

                if( user_counter >= USER_LIMIT )
                {
                    const char* info = "too many users, so close this connfd returned by accept.\n";
                    printf( "%s", info );
                    send( connfd, info, strlen( info ), 0 );
                    close( connfd );
                    continue;
                }
                user_counter++;
                users[connfd].address = client_address;
                setnonblocking( connfd );  //connfd is nonblock mode
                fds[user_counter].fd = connfd;
                fds[user_counter].events = POLLIN | POLLRDHUP | POLLERR;// concern about read & hanguo & error event at start
                fds[user_counter].revents = 0;
                printf( "comes a new user, now have %d users\n", user_counter );
            }
            else if( fds[i].revents & POLLERR ) //fds[i] has error!
            {
                printf( "get an error from %d\n", fds[i].fd );
                char errors[ 100 ];
                memset( errors, ‘\0‘, 100 );
                socklen_t length = sizeof( errors );
                if( getsockopt( fds[i].fd, SOL_SOCKET, SO_ERROR, &errors, &length ) < 0 )
                {
                    printf( "get socket option failed\n" );
                }
                else
                    printf("%d got an error %s\n", fds[i].fd, errors);
                continue;
            }
            else if( fds[i].revents & POLLRDHUP ) //fds[i] was hang up by peer
            {
                users[fds[i].fd] = users[fds[user_counter].fd];
                close( fds[i].fd );
                fds[i] = fds[user_counter];
                i--;
                user_counter--;
                printf( "a client left\n" );
            }
            //got readable event
            else if( fds[i].revents & POLLIN )
            {
                int connfd = fds[i].fd;
                memset( users[connfd].buf, ‘\0‘, BUFFER_SIZE );
                ret = recv( connfd, users[connfd].buf, BUFFER_SIZE-1, 0 );
                printf( "get %d bytes of client data: %s   from connection: %d\n", ret, users[connfd].buf, connfd );
                if( ret < 0 ) //has error
                {
                    //NONBLOCK socket, read函数会返回一个错误EAGAIN,提示你的应用程序现在没有数据可读请稍后再试。
                    //so if it is a EAGAIN error we just ignore this error and wait for next POLLIN event
                    if( errno != EAGAIN )
                    {
                        close( connfd );
                        users[fds[i].fd] = users[fds[user_counter].fd];
                        fds[i] = fds[user_counter];
                        i--;
                        user_counter--;
                        printf( "recv data from a client ran into error, a client maybe left\n" );
                    }
                }
                //If the connection has been gracefully closed, the return value is zero.
                else if( ret == 0 )
                {
                    //printf( "code should not come to here\n" );
                    printf("recv function return 0 indicates the client has gracefully closed and exit.\n");

                    //need to remove it from fds??

                }
                else //server send the data recv from a client to all other clients
                {
                    for( int j = 1; j <= user_counter; ++j )
                    {
                        if( fds[j].fd == connfd )
                        {
                            continue; //don‘t send to self
                        }

                        //其他的client的socket此刻不能再读了(当前client继续发多个消息的话,这些消息会阻塞在socket的sendbuffer),
                        //要设置为只可以写!因为如果可以再读的话,假如这个client连续发多个消息,其他client就会漏收后面的消息!
                        fds[j].events |= ~POLLIN;
                        fds[j].events |= POLLOUT;
                        users[fds[j].fd].write_buf = users[connfd].buf;
                    }
                }
            }
            else if( fds[i].revents & POLLOUT ) //got writable event
            {
                int connfd = fds[i].fd;
                if( ! users[connfd].write_buf )
                {
                    continue; //no data send to this client
                }
                ret = send( connfd, users[connfd].write_buf, strlen( users[connfd].write_buf ), 0 );
                users[connfd].write_buf = NULL;
                //reset back
                fds[i].events |= ~POLLOUT;
                fds[i].events |= POLLIN;
            }
        }
    }

    delete [] users;
    close( listenfd );
    return 0;
}

  

时间: 2024-10-19 13:01:28

linux socket talkclient talkserver示例的相关文章

Linux - Socket网络套接字

OSI七层协议功能 物理层 面向物理传输媒体,屏蔽媒体的不同 主要定义物理设备标准,如网线的接口类型.光纤的接口类型.各种传输介质的传输速率等.它的主要作用是传输比特流(就是由1.0转化为电流强弱来进行传输,到达目的地后在转化为1.0,也就是我们常说的模数转换与数模转换).这一层的数据叫做比特. 链路层 面向一条链路,成帧和无差错传输 主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装.常把这一层的数据叫做帧.在这一层工作的设备是交换机,数据通过交换机来传输. 网络层 分配地址.

Android Jni层 创建 linux socket 出错问题解决

问题: 想在Jni层创建 udp socket 与服务端通信,但是没有成功,最后发现竟然是创建socket失败(代码如下) // create socket g_sd = socket(AF_INET, SOCK_DGRAM, 0); if (-1 == g_sd) { perror("socket()"); goto err_socket; } 解决办法: 在 AndroidManifest.xml 文件中,添加访问网络的权限: <uses-permission android

C# 如何实现简单的Socket通信(附示例)

上周由于有个项目需要用到网络通信这块,然后就花了点时间研究了一下,本来想上周就写出来的,但是突然要忙,所以等到现在. 话说对于网络通信,以前写C++的时候,天天面对着线程和Socket,所以换成C#也就没那么怕了,虽然C++下也没有掌握的多好,但毕竟只是一个小Demo,只作为了解一下过程. 自己写了一个服务端和一个客户端,刚开始比较简单,只是能达到连通,收发信息的目的,但是很粗糙.而后稍加改进呢~加上了多线程,所以能感觉更科学一些,不过自己真的很菜,代码写的不是很好看,下面分两个版本给大家表出来

Linux socket编程的心跳机制总结

Linux socket编程的心跳机制总结 我写这篇文章的目的是想总结一下心跳机制的使用,因为最近两个项目的TCP通信中都使用了这个方法,感觉用法好诗比较经典的,所以拿出来与大家共享. 什么是心跳机制 心跳机制就是当客户端与服务端建立连接后,每隔几分钟发送一个固定消息给服务端,服务端收到后回复一个固定消息给客户端,如果服务端几分钟内没有收到客户端消息,则视客户端断开.发送方可以是客户端和服务端,看具体需求. 为什么要使用 我们都知道在TCP这种长连接情况下下,有可能有一大段时间是没有数据往来的,

Linux socket编程 DNS查询IP地址

本来是一次计算机网络的实验,但是还没有完全写好,DNS的响应请求报文的冗余信息太多了,不只有IP地址.所以这次的实验主要就是解析DNS报文.同时也需要正确的填充请求报文.如果代码有什么bug,欢迎指正啊.代码排版有点乱... 本文有以下内容 DNS报文的填充和解析 利用socket API传输信息 一.填充DNS请求报文 随便百度一下,就可以知道DNS报文的格式.所以这里只介绍如何填充DNS报文. 首先是填充报文首部: ? 1 2 3 4 5 6 7 8 9 /* 填充首部的格式大致相同,下面的

linux socket中的SO_REUSEADDR

Welcome to the wonderful world of portability... or rather the lack of it. Before we start analyzing these two options in detail and take a deeper look how different operating systems handle them, it should be noted that the BSD socket implementation

windows 与 Linux SOCKET通讯

? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 windows client 端口 // Def_win_client_socket_test.cpp :

Linux Socket编程

Linux Socket编程 一.Socket编程具体函数解析参考网址 http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html(本文转载于此网址,转载请注明源链接) 二.Socket编程的基本知识 2.1 基本TCP客户/服务器程序套接字函数 图2.1 TCP服务器/客户端响应方式 2.2 TCP三次握手 图2.2 TCP 三次握手建立连接 2.3 TCP断开连接 图2.3 TCP 四次握手断开连接 三.Socket编程细节知识

linux socket网络编程 常用函数及头文件

转自:http://blog.chinaunix.net/u3/102500/showart_2065640.html 一 三种类型的套接字: 1.流式套接字(SOCKET_STREAM) 提供面向连接的可靠的数据传输服务.数据被看作是字节流,无长度限制.例如FTP协议就采用这种. 2.数据报式套接字(SOCKET_DGRAM) 提供无连接的数据传输服务,不保证可靠性. 3.原始式套接字(SOCKET_RAW) 该接口允许对较低层次协议,如IP,ICMP直接访问. 二 基本套接字系统调有有如下一