转:http://www.linuxforu.com/2011/09/creating-your-own-server-the-socket-api-part-2/

By Pankaj Tanwar on September 1, 2011 in CodingDevelopers · 0 Comments

Earlier, we created a simple server and client program using the socket API. This time, we’ll first start with a program, and then explain what’s going on. So start up your systems, and get ready to go deeper into socket programming.

As mentioned, let’s dive straight into the code.

The IPv6 version of the server

Here’s the IPv6 version of the server we created in the previous article. There are no big changes, except the appearance of “6″ in the code. Let’s name it serverin6.c:

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
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
    int sfd, cfd;
    char ch;
    socklen_t len;
    struct sockaddr_in6 saddr, caddr;
    sfd= socket(AF_INET6, SOCK_STREAM, 0);
    saddr.sin6_family=AF_INET6;
    saddr.sin6_addr=in6addr_any;
    saddr.sin6_port=htons(1205);
    bind(sfd, (struct sockaddr *)&saddr, sizeof(saddr));
    listen(sfd, 5);
    while(1) {
        printf("Waiting...n");
        len=sizeof(cfd);
        cfd=accept(sfd, (struct sockaddr *)&caddr, &len);
        if(read(cfd, &ch, 1)<0) perror("read");
        ch++;
        if(write(cfd, &ch, 1)<0) perror("write");
        close(cfd);
    }
}

Now let’s review the differences. The first is at Line 6 (sockaddr_in6) and is easily understood; for IPv6 addresses, we need to store the address, port and address family in this address structure, as we have done in lines 13, 14 and 15. At Line 14, in6addr_any is a wildcard for all IPv6 addresses, like INADDR_ANY in IPv4. There’s also a change in accept(), which can be easily understood. And here’s our server working with IPv6. You can set up your IPv6 using the following command as root:

ip -f inet6 addr add face:1f::ea54:a dev eth0

Here, -f specifies the family (inet6), and addr is for the address — we added face:1f::ea54:a(while writing IPv6 addresses, leading zeros can be dropped — the above address is actuallyface:001f::ea54:000a). You can give any address; and to randomise it, you can use your MAC address. The dev parameter specifies the device we are setting the address for — in this case,eth0. You can check the results using the ifconfig command.

Compile and run the server as follows:

cc serverin6.c -o serverin6
./serverin6

Before writing the client, you can see that it even works with our IPv4 client, as the IPv4 address is also pointing to the same machine. For every server, we can use telnet to test if it’s working as expected — for example:

telnet localhost 1205

Type in the character you want to send to the server and press Enter, and the server will reply with the next ASCII character, and close the connection. Remember, for IPv4, localhost is 127.0.0.1, and for IPv6, it is ::1.

Client (IPv6 version)

Now let’s write the IPv6 version client, and name it clientin6.c:

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
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char **argv)
{
    int cfd;
    struct sockaddr_in6 addr;
    char ch;
    if(argc!=3) {
        printf("Usage: %s in6addr charactern", argv[0]);
        return -1;
    }
    if( ! inet_pton(AF_INET6, argv[1], &(addr.sin6_addr))) { /* returns 0 on error */
        printf("Invalid Addressn");
        return -1;
    }
    ch=argv[2][0];      /* Set the character to second argument */
    cfd=socket(AF_INET6, SOCK_STREAM, 0);
    addr.sin6_family=AF_INET6;
    addr.sin6_port=htons(1205);
    if(connect(cfd, (struct sockaddr *)&addr,
    sizeof(addr))<0) {
        perror("connect error");
                return -1;
    }
    if(write(cfd, &ch, 1)<0) perror("write");
    if(read(cfd, &ch, 1)<0) perror("read");
    printf("Server sent: %cn", ch);
    close(cfd);
    return 0;
}

Here, again, the changes are small and self-explanatory — just an added “6″. The client takes the IP address and characters as arguments, so to run it, just type the following:

cc clientin6.c -o clientin6
./clientin6  ::1  d

When d is sent, the server replies with e. The output will be the same as for the last examples, shown in the figures of the last article. To process the address, we have used the inet_pton()function, which we’ll look at in more detail in the next section.

Diving deeper

First, let’s look at the function we used in our client to convert the address from string to numeric. In binary, the IP address 192.168.1.23 will be 11000000 10101000 00000001 00010111 (a 32-bit string of 0′s and 1′s). It will be a 128-bit string for IPv6 (64-bit address). The human-readable addresses 192.168.1.23 or face:1f::ea54:a are the ‘presentation’ form (the p of the function), and n is for the numeric (binary) form. The function is prototyped in <arpa/inet.h> as:

int inet_pton (int family, const char *strptr, void *addrptr);

This function can convert both IPv4 and IPv6 addresses from string form to binary as needed by the machine. The first argument is, of course, the family: AF_INET or AF_INET6. The *strptrargument is the address in string form, and *addrptr will be the destination where the numeric address will be stored; this must be a structure (sin_addr for IPv4, and sin6_addr for IPv6). The function returns 1 on success and 0 on error. The next function is:

const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

This function does exactly the reverse of the previous case; it converts the numeric (addrptr) and stores in the presentation (strptr). The last argument is len, the size/length of the destination variable, to avoid overflow. For ease, netinet/in.h defines the size constants, as follows:

#define INET_ADDRSTRLEN     16
#define INET6_ADDRSTRLEN    46

There are other functions available for the same purpose — inet_aton() and inet_ntoa() — but only for IPv4; therefore, we aren’t using them.

Now let’s look at the functions to convert bytes between the network protocol stack and host to get machine-independent code. Machine architecture is either big-endian or little-endian, i.e., how the machine stores data. The high-order byte at a higher address and low-order byte at a lower address is little-endian. Conversely, high-order byte at a lower address, and low-order byte at a higher address will be big-endian.

For example, if we have a 2-byte integer storing 0x1234 at address 0×0200, it will occupy two bytes: 0x0200 and 0x0201. If, at 0x0200 we have 0x12, and at 0x0201 we have 0x32, the machine is little-endian. If the values are stored the opposite way, the machine is big-endian. The book Unix Network Programming, by W Richard Stevens, gives a program to find out what machine you have. Let’s look at the functions now, defined in netinet/in.h:

uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(uint32_t host32bitvalue);

These two functions return 16-bit and 32-bit values respectively; htons() stands for the host to network short, htonl() for long. They convert from host byte-ordering to network-stack byte-ordering. The functions ntohs() and ntohl() do the reverse (from network byte-ordering to host byte-ordering).

These functions are particularly useful when we ask the system to provide us the information of the host running a client or server. Now let’s try to extract some information from the client. Just add these lines to the server:

char address[INET6_ADDRSTRLEN]; /* at declaration section */
printf("Connected to client %s at port %dn", inet_ntop(AF_INET6, &caddr.sin6_addr,
buff, sizeof(buff)), ntohs(caddr.sin6_port)); /* after the call to accept() */

You can see the output after these code changes in Figure 1. The code itself is clear—we have used the functions we just understood, above.

Figure 1: Server running

A little exercise

Now put this code in the proper section of the server program, to make it do something more interesting than what we did earlier:

if(read(cfd, &ch, 1)<0) perror("read");
while( ch != EOF) {
        if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z'))
        ch^=0x20;    /* EXORing 6th bit will result in change in case */
        if(write(cfd, &ch, 1)<0) perror("write");
        if(read(cfd, &ch, 1)<0) perror("read");
}

Yes, you’ve got it right: add it after the call to accept(). Now, if you’re as sleepy as I am right now, then just telnet to the server — or go on and write a client; great, that’s the spirit.

Well, before closing, let’s look at the output of a telnet session. Run telnet ::1 1205 in your shell, and start typing. See Figure 2 for a sample output.

Figure 2: Telnetting

On hitting Ctrl+D, the server will close the connection, and wait for a new one. And for those lazy fellas who didn’t want to write their own client’s :P, you just needed to put this into your client, instead of only write() and read() calls, for the result shown in Figure 3:

while(1) {
      ch=getchar();
      if(write(cfd, &ch, 1)<0) perror("write");
      if(read(cfd, &ch, 1)<0) perror("read");
      printf("%c", ch);
}

Figure 3: Client running

Compile and run your client.

To close, use Ctrl+D to send EOF to the server, and the server will close the connection to the client, instead of shutting the server down using Ctrl-C. Or you can also close the connection from the client; we’ll handle this problem later, using signal handlers. Now, I think I should try to get some rest. Good night, and FOSS ROCKS!

Creating Your Own Server: The Socket API, Part 2的更多相关文章

  1. Creating Your Own Server: The Socket API, Part 1

    转:http://www.linuxforu.com/2011/08/creating-your-own-server-the-socket-api-part-1/ By Pankaj Tanwar  ...

  2. The Socket API, Part 5: SCTP

    转:http://www.linuxforu.com/2011/12/socket-api-part-5-sctp/ By Pankaj Tanwar on December 29, 2011 in  ...

  3. Mac mySql ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)的解决办法

    我的环境:Mac 10.11.6 ,mysql  5.7.14  . mac mySql 报错ERROR 2002 (HY000): Can't connect to local MySQL serv ...

  4. .NET3.5中的高性能 Socket API

    转载:http://www.cnblogs.com/TianFang/archive/2007/11/09/954730.html 在 .NET Framework 2.0 版本中,System.Ne ...

  5. UNIX网络编程——SOCKET API和TCP STATE的对应关系_三次握手_四次挥手及TCP延迟确认

    在socket系统调用中,如何完成三次握手和四次挥手: SOCK_DGRAM即UDP中的connect操作知识在内核中注册对方机器的IP和PORT信息,并没有建立连接的过程,即没有发包,close也不 ...

  6. ERROR 2002 (HY000): Can&#39;t connect to local MySQL server through socket

    在安装好了MySQL之后,使用了新的配置文件后.MySQL服务器能够成功启动,但在登陆的时候出现了ERROR 2002 (HY000): Can't connect to local MySQL se ...

  7. Mac - Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

    在终端输入mysql,结果出现 macdeMacBook-Pro:~ mac$ alias mysql=/usr/local/mysql/bin/mysql macdeMacBook-Pro:~ ma ...

  8. JNI 和 socket api

    1.JavaVM 和 JNIEnvJNIEnv是一个与线程相关的变量,不同线程的JNIEnv彼此独立.JavaVM是虚拟机在JNI层的代表,在一个虚拟机进程中只有一个JavaVM,因此该进程的所有线程 ...

  9. socket编程 ------ BSD socket API

    伯克利套接字(Berkeley sockets),也称为BSD Socket.伯克利套接字的应用编程接口(API)是采用C语言的进程间通信的库,经常用在计算机网络间的通信. BSD Socket的应用 ...

随机推荐

  1. Having the Result Set of a Stored Proc Sent to You by RSS Feed.

    Having the Result Set of a Stored Proc Sent to You by RSS Feed. by JBrooks   14. 十二月 2010 12:44 I wa ...

  2. c++builder Color

    procedure ExtractRGB(const Color: Graphics.TColor; out Red, Green, Blue: Byte); var RGB: Windows.TCo ...

  3. devexpress皮肤设置

    DEV的皮肤管理控件:SkinController: TdxSkinController; 皮肤设置:SkinController.SkinName := appInfo.SkinName; TdxR ...

  4. POJ 2386 Lake Counting (水题,DFS)

    题意:给定一个n*m的矩阵,让你判断有多少个连通块. 析:用DFS搜一下即可. 代码如下: #pragma comment(linker, "/STACK:1024000000,102400 ...

  5. 实现jsp网站添加到收藏夹

    var ctrl = (navigator.userAgent.toLowerCase()).indexOf('mac') != -1 ? 'Command/Cmd': 'CTRL';         ...

  6. Excel数据通过plsql导入到Oracle

    Excel数据通过plsql导入到Oracle 1 准备Excel导入数据 2 把Excel文件另存为(文本文件(制表符分隔)(*.txt)) 或者将Excel文件另存为(Unicode文本) 之后唯 ...

  7. maven 基础整理

    教程 依赖管理 IDE设置121 IntelliJ,Edit Configurations中添加maven,选中 Resolve Workspace artifacts能自动编译依赖模块 内置命令 m ...

  8. C\C++编程中:相对路径+绝对路径

    电脑硬盘E盘下,建文件夹“test”,"test"下建立子文件夹“file”,"file"下建子文件夹“data”, 电脑资源管理器显示目录  E:\test\ ...

  9. Function.caller

    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/caller 非标准 ...

  10. 基于KVM的虚拟化研究及应用

    引言 虚拟化技术是IBM在20世纪70年代首先应用在IBM/370大型机上,这项技术极大地提高了大型机资源利用率.随着软硬件技术的迅速发展,这项属于大型机及专利的技术开始在普通X86计算机上应用并成为 ...