转: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. 开源的c语言人工神经网络计算库 FANN

    这年头机器学习非常的火,神经网络算是机器学习算法中的比较重要的一种.这段时间我也花了些功夫,学了点皮毛,顺便做点学习笔记. 介绍人工神经网络的基本理论的教科书很多.我正在看的是蒋宗礼教授写的<人 ...

  2. SCU 4440 分类: ACM 2015-06-20 23:58 16人阅读 评论(0) 收藏

    SCU - 4440 Rectangle Time Limit: Unknown   Memory Limit: Unknown   64bit IO Format: %lld & %llu ...

  3. find命令之xargs

    在使用 find命令的-exec选项处理匹配到的文件时, find命令将所有匹配到的文件一起传递给exec执行.但有些系统对能够传递给exec的命令长度有限制,这样在find命令运行几分钟之后,就会出 ...

  4. python简单网络服务器

    对于服务器来说建立TCP连接的过程分为4步: 1.建立socket对象:这里与客户端一样,依然是: s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) ...

  5. DMOZ介绍以及如何提交

    转载自 http://www.cnblogs.com/freespider/archive/2009/12/28/1633818.html Dmoz介绍及怎么提交? 1.Dmoz目录简介: Dmoz是 ...

  6. CodeForces 702 A Maximum Increase (贪心,高效算法)

    题意:给定 n 个数,问你连续的最长的序列是几个. 析:从头扫一遍即可. 代码如下: #include <cstdio> #include <string> #include ...

  7. MFC下的各种字符串类型和相互转换

    MFC下的常用字符串数据类型表示的含义: L:Long  长 P:Point  指针 C:Const  常量 W:Wchar_t  宽字符 T:TCHAR  STR:String  字符串 在看看MF ...

  8. [Mac]Mac Xcode 删除已经下载好的模拟器版本

    Delete simulator refences for xCode: Delete the particular simulator runtime references (*.simruntim ...

  9. Visual Studio 2012 主题下的代码配色方案

    默认的VS2012的深色配色方案个人感觉很丑,不是很好看,于是就自己动手配置了一下,突出语法高亮显示,增加代码语法识别度,个人感觉还是可以的. 原来使用的是VAX,但自从VAX导致的我的VS不能输入中 ...

  10. jquery表单内容过滤

    效果:    输入筛选字段后显示效果: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http ...