简单的socket通信

服务端

#pragma comment(lib, "ws2_32.lib")
#include <Winsock2.h>
#include <stdio.h>
void main()
{
    WORD wVersionRequested;	// winsock的版本
    WSADATA wsaData;		// winsock结构体
    int err;				// 错误信息

    // 配置Windows Socket版本
    wVersionRequested = MAKEWORD( 2, 2 );
 
	// 初始化Windows Socket
    err = WSAStartup( wVersionRequested, &wsaData );

    if ( err != 0 ) 
	{
        // 启动错误,程序结束
        return;
    }
 
    /*
	 * 确认的WinSock DLL支持2.2
     * 请注意如果DLL支持的版本大于2.2至2.2
     * 它仍然会返回在wVersion2.2的版本
     */
 
    if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 )
	{
        // 启动错误,程序结束
        WSACleanup( ); // 终止Winsock 2 DLL (Ws2_32.dll) 的使用
        return; 
    }
    
    SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);		// 定义服务器端socket
    SOCKADDR_IN addrSrv;								// socket本地地址
    addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);		// 本机IP
    addrSrv.sin_family=AF_INET;							// 协议类型是INET
    addrSrv.sin_port=htons(6000);						// 绑定端口是6000

	// 将服务器端socket绑定在本地端口
    bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

	// Listen 监听端口
    listen(sockSrv,5);				// 等待连接数目
	printf("Listening...n");

    SOCKADDR_IN addrClient;
    int len=sizeof(SOCKADDR);

    while(1)
    {
        SOCKET sockConn = accept(sockSrv,(SOCKADDR*)&addrClient,&len);
        char sendBuf[100];
        sprintf(sendBuf,"welcome %s to me, this is the server clientn", inet_ntoa(addrClient.sin_addr));

		// 获取到联接则发送字符串
        send(sockConn,sendBuf,strlen(sendBuf)+1,0);
        char recvBuf[100];
		// 获取返回数据
        recv(sockConn,recvBuf,100,0);
        printf("%sn",recvBuf);
		// 关闭socket
        closesocket(sockConn);
    }

}

客户端

#pragma comment(lib, "ws2_32.lib")
#include <Winsock2.h>
#include <stdio.h>
void main()
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;
    
    wVersionRequested = MAKEWORD( 2, 2 );
    
    err = WSAStartup( wVersionRequested, &wsaData );
    if ( err != 0 ) {
        return;
    }
    
    if ( LOBYTE( wsaData.wVersion ) != 2 ||
           HIBYTE( wsaData.wVersion ) != 2 ) {
        // 启动错误,程序结束
        WSACleanup( );
        return; 
    }
    SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
    SOCKADDR_IN addrSrv;

    addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
    addrSrv.sin_family=AF_INET;
    addrSrv.sin_port=htons(6000);

    connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

    char recvBuf[100];

	// 获取数据
    recv(sockClient,recvBuf,100,0);

	// 打印数据
    printf("%sn",recvBuf);

	char message[50] = "这里是Lellansin!";

	// 发送数据
    send(sockClient,message,strlen(message)+1,0);

	// 关闭socket
    closesocket(sockClient);
    WSACleanup();

	getchar();
}

socket 常见函数

accept函数

int accept( SOCKET s, struct sockaddr *addr, int *addrlen);

参数一 s
设置监听socket,该套接口在listen()设置。

参数二 addr (可选)
指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。

参数三 addrlen (可选)
指针,指向存有addr地址长度的整形数。

返回:
成功:非负描述字
失败:-1

accept默认会阻塞进程,直到有客户端建立连接后返回,它返回的是连接用的socket。如果accept成功返回,则服务器与客户已经正确建立连接了,此时服务器通过accept返回的socket来完成与客户的通信。

send函数

int send( SOCKET s, const char *buf, int len, int flags );

不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。

参数一 s
指定发送端socket描述符;

参数二 *buf
指明存放要发送的数据的缓冲区;

参数三 len
指明实际要发送的数据的字节数;

参数四 flags
一般置零

将*buf指向的字符串发送至客户端

recv函数

int recv( SOCKET s, char *buf, int len, int flags );

不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。

参数一 s
指定接收端socket描述符;

参数二 *buf
指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;

参数三 len
指明buf的长度;

参数四 flags
一般置0。

获取到客户端返回的字符串并将其写入到buf中

备注:
以上语法遵从c++,如果用c编译会报错,文章未完,待更新

Node 与 windows shell

本文旨在面向Unix的开发者有兴趣学习一点点Windows脚本。并且演示一个从Node调用PowerShell的简单方法。

Windows PowerShell是由三部分组成:一个命令行shell,脚本语言,和NET框架的集成。它同时支持COM和WMI,所以这是一个完美的管理任务自动化工具与脚本设施相结合。有相当多的节点模块,包括shell脚本 – PowerShell是通过友好的Windows方式实现类似的目标。

Unix系统的模块手册,可以通过包含Windows PowerShell的帮助来支持Windows,这实际上只是XML文件。

如果你是一个有Unix的Node开发基础的开发者,并且在寻求Windows上的支持,那么熟悉PowerShell的基本知识是一个好主意。PowerShell可以用来执行程序,并像是在执行一个单独的进程(就像一个Unix shell)一样运行。针对PowerShell 专门设计的NET程序设计被称为cmdlet -同时也被视为“原生”的命令并且在当前d的PowerShell中执行。实际上他支持将输入(input)和输出(output)视作数组一样的对象的集合,并且可以通过管道读取和写入。

使用PowerShell

windows power shell in start menu

我们并不需要安装。如果你使用的是win7操作系统的话,你可以直接在“开始”菜单里面找到Windows PowerShell。同时还有下面Windows PowerShell ISE(编写PowerShell脚本的IDE)。

一旦它加载,你可以尝试发出一些命令。我提到的帮助页面可以访问帮助文档,所以试试输入

get-help get-service


支持Tab键补充单词,所以输入Get-并按下Tab键补完匹配的命令。当然还可以完成参数。

PowerShell还具有Unix风格的命令。Get-ChildItem 命令可以列出文件和目录,就像Unix中的LS。幸运的是,Get-ChildItem是LS的别名,如果你精通Unix shells的话,那么肯定很容易上手。当然这里面,还有有大量的DOS和Unix风格的其他别名:

 Get-Content:   cat
    Get-Help:   man, help
Set-Location:   cd

使用Node来操作PowerShell

接下来就是我一直想要描述的,关于利用PowerShell方便而且快捷的使用Node来编程。

使用Node的child_process模块:

var exec = require('child_process').exec;

//List a directory
exec('powershell.exe -Command "Get-ChildItem"', function(err, stdout, stderr){

console.log(stdout);

})
.stdin.end();

调用的基本形式是powershell.exe -Command “…” 。而结果应该是这样的:

同时,PowerShell还支持使用COM访问对象:

var exec = require('child_process').exec

, script;

// Use COM objects to get filesystem information

script = '$f = new-object -com "scripting.filesystemobject"; $f.drives | where {$_.drivetype -eq 2} | Select Path,AvailableSpace,FreeSpace,TotalSize';

exec('powershell.exe -Command "' + script + '"', function (err, stdout, stderr) {

console.log(stdout);

})

.stdin.end();

这个可以在什么地方用到?好吧,我觉得某些基于Unix开发的程序和模块可以更容易的移植到Windows上来。而Windows开发者也可以不用准备一些额外的程序来使用某些接口(比如虚拟机…笔者注)。

总结

  • PowerShell是一个命令行shell,脚本语言,并且与NET层集成
  • 它有支持的帮助文件,并且是可扩展的
  • 命令经常操作对象,而不是文本,并且对象可以当做通过管道输入输出(pipelined)
  • Forking using the PowerShell binary is possible, which may help port modules that depend on Unix binaries

英文原文地址:
http://dailyjs.com/2012/06/21/windows-and-node-6/

C语言入门教程 第9讲 指针与数组

上节课后作业

现在有一个数组,存储的是一个同学的期中考试成绩。

int score[8] = { 75, 86, 70, 88, 62, 87, 69, 77 };

那么现在我们要做的事情是:

1)求总分,求平均分

2)用指针遍历数组,求最大值和最小值

附加题:
用一个二维数组存储八门课的名称,例如:
char course[8][256] = { “chinese”, “English” … }
再用二维数组,存储多个人的成绩,用指针遍历求出每门课的平均分

#include<stdio.h>
#include<stdlib.h>

int main()
{
    // 现在有一个数组,存储的是一个同学的期中考试成绩。
    char course[8][256] = { "语文", "数学", "英语", "物理", "化学", "生物", "历史", "地理" };
    char name[5][256] = {"Alan", "Bob", "Clain", "David", "Elis"};
    int score[5][8];
    int sum[5] = {0};
    float avg[8] = {0};

    int i,j;

    for(i = 0; i < 5; i++)
    {
        for(j = 0; j < 8; j++)
        {
            score[i][j] = rand() % 100;
        }
    }


    for(i = 0; i < 5; i++)
    {
        for(j = 0; j < 8; j++)
        {
            sum[i] += score[i][j];
            avg[j] += score[i][j];
        }
    }


    for(i = 0; i < 8; i++)
    {
        printf("t%s", course[i]);
    }

    printf("t总分");

    for(i = 0; i < 5; i++)
    {
        printf("n%s", name[i]);
        for(j = 0; j < 8; j++)
        {
            printf("t %d",score[i][j]);
        }
        printf("t%d", sum[i]);
    }

    printf("n平均分");

    for(j = 0; j < 8; j++)
    {
        printf("t%.1f", avg[j] / 8.0);
    }

    printf("n");
    return 0;
} 

字符数组与指针

字符串 == 字符数组+”结尾

// 对于字符串"Hello world"相当于如下的字符数组 
char hi[] = {'h','e','l','l','o',' ','w','o','r','l','d', '\0'};

printf("%c n", hi[1]);  // e
printf("%c n", "Hello world"[1]); // e

因为字符串相当于字符数组,所以”Hello world”[1]越数组hi[1]的值一样。

数组名 == 起始地址

学会自己研究各种变量

1)如何研究?
学会善用printf

2)怎么知道变量是不是一个指针(或地址)
学会使用*

int a[5] = { 1, 3, 5 };

printf("%d",    a);   // 1638196
printf("%d",   *a);   // 1
printf("%d", *(a+1)); // 3

运行成功,表示这个变量的类型即使不是指针,也是一个地址

常见用法:

int a[5] = { 1, 3, 5 };
int *p;
p = a;
printf("%d", *(p+1)); // 3
printf("%d", *(p+2)); // 5

“Hello world”究竟是?

有了上面的两个结论之后,我就可以继续使用printf来探讨更多的东西。
比如”Hello world”这个字符串所代表的意义。
我们已经知道这个字符串实际上就是一个字符数组,通过”Hello world”[1]甚至可以取到其中的值。那么对于这样一个字符串而言它的数组名(地址)是什么?
运用以上相同的方法可以看到:

printf("%d",   "Hello world" );   // 4337572
printf("%d", *("Hello world"));   // 72
printf("%c", *("Hello world"));   // H
printf("%c", *("Hello world"+1)); // e

多个名字如何用char数组存储?

多个人的名字:

char a[256] = "Alan";
char b[256] = "Bob";
char c[256] = "Cici";

现在要用数组来存储这三个人的名字。
也就是要用一个数组来存储三个数组,数组的每一个元素是一个数组:

char name[3][256] = {"Alan", "Bob", "Cici" };

如何使用指针遍历

int arr[8] = { 1, 1, 2, 3, 5, 8, 13, 21 };
int i;
int *p;
// 普通遍历
for(i = 0; i < 8; i++)
{
    printf("%d ", arr[i]);
}
// 指针遍历
for(p = &(arr[0]); p <= &(arr[7]); p++)
{
    printf("%d ", *p);
}
// 数组名获取指针地址
for(p = arr; p < (arr+8); p++)
{
    printf("%d ", *p);
}

本讲小结

1)如何使用指针遍历数组?
使用循环

2)如何研究各种变量?
尝试使用printf

3)数组与指针什么关系?
数组名就是一个地址,指向这个数据的开端

int a[] = {1, 3, 5, 7, 9};
int *p = a;

printf( "%d n",     *p == a[0] ); // 1
printf( "%d n", *(p+1) == a[1] ); // 1
printf( "%d n", *(p+2) == a[2] ); // 1

可以知道:

*(p+n) == a[n]

引申推导:

a[n] = *(p+n)
     = *(n+p)
     = n[a]

即:

a[n] = n[a]

可以这样理解,下标对于数组来说,就是相对于起始地址的一个偏移量。

c语言入门教程 第10讲 字符串处理