`
836811384
  • 浏览: 547757 次
文章分类
社区版块
存档分类
最新评论

最简单的socket套接字编程

 
阅读更多

最简单的socket套接字编程

作者:gaopenghigh,转载请注明出处。(原文地址)


本文主要介绍了UNIX环境下socket网络编程的主要概念和步骤,并且附带了一个简单的 服 务器和客户端代码实例,实现了一个网络服务,该服务接受一个字符串的命令,执行该命 令,并且把结 果返回给客户端。分别使用了C语言的多进程、多线程模式,以及Python的 多线程模式实现 。

主要概念

socket

建立socket:

int socket(int domain, int type, int protocol);
/*
 * @domain : AF_INET(TCP/IP)
 * @type : SOCK_STREAM | SOCK_DGRAM
 * @protocol : Default 0

socket类型有两种:

  • 流式Socket(SOCK_STREAM),一般对应TCP
  • 数据报式Socket(SOCK_DGRAM),对于UDP

bind

bind函数将socket与本机上的一个端口相关联,随后你就可以在该端口监听服务请求:

int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
/* 成功返回0,出错返回-1并设置errno */

其中sockaddr结构用来保存socket信息:

struct sockaddr {
    unsigned short sa_family;  /* 地址族, AF_xxx */
    char sa_data[14];          /* 14 字节的协议地址 */
};

另外还可以用一个更方便的结构sockaddr_in结构来保存信息:

struct sockaddr_in {
    short int sin_family;        /* 地址族 */
    unsigned short int sin_port; /* 端口号,设为0表示系统随机选择端口号 */
    struct in_addr sin_addr;     /* IP地址,设为INADDR_ANY表示本机地址 */
    unsigned char sin_zero[8];   /* 填充0 以保持与struct sockaddr同样大小 */
};

由于大小相等,sockaddr_in可以转换为sockaddrsockaddr_in中,

connect

面向连接的客户程序使用Connect函数来配置socket并与远端服务器建立一个TCP连接:

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
/*
 * @sockaddr : 远端服务的地址
 * @addrlen : 远端地址服务的结构
 * 成功返回, 出错返回-1
 */

listen

Listen函数使socket处于被动的监听模式,并为该socket建立一个输入数据队列,将到达 的服务请求保存在此队列中,直到程序处理它们:

int listen(int sockfd, int backlog);
/* 成功返回0,出错返回-1 */

backlog表示进程所要入队的连接请求数量,实际值由系统决定,但不能超过<sys/socket.h>中的SOMAXCONN,该值默认为128。

accept

accept()函数让服务器接收客户的连接请求:

int accept(int sockfd, void *addr, int *addrlen);
/*
 * @sockfd : 被监听的socket描述符
 * @addr : 通常是一个指向sockaddr_in变量的指针,存放client端的信息
 * @addrlen : 常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量
 * 成功时返回0, 出错返回-1 并设置errno
 */

数据传输

可以直接用read()write(),也可以使用send()recv()以及sendto()recvfrom():

int send(int sockfd, const void *msg, int len, int flags);
/*
 * @sockfd :想用来传输数据的socket描述符
 * @msg : 指向要发送数据的指针
 * @len : 以字节为单位的数据的长度
 * @flags : 一般情况下置为0(关于该参数的用法可参照man手册)
 * 成功返回实际上发送的字节数
 */

 int recv(int sockfd,void *buf,int len,unsigned int flags);
 /*
  * 参数定义和 send() 类似
  * 成功返回接受到的字节数
  */

sendto()recvfrom()用于在无连接的数据报socket方式下进行数据传输。由于本地 socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址:

 int sendto(int sockfd, const void *msg, int len, unsigned int flags,
            const struct sockaddr *to, int tolen);
 int recvfrom(int sockfd,void *buf, int len,unsigned int flags,
              struct sockaddr *from, int *fromlen);

结束传输

可以调用close(sockfd)来释放socket。

还可以使用shutdown()函数来关闭socket,该函数允许你只停止在某个方向上的数据传 输,而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该 socket上接受数据,直至读入所有数据。

实例

下面的程序实现了一个网络服务,该服务接受一个字符串的命令,执行该命令,并且把结 果返回给客户端。分别使用了C语言的多进程、多线程模式,以及Python的多线程模式实现 。

C语言实现

客户端

/* cmd_client.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define BUFLEN 256

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;

    char buffer[BUFLEN];
    if (argc < 3) {
       fprintf(stderr,"usage %s hostname port\n", argv[0]);
       exit(0);
    }
    portno = atoi(argv[2]);

    if ((server = gethostbyname(argv[1])) == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr,
          (char *)&serv_addr.sin_addr.s_addr,
          server->h_length);
    serv_addr.sin_port = htons(portno);

    while (1) {
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
            error("ERROR opening socket");

        if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
            error("ERROR connecting");
        printf(">>> ");
        bzero(buffer, BUFLEN);
        fgets(buffer, BUFLEN, stdin);
        if((n = send(sockfd, buffer, strlen(buffer), 0)) <= 0)
             error("ERROR writing to socket");
        bzero(buffer, BUFLEN);
        while ((n = recv(sockfd, buffer, BUFLEN, 0)) > 0) {
            printf("%s",buffer);
        }
    }
    return 0;
}

多进程服务器端

/* A simple server to run system commands use multi process */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORTNO 4444
#define BACKLOG 10
#define BUFLEN 256

void error(const char *msg) {
    perror(msg);
    exit(1);
}

/*
 * function really do the service
 * run cmd geted from client and return the output to client
 */
void serve(int sockfd) {
    int n;
    char buffer[BUFLEN];
    FILE *fp;

    bzero(buffer, BUFLEN);
    if ((n = read(sockfd,buffer, BUFLEN)) < 0)
        error("ERROR reading from socket");
    printf("CMD : %s\n",buffer);

    if ((fp = popen(buffer, "r")) == NULL)
        error("ERROR when popen");
    while (fgets(buffer, BUFLEN, fp) != NULL) {
        if (send(sockfd, buffer, BUFLEN, 0) == -1)
            error("send ERROR");
    }
    pclose(fp);
    close(sockfd);
    exit(0);
}

/*
 * Init listen socket and bind it to addr
 */
int init_server() {
    int sockfd;
    struct sockaddr_in serv_addr;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        error("ERROR opening socket");

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(PORTNO);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        error("ERROR on binding");

    return sockfd;
}

/*
 * Use a child process to serve for every connection
 */
int main(int argc, char *argv[]) {
    signal(SIGCHLD,SIG_IGN);       /* do not care about child process */

    pid_t pid;
    int sockfd = init_server();
    int newsockfd;
    struct sockaddr_in cli_addr;
    socklen_t clilen = sizeof(cli_addr);

    listen(sockfd, BACKLOG);

    while (1) {
        if ((newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen)) < 0)
            error("ERROR on accept");
        if ((pid = fork()) < 0) {
            error("Fork Error");
        } else if (pid == 0) {          /* child */
            serve(newsockfd);
        } else {                        /* parent */
            close(newsockfd);
        }
    }
}

多线程服务器端

/* A simple server to run system commands use multi threads */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>

#define PORTNO 4444
#define BACKLOG 10
#define BUFLEN 256

void error(const char *msg) {
    perror(msg);
    exit(1);
}

/*
 * function really do the service in sub thread
 * run cmd geted from client and return the output to client
 */
void *serve(void *arg) {
    int sockfd = *(unsigned int *)arg;
    int n;
    char buffer[BUFLEN];
    FILE *fp;

    printf("start serve...\n");
    bzero(buffer, BUFLEN);
    if ((n = read(sockfd, buffer, BUFLEN)) < 0)
        error("ERROR reading from socket");
    printf("3start serve...\n");
    printf("CMD : %s\n",buffer);

    if ((fp = popen(buffer, "r")) == NULL)
        error("ERROR when popen");
    while (fgets(buffer, BUFLEN, fp) != NULL) {
        if (send(sockfd, buffer, BUFLEN, 0) == -1)
            error("send ERROR");
    }
    pclose(fp);
    close(sockfd);

    printf("end serve...\n");
    return(0);
}

/*
 * Init listen socket and bind it to addr
 */
int init_server() {
    int sockfd;
    struct sockaddr_in serv_addr;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        error("ERROR opening socket");

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(PORTNO);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        error("ERROR on binding");

    return sockfd;
}
/*
 * Use a sub threads to serve for every connection
 */
int main(int argc, char *argv[]) {
    signal(SIGCHLD,SIG_IGN);

    int sockfd = init_server();
    int newsockfd;
    struct sockaddr_in cli_addr;
    socklen_t clilen = sizeof(cli_addr);

    listen(sockfd, BACKLOG);

    while (1) {
        if ((newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen)) < 0)
            error("ERROR on accept");
        printf("newsockfd=%d\n", newsockfd);
        pthread_t t;
        int fd_in_thread = newsockfd;
        if(pthread_create(&t, NULL, serve, (void *)&fd_in_thread) != 0)
            error("ERROR on pthread_create");
    }
}

Python实现

客户端

#!/usr/bin/evn python
# -*- coding:utf-8 -*-

from socket import *
import commands

HOST = "localhost"
PORT = 4444
ADDR = (HOST, PORT)
BUFLEN = 256
BAKLOG = 10

def client():
    while True:
        clisock = socket(AF_INET, SOCK_STREAM)
        clisock.connect(ADDR)
        cmd = raw_input("\n>>> ")
        clisock.send(cmd)
        while True:
            data = clisock.recv(BUFLEN)
            print data
            if not data:
                break

if __name__ == '__main__':
    client()

多线程服务器端

#!/usr/bin/evn python
# -*- coding:utf-8 -*-

import threading
import signal
from socket import *
import commands
import sys

HOST = ""
PORT = 4444
ADDR = (HOST, PORT)
BUFLEN = 256
BAKLOG = 10


def server():
    listensock = socket(AF_INET, SOCK_STREAM)
    listensock.bind(ADDR)
    listensock.listen(BAKLOG)

    while True:
        clisock, addr = listensock.accept()
        t = ServeThread(clisock)
        t.start()

    listensock.close()

class ServeThread(threading.Thread):
    def __init__(self, sock):
        threading.Thread.__init__(self)
        self.sock = sock

    def run(self):
        cmd = self.sock.recv(BUFLEN)
        print "CMD:%s" % cmd
        output = commands.getstatusoutput(cmd)[1]
        self.sock.send(output)
        self.sock.close()
        sys.exit(0)


if __name__ == '__main__':
    server()

参考资料:



分享到:
评论

相关推荐

    Python网络编程之TCP套接字简单用法示例

    上学期学的计算机网络,因为之前还未学习python,而java则一知半解,C写起来又麻烦,所以一直都没有真正实现过TCP套接字编程。 最近学习了python,而用它来写套接字又十分方便简单,所以当然要试一试咯。 下面根据...

    C# Winform Socket 字符串发送 套接字编程 精简程序核心技术

    我查阅网上的socket技术代码,大部分写的功能都很多,这样不利于初学者学习核心代码,所以我做了个最简单的,字符串发送程序,希望对爱好者有所帮助!

    c语言socket编程指南

    最基本最简单的入门概念级教程 适合初学者 目录: 1) 什么是套接字? 2) Internet 套接字的两种类型 3) 网络理论 4) 结构体 ...21) 数据报套接字Socket 22) 阻塞 23) select()--多路同步I/O

    MFC网络编程之自制浏览器

    网络编程,当然要用到Windows Socket(套接字)技术。Socket相关的操作由一系列API函数来完成,比如socket、bind、listen、connect、accept、send、sendto、recv、recvfrom等。调用这些API函数有一定的先后次序,有些...

    c#网络编程

    主要讲述了基于套接字(Socket)进行网络编程的基本概念,其 中包括 TCP 协议、套接字、聊天程序的三种开发模式,以及两个基本操作:侦听端口、连接 远程服务端;第二篇讲述了一个简单的范例:从客户端传输字符串到...

    网络游戏框架服务端编程

     提供基于win32、linux和frcebsd等多种平台的网络游戏编程的基础——套接字编程的api参考和实例。  通过分析服务器组的模型和简单插件式游戏的服务器模块等的示例源程序代码,介绍实际网络游戏开发的关键技术。

    linux programming instances网络编程教程 附源代码

    全书由13章组成,内容涉及到Lindx系统编程基础、TCP/UDP协议、套接字编程概念及I/O模型、高级编程中需要用到的进程问通信同步、多路复用、多线程编程和一些高级套接字控制方法、IPv6介绍以及网络安全等。...

    c#网络编程聊天室

     本文是该系列第一篇,主要讲述了基于套接字(Socket)进行网络编程的基本概念,其中包括TCP协议、套接字、聊天程序的三种开发模式,以及两个基本操作:侦听端口、连接远程服务端;第二篇讲述了一个简单的范例:从...

    C#网络核心编程(Word版电子书+PPT+源代码+习题解答)

    1.3.2 面向连接的套接字 21 1.3.3 无连接的套接字 23 1.4 网络流 24 1.5 习题1 25 第2章 TCP应用编程 27 2.1 同步TCP应用编程 28 2.1.1 使用套接字发送和接收数据 28 2.1.2 使用NetworkStream对象发送和接收数据 30 ...

    基于UDP协议的字符串的回射显示操作

    基于udp协议、c语言、socket套接字编程的字符串回显程序,基于s/c架构,运行环境是VC++6.0 ,dos界面的控制台应用程序。实现功能是,从客户主机发送字符串到服务器主机,最后回射到客户机上进行显示。代码简单有注解...

    MUD游戏编程part7

    包括编译器的设置、套接字错误代码、C++基本知识和模板基本知识,另外还包括本书所用到的术语。.. 本书是游戏开发经典丛书系列之一,适合游戏开发人员、业余游戏软件开发爱好者,也可以作为大专院校相关专业的参考...

    VB.net 网络通讯实例

    而套接字(Socket)就能够胜任这项工作,套接字不仅能够实现各种类型数据在网络上的传输和接收,也是实现网络中其他应用协议的关键。诸位若想真正成为网络编程的高手,必须掌握Socket的使用方法。最后希望本文能够...

    MUD游戏编程part1

    包括编译器的设置、套接字错误代码、C++基本知识和模板基本知识,另外还包括本书所用到的术语。.. 本书是游戏开发经典丛书系列之一,适合游戏开发人员、业余游戏软件开发爱好者,也可以作为大专院校相关专业的参考...

    文件传输协议的简单设计与实现

    实验室各计算机具备Windows环境中套接字socket 的编程接口功能,可为用户提供全网范围的进程通信功能。本实验要求学生利用这些功能,设计和实现一个简单的文件传送协议。 2、具体要求 用socket 编程接口编写两个...

    基于流媒体的网络服务器

    本论文论述了基于TCP协议,运用多线程技术和socket套接字原理,通过自定义协议和良好的人机界面环境操作,实现了一种最广泛的流媒体技术应用——音频点播功能。 此流媒体网络服务器与基于嵌入式Windows CE播放系统...

    MUD游戏编程part3

    包括编译器的设置、套接字错误代码、C++基本知识和模板基本知识,另外还包括本书所用到的术语。.. 本书是游戏开发经典丛书系列之一,适合游戏开发人员、业余游戏软件开发爱好者,也可以作为大专院校相关专业的参考...

    Python的Asyncore异步Socket模块及实现端口转发的例子

    Asyncore模块提供了以异步的方式写入套接字服务客户端和服务器的基础结构。 只有两种方式使一个程序在单处理器上实现“同时做不止一件事”。多线程编程是最简单和最流行的方式,但是有另一种很不一样的技术,可以...

Global site tag (gtag.js) - Google Analytics