作者:糖果

[问题] C和python的Socket通信

Socket是linux提供的基础服务,用于实现计算机间的tcp/ip协议通信。python是强大的高级解释行语言,功能抽象程度高,语法简洁功能强大。网络上有很多socket入门例子程序,都是用C语言写的,传统的CS服务器客户端演示程序。

本文提供的同样是最简单的CS结构通信程序,区别是,Server用Python编写,Client用C语言编写。

基础的Socket API 使用是模式化的,没有算法过程,只有配置过程。而pyhon被誉为是可以运行的伪语言,可以更好的说明Socket API的模式化使用步骤。

[解决案]
首先列出Server端程序的Python代码,除了import和print,几乎其中的每一句都是socket API使用步骤中,不可或缺的部分。

C语言使用Socket的套路几乎和python的一样,区别是,C语言本身还要考虑buf空间的申请和释放,判断socket各个步骤成功异常。还要准备hostent结构体和sockaddr结构体,进行更详细定义过程。

server.py

import socket
#取得一个socket对象句柄
s = socket.socket()                 
#取得主机名
host = socket.gethostname()                  
#定义端口号
port = 1236                  
#将socket对象句柄,与指定的IP和Port号绑定。
s.bind((host, port))                  
#服务其开始监听数据
s.listen(5)                  
#大循环,开始堵塞式的数据读取
while True:                  
    #接受外部链接请求“c”其他客户机与本机链接产生的,新的通信socket句柄,“addr”表示client记得IP地址。
    c, addr = s.accept()                  
    #接受1024大的数据
    data = c.recv(1024)                  
    print data
    print 'Got connection from:', addr, data
    #相应链接,把一条英文信息返回给client
    c.send('Thank you for connecting:')                 
    #关闭socket链接
    c.close()                 

运行服务器端程序:

python server.py

“坑”提示:代码“c, addr = s.accept()”中,c和addr之间的符号是", "逗号,输入时请注意!


client.c
下面是一段c语言的客户端程序。
一般来说,需要对“hostent”和“sockaddr_in”进行解释,还有对socket, recv, connect,send等API接口参数含义的介绍。就不介绍了,直接在系统里man一下API吧,或是发挥一下自己想象力,然后代码实践,再看文档求证一下吧!因为API不用背,需要动态的理解。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define PORT 1236
#define BUFFER_SIZE 1024

int main(int argc, char** argv)
{
  int sock_fd, recvbytes;
  char buf[BUFFER_SIZE];
  struct hostent *host;
  struct sockaddr_in serv_addr;
  if (argc < 2) {
    fprintf(stderr, "Please enter the server's hostname!\n");
    exit(1);
  }

  if ((host=gethostbyname(argv[1]))== NULL) {
    herror("get host b name error!");
  }


  if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    perror("Create socket error!");
    exit(1);
  }
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(PORT);
  serv_addr.sin_addr = *((struct in_addr*)host->h_addr);

  printf("hostname:%s", host->h_addr);

  bzero(&(serv_addr.sin_zero), 8);

  if (connect(sock_fd, (struct sockaddr* )&serv_addr, sizeof(struct sockaddr)) == -1) {
    perror("connect error");
  }

  if (send(sock_fd, "spring", 8,0) == -1) {
    perror("send error!");
  }


  if ((recvbytes = recv(sock_fd, buf, BUFFER_SIZE, 0)) == -1) {
    perror("recv error!");
    exit(1);
  }
  
  buf[recvbytes] = '\0';
  printf("Received:%s", buf);
  close(sock_fd);
  return 0;
}

编译客户端程序:
[code]gcc -o client client.c[/code]
运行客户端程序:
[code]./client 127.0.0.1[/code]

“坑”提示:C语言程序中的
“if ((recvbytes = recv(sock_fd, buf, BUFFER_SIZE, 0)) == -1) ”这句,不要写成:“if (recvbytes = recv(sock_fd, buf, BUFFER_SIZE, 0) == -1)”


以上的C语言client程序,几乎等同于如下python的client代码。
结合上面的python服务器端的程序注释,下面的client端程序几乎不需要解释。

import socket
s = socket.socket()
host = socket.gethostname()
port = 1236
s.connect((host, port))
s.send(bytes("python"))
print s.recv(1024)
s.close()

[尾声]
socket的API使用相对很简单,于Linux下的打开关闭读写文件类似。而socket API的使用,是建立在对TCP/IP基本概念了解的基础上,至少要了解主机和端口号的概念。项目中几乎不会用这么简单的代码,下一篇介绍select基础,基础原理哥附身。

谨以此文,献给大姐!献给想不起API的青春,再见青春,再见的永远的故乡!峰哥护体。

[赠送内容]
TCP状态机

下面是python之间进行UDP通信:


import socket  
  
address = ('127.0.0.1', 1812)  
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  
s.bind(address)  
  
while True:  
    data, addr = s.recvfrom(2048)  
    if not data:  
        print "client has exist"  
        break  
    print "received:", data, "from", addr  
  
s.close()  
import socket  
  
address = ('127.0.0.1', 1812)  
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  
  
while True:  
    msg = raw_input()  
    if not msg:  
        break  
    s.sendto(msg, address)  
  
s.close()  

下面是python之间进行TCP通信:

def tcpServer():   
    srvsock = socket.socket( socket.AF_INET, socket.SOCK_STREAM)   
    srvsock.bind(('', 1812))   
    srvsock.listen(5)   
  
    while True:   
        clisock, (remoteHost, remotePort) = srvsock.accept()   
        print "[%s:%s] connected" % (remoteHost, remotePort)   
        #do something on the clisock   
        clisock.close()   
  
  
if __name__ == "__main__":   
    tcpServer()  
 
def tcpClient():   
    clisock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   
    clisock.connect(('localhost', 1812))   
    #I/O on this clisock   
    #clisock.send("")   
    #dat = clisock.recv(len)   
       
    print dat   
       
if __name__ == "__main__":   
    tcpClient()  

注释:个人劳动成果,转载使用请注明本文作者及出处链接,谢谢合作!