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

By Pankaj Tanwar on August 1, 2011 in CodingDevelopers · 2 Comments

In this series of articles aimed at newbies to network programming (knowledge of C is a prerequisite), we’ll learn how to create network clients and servers using the UNIX Socket API. We’ll start with creating simple server-client programs, and later on try something more complex. We’ll also try to understand how different servers work. I’ve tried to include a lot of details, but if you find some information missing, please feel free to comment.

Since we’re discussing sockets in network programming, newbies should first understand the OSI model layers, and the protocols used in these layers. Every layer in the model is responsible for doing some particular work, to make data transfer possible on the network. Each layer abstracts the lower layers’ work to the layer above it. I recommend that if you don’t know the ISO OSI (Open Systems Interconnection) Reference Model, you should read up on it. A good starting point is Wikipedia.

Here, we focus on the session layer (which establishes and maintains sessions) and the transport layer, which provides end-to-end reliable and unreliable delivery. Some protocols are TCP (for reliable connections), UDP (non-reliable connections) and SCTP (an advanced protocol with multiple-connection capability). Please also look at this and this for information on TCP/IP.

Transmission Control Protocol (TCP)

TCP is a connection-oriented protocol that provides a reliable, full-duplex byte stream to users. Here, we will directly communicate with the transport layer, using TCP from the application layer, where users can communicate with the program.

Here are some important features of TCP. It is a reliable protocol (the opposite of connection-less UDP, which we will look at in later articles). After transmitting a packet, it awaits an acknowledgement; if one is not returned, it retransmits the packet a number of times (depending upon the implementation). If the data cannot be transmitted, it notifies the user and closes the connection.

TCP decides how long to wait for an acknowledgement by the estimating RTT (Round Trip Time) between server and client. It also associates sequence numbers with segments, so that segments arriving out of order can be reordered at the receiver. Duplicate segments (retransmitted due to delays) can thus also be ignored. TCP provides flow control; the receiver can tell the sender how many bytes it is willing to receive, so that a slow receiver is not flooded.

TCP connections are full-duplex — the application can both send and receive data simultaneously.

Simple servers

Now let’s create a simple server to understand Internet sockets (server.c). Initially, our code will be for IPv4, but in later articles, we’ll look at IPv6, and then move to protocol-independent code.

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;
        int ch='k';
        struct sockaddr_in saddr, caddr;
 
        sfd= socket(AF_INET, SOCK_STREAM, 0);
        saddr.sin_family=AF_INET;           /* Set Address Family to Internet */
        saddr.sin_addr.s_addr=htonl(INADDR_ANY);    /* Any Internet address */
        saddr.sin_port=htons(29008);            /* Set server port to 29008 */
                            /* select any arbitrary Port >1024 */
        bind(sfd, (struct sockaddr *)&saddr, sizeof(saddr));
        listen(sfd, 1);
        while(1) {
                printf("Server waitingn");
                cfd=accept(sfd, (struct sockaddr *)NULL, NULL);
                if(read(cfd, &ch, 1)<0) perror("read");
                ch++;
                if(write(cfd, &ch, 1)<0) perror("write");
                close(cfd);
        }
}

About the code

The first thing to look at is struct sockaddr_in. This structure is used to hold an Internet (IP) address in the sin_addr member, which is of the type struct in_addr and holds a 32-bit unsigned integer. The port number is held in the sin_port member, an unsigned 16-bit integer (so port numbers must be less than 65536).

Next, let’s look at a call to socket() (the includes required are sys/types.h and sys/socket.h):

int socket(int domain, int type, int protocol);

A successful call to socket() returns a descriptor that will be used for end-point communication. The first argument, domain, specifies a communication domain — the protocol family to be used for communication. According to sys/sockets.h, these are:

Name Purpose
AF_UNIX, AF_LOCAL Local communication
AF_INET IPv4 Internet protocols
AF_INET6 IPv6 Internet protocols
AF_IPX IPX — Novell protocols
AF_NETLINK Kernel user interface device
AF_X25 ITU-T X.25 / ISO-8208 protocol
AF_AX25 Amateur radio AX.25 protocol
AF_ATMPVC Access to raw ATM PVCs
AF_APPLETALK AppleTalk
AF_PACKET Low-level packet interface

AF stands for Address Family. We’re using AF_INET here — Internet IPv4. The next argument,type, specifies the type of communication, from these choices:

SOCK_STREAM Sequenced, reliable, two-way, connection-based byte stream (TCP,SCTP, etc)
SOCK_DGRAM Datagrams — connectionless, unreliable (UDP)
SOCK_SEQPACKET Sequenced, reliable, two-way, connection-based data transmission path for datagrams of fixed maximum length (SCTP)
SOCK_RAW  Raw network protocol access (Transport layer protocols not required)

The protocol argument specifies a protocol to be used with the socket. Normally, only a single protocol exists to support a particular socket type within a given family (as specified within parentheses above). In such cases, this argument is 0.

Next, let us set the address in sin_addr, as explained above. With the call to socket(), a socket is created, but no address is assigned to it. So we have the bind() function:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

This is used to bind the socket descriptor sockfd with the address addr; and addrlen is its length. This is called assigning a name to the socket. Next up is listen():

int listen(int sockfd, int backlog);

The listen() call marks the socket referred to by sockfd as a passive socket — one that will be used to accept incoming connections. The socket type should be SOCK_STREAM orSOCK_SEQPACKET, i.e., a reliable connection. The backlog argument defines the maximum length of the queue of pending connections for sockfd. If the queue grows above this, connections will be refused by clients.

Next, let’s enter an infinite loop to keep on serving the client requests. Here we have to create another socket descriptor for the client, by calling accept. Now, whatever is written to this descriptor is sent to the client, and whatever is read from this descriptor is the data the client sent to the server:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

The accept() system call is used with socket types SOCK_STREAM and SOCK_SEQPACKET. It extracts the first connection request on the queue of pending connections for the listening socket sockfd, creates a new connected socket, and returns a new descriptor referring to that socket — in our program, cfd.

The newly created socket is not in the listening state. The original socket sockfd is unaffected by this call. The addr argument contains the address of the remote machine to which we connect; since we don’t know the client’s address in advance, it’s NULL here. The addrlen argument is the length of addr — so, again, this is NULL.

Then, let us read a character (sent by the client to the server) from the descriptor with read(), increment it, and write to the descriptor using write(), to send the character to the client. Then close the descriptor by calling close().

The error handling I chose is based on the knowledge that these functions return a negative number on failure; I use perror() to display an error number message.

The client

And now the client (client.c). This client sends a character to the server running on port 29008 (or any arbitrary port):

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
#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_in addr;
    char ch='r';
    cfd=socket(AF_INET, SOCK_STREAM, 0);
    addr.sin_family=AF_INET;
    addr.sin_addr.s_addr=inet_addr("127.0.0.1"); /* Check for server on loopback */
    addr.sin_port=htons(29008);
    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("nReply from Server: %cnn",ch);
    close(cfd);
    return 0;
}

The flow of the client program is similar to the server. The first difference is that it specifies an Internet address for the server (the loop-back localhost address) in sin_addr.

Next, instead of listening, it calls the connect() system call to connect the socket referred to bysockfd to the address specified by addr. The returned descriptor will be used to communicate to the specified address.

Then, in the program, we have used write() and read() to send a character to the server, and receive a character, and then closed the descriptor.

Running the programs

Compile the programs as follows:

cc server.c -o server
cc client.c -o client

Then, run them:

./server &
./client

Run each programs in a different terminal for a better view. See Figure 1 for the server’s output, and Figure 2 for the client’s.

Figure 1: Server running

Figure 2: Client's output

A good beginning, right? In the next article we’ll rewrite both these programs for IPv6, and move further to UDP. And yes, FOSS rocks!

Feature image credit: Emilian Robert Vicol. Reused under terms of CC-BY 2.0 License.

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

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

    转:http://www.linuxforu.com/2011/09/creating-your-own-server-the-socket-api-part-2/ 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. 第三百零八至三百二十天 how can I 坚持

    十三天..2月4号至2月16号,好快,假期还没开始就结束了.一一回忆下. 2月4号,腊月二十六,最后一天上班,没多大事,好像是玩了一天,东月回家,貌似路上好折腾,晚上D401,和她聊了一路,也聊了好多 ...

  2. 转】MyEclipse使用总结——使用MyEclipse打包带源码的jar包

    原博文出自于: http://www.cnblogs.com/xdp-gacl/p/4136303.html 感谢! 平时开发中,我们喜欢将一些类打包成jar包,然后在别的项目中继续使用,不过由于看不 ...

  3. Linux下的sort排序命令详解(二)

    有时候学习脚本,你会发现sort命令后面跟了一堆类似-k1,2,或者-k1.2 -k3.4的东东,有些匪夷所思.今天,我们就来搞定它—-k选项! 1 准备素材 [root@FDMdevBI opt]# ...

  4. UVA 624 (0 1背包 + 打印路径)

    #include<stdio.h> #include<string.h> #include<stdlib.h> #include<ctype.h> #i ...

  5. svn如何回滚到之前版本

    第一种情况:改动没有被提交(commit). 这种情况下,使用svn revert就能取消之前的修改. svn revert用法如下: # svn revert [-R] something 其中so ...

  6. HDU 1241 Oil Deposits DFS(深度优先搜索) 和 BFS(广度优先搜索)

    Oil Deposits Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total ...

  7. 对Spring.Net的AOP一些思考及应用

    前言      这几天在配置Spring.NET,配到AOP的时候发现自己现在还是没有理解到Spring AOP的实现,只是认识到了一个思想,以前配的时候,看的是给好的例子用,真正用的时候还是要想一下 ...

  8. [网络]远程访问局域网svn服务器[转]

    转至:http://8474832.blog.51cto.com/8464832/1555449 打开路由器访问界面 选择转发规则->端口映射-新建 在弹出的界面中填写相应的端口号了内网ip 填 ...

  9. EASYUI DATAGRID 多列复选框CheckBox

    主要实现: 用的 easyui 1.3.2 实现多个复选框列,各列互不影响.能够实现全选.主要部门用红色标记了的. easyui datagrid 初始化: <script> functi ...

  10. SQLServer: 无法修改表

    工具—选项—Designers—表设计器和数据库设计器—阻止保存要求重新创建表的更改前的勾去掉.